mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 04:06:03 +00:00
bump(github.com/appc/spec): fc380db5fc13c6dd71a5b0bf2af0d182865d1b1d
This commit is contained in:
parent
bc6fdd925d
commit
011d35a422
17
Godeps/Godeps.json
generated
17
Godeps/Godeps.json
generated
@ -115,18 +115,23 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/appc/spec/schema",
|
||||
"Comment": "v0.8.1-6-gab50d12",
|
||||
"Rev": "ab50d12e88f57788bf84b83fef2be236eb1fcc0b"
|
||||
"Comment": "v0.8.9-17-gfc380db",
|
||||
"Rev": "fc380db5fc13c6dd71a5b0bf2af0d182865d1b1d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/appc/spec/schema/common",
|
||||
"Comment": "v0.8.1-6-gab50d12",
|
||||
"Rev": "ab50d12e88f57788bf84b83fef2be236eb1fcc0b"
|
||||
"Comment": "v0.8.9-17-gfc380db",
|
||||
"Rev": "fc380db5fc13c6dd71a5b0bf2af0d182865d1b1d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/appc/spec/schema/types",
|
||||
"Comment": "v0.8.1-6-gab50d12",
|
||||
"Rev": "ab50d12e88f57788bf84b83fef2be236eb1fcc0b"
|
||||
"Comment": "v0.8.9-17-gfc380db",
|
||||
"Rev": "fc380db5fc13c6dd71a5b0bf2af0d182865d1b1d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/appc/spec/schema/types/resource",
|
||||
"Comment": "v0.8.9-17-gfc380db",
|
||||
"Rev": "fc380db5fc13c6dd71a5b0bf2af0d182865d1b1d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/armon/circbuf",
|
||||
|
23
vendor/github.com/appc/spec/schema/pod.go
generated
vendored
23
vendor/github.com/appc/spec/schema/pod.go
generated
vendored
@ -28,13 +28,15 @@ import (
|
||||
const PodManifestKind = types.ACKind("PodManifest")
|
||||
|
||||
type PodManifest struct {
|
||||
ACVersion types.SemVer `json:"acVersion"`
|
||||
ACKind types.ACKind `json:"acKind"`
|
||||
Apps AppList `json:"apps"`
|
||||
Volumes []types.Volume `json:"volumes"`
|
||||
Isolators []types.Isolator `json:"isolators"`
|
||||
Annotations types.Annotations `json:"annotations"`
|
||||
Ports []types.ExposedPort `json:"ports"`
|
||||
ACVersion types.SemVer `json:"acVersion"`
|
||||
ACKind types.ACKind `json:"acKind"`
|
||||
Apps AppList `json:"apps"`
|
||||
Volumes []types.Volume `json:"volumes"`
|
||||
Isolators []types.Isolator `json:"isolators"`
|
||||
Annotations types.Annotations `json:"annotations"`
|
||||
Ports []types.ExposedPort `json:"ports"`
|
||||
UserAnnotations types.UserAnnotations `json:"userAnnotations,omitempty"`
|
||||
UserLabels types.UserLabels `json:"userLabels,omitempty"`
|
||||
}
|
||||
|
||||
// podManifest is a model to facilitate extra validation during the
|
||||
@ -135,9 +137,12 @@ func (al AppList) Get(name types.ACName) *RuntimeApp {
|
||||
|
||||
// Mount describes the mapping between a volume and the path it is mounted
|
||||
// inside of an app's filesystem.
|
||||
// The AppVolume is optional. If missing, the pod-level Volume of the
|
||||
// same name shall be used.
|
||||
type Mount struct {
|
||||
Volume types.ACName `json:"volume"`
|
||||
Path string `json:"path"`
|
||||
Volume types.ACName `json:"volume"`
|
||||
Path string `json:"path"`
|
||||
AppVolume *types.Volume `json:"appVolume,omitempty"`
|
||||
}
|
||||
|
||||
func (r Mount) assertValid() error {
|
||||
|
25
vendor/github.com/appc/spec/schema/types/app.go
generated
vendored
25
vendor/github.com/appc/spec/schema/types/app.go
generated
vendored
@ -22,16 +22,18 @@ import (
|
||||
)
|
||||
|
||||
type App struct {
|
||||
Exec Exec `json:"exec"`
|
||||
EventHandlers []EventHandler `json:"eventHandlers,omitempty"`
|
||||
User string `json:"user"`
|
||||
Group string `json:"group"`
|
||||
SupplementaryGIDs []int `json:"supplementaryGIDs,omitempty"`
|
||||
WorkingDirectory string `json:"workingDirectory,omitempty"`
|
||||
Environment Environment `json:"environment,omitempty"`
|
||||
MountPoints []MountPoint `json:"mountPoints,omitempty"`
|
||||
Ports []Port `json:"ports,omitempty"`
|
||||
Isolators Isolators `json:"isolators,omitempty"`
|
||||
Exec Exec `json:"exec"`
|
||||
EventHandlers []EventHandler `json:"eventHandlers,omitempty"`
|
||||
User string `json:"user"`
|
||||
Group string `json:"group"`
|
||||
SupplementaryGIDs []int `json:"supplementaryGIDs,omitempty"`
|
||||
WorkingDirectory string `json:"workingDirectory,omitempty"`
|
||||
Environment Environment `json:"environment,omitempty"`
|
||||
MountPoints []MountPoint `json:"mountPoints,omitempty"`
|
||||
Ports []Port `json:"ports,omitempty"`
|
||||
Isolators Isolators `json:"isolators,omitempty"`
|
||||
UserAnnotations UserAnnotations `json:"userAnnotations,omitempty"`
|
||||
UserLabels UserLabels `json:"userLabels,omitempty"`
|
||||
}
|
||||
|
||||
// app is a model to facilitate extra validation during the
|
||||
@ -86,5 +88,8 @@ func (a *App) assertValid() error {
|
||||
if err := a.Environment.assertValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := a.Isolators.assertValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
61
vendor/github.com/appc/spec/schema/types/isolator.go
generated
vendored
61
vendor/github.com/appc/spec/schema/types/isolator.go
generated
vendored
@ -16,10 +16,19 @@ package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
isolatorMap map[ACIdentifier]IsolatorValueConstructor
|
||||
|
||||
// ErrIncompatibleIsolator is returned whenever an Isolators set contains
|
||||
// conflicting IsolatorValue instances
|
||||
ErrIncompatibleIsolator = errors.New("isolators set contains incompatible types")
|
||||
// ErrInvalidIsolator is returned upon validation failures due to improper
|
||||
// or partially constructed Isolator instances (eg. from incomplete direct construction)
|
||||
ErrInvalidIsolator = errors.New("invalid isolator")
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -40,6 +49,33 @@ func AddIsolatorName(n ACIdentifier, ns map[ACIdentifier]struct{}) {
|
||||
// and PodManifest schemas.
|
||||
type Isolators []Isolator
|
||||
|
||||
// assertValid checks that every single isolator is valid and that
|
||||
// the whole set is well built
|
||||
func (isolators Isolators) assertValid() error {
|
||||
typesMap := make(map[ACIdentifier]bool)
|
||||
for _, i := range isolators {
|
||||
v := i.Value()
|
||||
if v == nil {
|
||||
return ErrInvalidIsolator
|
||||
}
|
||||
if err := v.AssertValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := typesMap[i.Name]; ok {
|
||||
if !v.multipleAllowed() {
|
||||
return fmt.Errorf(`isolators set contains too many instances of type %s"`, i.Name)
|
||||
}
|
||||
}
|
||||
for _, c := range v.Conflicts() {
|
||||
if _, found := typesMap[c]; found {
|
||||
return ErrIncompatibleIsolator
|
||||
}
|
||||
}
|
||||
typesMap[i.Name] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetByName returns the last isolator in the list by the given name.
|
||||
func (is *Isolators) GetByName(name ACIdentifier) *Isolator {
|
||||
var i Isolator
|
||||
@ -52,6 +88,22 @@ func (is *Isolators) GetByName(name ACIdentifier) *Isolator {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReplaceIsolatorsByName overrides matching isolator types with a new
|
||||
// isolator, deleting them all and appending the new one instead
|
||||
func (is *Isolators) ReplaceIsolatorsByName(newIs Isolator, oldNames []ACIdentifier) {
|
||||
var i Isolator
|
||||
for j := len(*is) - 1; j >= 0; j-- {
|
||||
i = []Isolator(*is)[j]
|
||||
for _, name := range oldNames {
|
||||
if i.Name == name {
|
||||
*is = append((*is)[:j], (*is)[j+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
*is = append((*is)[:], newIs)
|
||||
return
|
||||
}
|
||||
|
||||
// Unrecognized returns a set of isolators that are not recognized.
|
||||
// An isolator is not recognized if it has not had an associated
|
||||
// constructor registered with AddIsolatorValueConstructor.
|
||||
@ -69,8 +121,17 @@ func (is *Isolators) Unrecognized() Isolators {
|
||||
// serialized as any arbitrary JSON blob. Specific Isolator types should
|
||||
// implement this interface to facilitate unmarshalling and validation.
|
||||
type IsolatorValue interface {
|
||||
// UnmarshalJSON unserialize a JSON-encoded isolator
|
||||
UnmarshalJSON(b []byte) error
|
||||
// AssertValid returns a non-nil error value if an IsolatorValue is not valid
|
||||
// according to appc spec
|
||||
AssertValid() error
|
||||
// Conflicts returns a list of conflicting isolators types, which cannot co-exist
|
||||
// together with this IsolatorValue
|
||||
Conflicts() []ACIdentifier
|
||||
// multipleAllowed specifies whether multiple isolator instances are allowed
|
||||
// for this isolator type
|
||||
multipleAllowed() bool
|
||||
}
|
||||
|
||||
// Isolator is a model for unmarshalling isolator types from their JSON-encoded
|
||||
|
398
vendor/github.com/appc/spec/schema/types/isolator_linux_specific.go
generated
vendored
398
vendor/github.com/appc/spec/schema/types/isolator_linux_specific.go
generated
vendored
@ -17,11 +17,20 @@ package types
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const (
|
||||
LinuxCapabilitiesRetainSetName = "os/linux/capabilities-retain-set"
|
||||
LinuxCapabilitiesRevokeSetName = "os/linux/capabilities-remove-set"
|
||||
LinuxNoNewPrivilegesName = "os/linux/no-new-privileges"
|
||||
LinuxSeccompRemoveSetName = "os/linux/seccomp-remove-set"
|
||||
LinuxSeccompRetainSetName = "os/linux/seccomp-retain-set"
|
||||
LinuxOOMScoreAdjName = "os/linux/oom-score-adj"
|
||||
LinuxCPUSharesName = "os/linux/cpu-shares"
|
||||
LinuxSELinuxContextName = "os/linux/selinux-context"
|
||||
)
|
||||
|
||||
var LinuxIsolatorNames = make(map[ACIdentifier]struct{})
|
||||
@ -30,12 +39,49 @@ func init() {
|
||||
for name, con := range map[ACIdentifier]IsolatorValueConstructor{
|
||||
LinuxCapabilitiesRevokeSetName: func() IsolatorValue { return &LinuxCapabilitiesRevokeSet{} },
|
||||
LinuxCapabilitiesRetainSetName: func() IsolatorValue { return &LinuxCapabilitiesRetainSet{} },
|
||||
LinuxNoNewPrivilegesName: func() IsolatorValue { v := LinuxNoNewPrivileges(false); return &v },
|
||||
LinuxOOMScoreAdjName: func() IsolatorValue { v := LinuxOOMScoreAdj(0); return &v },
|
||||
LinuxCPUSharesName: func() IsolatorValue { v := LinuxCPUShares(1024); return &v },
|
||||
LinuxSeccompRemoveSetName: func() IsolatorValue { return &LinuxSeccompRemoveSet{} },
|
||||
LinuxSeccompRetainSetName: func() IsolatorValue { return &LinuxSeccompRetainSet{} },
|
||||
LinuxSELinuxContextName: func() IsolatorValue { return &LinuxSELinuxContext{} },
|
||||
} {
|
||||
AddIsolatorName(name, LinuxIsolatorNames)
|
||||
AddIsolatorValueConstructor(name, con)
|
||||
}
|
||||
}
|
||||
|
||||
type LinuxNoNewPrivileges bool
|
||||
|
||||
func (l LinuxNoNewPrivileges) AssertValid() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(lucab): both need to be clarified in spec,
|
||||
// see https://github.com/appc/spec/issues/625
|
||||
func (l LinuxNoNewPrivileges) multipleAllowed() bool {
|
||||
return true
|
||||
}
|
||||
func (l LinuxNoNewPrivileges) Conflicts() []ACIdentifier {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LinuxNoNewPrivileges) UnmarshalJSON(b []byte) error {
|
||||
var v bool
|
||||
err := json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*l = LinuxNoNewPrivileges(v)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type AsIsolator interface {
|
||||
AsIsolator() (*Isolator, error)
|
||||
}
|
||||
|
||||
type LinuxCapabilitiesSet interface {
|
||||
Set() []LinuxCapability
|
||||
AssertValid() error
|
||||
@ -58,6 +104,15 @@ func (l linuxCapabilitiesSetBase) AssertValid() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(lucab): both need to be clarified in spec,
|
||||
// see https://github.com/appc/spec/issues/625
|
||||
func (l linuxCapabilitiesSetBase) multipleAllowed() bool {
|
||||
return true
|
||||
}
|
||||
func (l linuxCapabilitiesSetBase) Conflicts() []ACIdentifier {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *linuxCapabilitiesSetBase) UnmarshalJSON(b []byte) error {
|
||||
var v linuxCapabilitiesSetValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
@ -95,17 +150,17 @@ func NewLinuxCapabilitiesRetainSet(caps ...string) (*LinuxCapabilitiesRetainSet,
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
func (l LinuxCapabilitiesRetainSet) AsIsolator() Isolator {
|
||||
func (l LinuxCapabilitiesRetainSet) AsIsolator() (*Isolator, error) {
|
||||
b, err := json.Marshal(l.linuxCapabilitiesSetBase.val)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
rm := json.RawMessage(b)
|
||||
return Isolator{
|
||||
return &Isolator{
|
||||
Name: LinuxCapabilitiesRetainSetName,
|
||||
ValueRaw: &rm,
|
||||
value: &l,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
type LinuxCapabilitiesRevokeSet struct {
|
||||
@ -129,15 +184,346 @@ func NewLinuxCapabilitiesRevokeSet(caps ...string) (*LinuxCapabilitiesRevokeSet,
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
func (l LinuxCapabilitiesRevokeSet) AsIsolator() Isolator {
|
||||
func (l LinuxCapabilitiesRevokeSet) AsIsolator() (*Isolator, error) {
|
||||
b, err := json.Marshal(l.linuxCapabilitiesSetBase.val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rm := json.RawMessage(b)
|
||||
return &Isolator{
|
||||
Name: LinuxCapabilitiesRevokeSetName,
|
||||
ValueRaw: &rm,
|
||||
value: &l,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type LinuxSeccompSet interface {
|
||||
Set() []LinuxSeccompEntry
|
||||
Errno() LinuxSeccompErrno
|
||||
AssertValid() error
|
||||
}
|
||||
|
||||
type LinuxSeccompEntry string
|
||||
type LinuxSeccompErrno string
|
||||
|
||||
type linuxSeccompValue struct {
|
||||
Set []LinuxSeccompEntry `json:"set"`
|
||||
Errno LinuxSeccompErrno `json:"errno"`
|
||||
}
|
||||
|
||||
type linuxSeccompBase struct {
|
||||
val linuxSeccompValue
|
||||
}
|
||||
|
||||
func (l linuxSeccompBase) multipleAllowed() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (l linuxSeccompBase) AssertValid() error {
|
||||
if len(l.val.Set) == 0 {
|
||||
return errors.New("set must be non-empty")
|
||||
}
|
||||
if l.val.Errno == "" {
|
||||
return nil
|
||||
}
|
||||
for _, c := range l.val.Errno {
|
||||
if !unicode.IsUpper(c) {
|
||||
return errors.New("errno must be an upper case string")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *linuxSeccompBase) UnmarshalJSON(b []byte) error {
|
||||
var v linuxSeccompValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.val = v
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l linuxSeccompBase) Set() []LinuxSeccompEntry {
|
||||
return l.val.Set
|
||||
}
|
||||
|
||||
func (l linuxSeccompBase) Errno() LinuxSeccompErrno {
|
||||
return l.val.Errno
|
||||
}
|
||||
|
||||
type LinuxSeccompRetainSet struct {
|
||||
linuxSeccompBase
|
||||
}
|
||||
|
||||
func (l LinuxSeccompRetainSet) Conflicts() []ACIdentifier {
|
||||
return []ACIdentifier{LinuxSeccompRemoveSetName}
|
||||
}
|
||||
|
||||
func NewLinuxSeccompRetainSet(errno string, syscall ...string) (*LinuxSeccompRetainSet, error) {
|
||||
l := LinuxSeccompRetainSet{
|
||||
linuxSeccompBase{
|
||||
linuxSeccompValue{
|
||||
make([]LinuxSeccompEntry, len(syscall)),
|
||||
LinuxSeccompErrno(errno),
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, c := range syscall {
|
||||
l.linuxSeccompBase.val.Set[i] = LinuxSeccompEntry(c)
|
||||
}
|
||||
if err := l.AssertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
func (l LinuxSeccompRetainSet) AsIsolator() (*Isolator, error) {
|
||||
b, err := json.Marshal(l.linuxSeccompBase.val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rm := json.RawMessage(b)
|
||||
return &Isolator{
|
||||
Name: LinuxSeccompRetainSetName,
|
||||
ValueRaw: &rm,
|
||||
value: &l,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type LinuxSeccompRemoveSet struct {
|
||||
linuxSeccompBase
|
||||
}
|
||||
|
||||
func (l LinuxSeccompRemoveSet) Conflicts() []ACIdentifier {
|
||||
return []ACIdentifier{LinuxSeccompRetainSetName}
|
||||
}
|
||||
|
||||
func NewLinuxSeccompRemoveSet(errno string, syscall ...string) (*LinuxSeccompRemoveSet, error) {
|
||||
l := LinuxSeccompRemoveSet{
|
||||
linuxSeccompBase{
|
||||
linuxSeccompValue{
|
||||
make([]LinuxSeccompEntry, len(syscall)),
|
||||
LinuxSeccompErrno(errno),
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, c := range syscall {
|
||||
l.linuxSeccompBase.val.Set[i] = LinuxSeccompEntry(c)
|
||||
}
|
||||
if err := l.AssertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
func (l LinuxSeccompRemoveSet) AsIsolator() (*Isolator, error) {
|
||||
b, err := json.Marshal(l.linuxSeccompBase.val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rm := json.RawMessage(b)
|
||||
return &Isolator{
|
||||
Name: LinuxSeccompRemoveSetName,
|
||||
ValueRaw: &rm,
|
||||
value: &l,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// LinuxCPUShares assigns the CPU time share weight to the processes executed.
|
||||
// See https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight,
|
||||
// https://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt
|
||||
type LinuxCPUShares int
|
||||
|
||||
func NewLinuxCPUShares(val int) (*LinuxCPUShares, error) {
|
||||
l := LinuxCPUShares(val)
|
||||
if err := l.AssertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
func (l LinuxCPUShares) AssertValid() error {
|
||||
if l < 2 || l > 262144 {
|
||||
return fmt.Errorf("%s must be between 2 and 262144, got %d", LinuxCPUSharesName, l)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l LinuxCPUShares) multipleAllowed() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (l LinuxCPUShares) Conflicts() []ACIdentifier {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LinuxCPUShares) UnmarshalJSON(b []byte) error {
|
||||
var v int
|
||||
err := json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*l = LinuxCPUShares(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l LinuxCPUShares) AsIsolator() Isolator {
|
||||
b, err := json.Marshal(l)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
rm := json.RawMessage(b)
|
||||
return Isolator{
|
||||
Name: LinuxCapabilitiesRevokeSetName,
|
||||
Name: LinuxCPUSharesName,
|
||||
ValueRaw: &rm,
|
||||
value: &l,
|
||||
}
|
||||
}
|
||||
|
||||
// LinuxOOMScoreAdj is equivalent to /proc/[pid]/oom_score_adj
|
||||
type LinuxOOMScoreAdj int // -1000 to 1000
|
||||
|
||||
func NewLinuxOOMScoreAdj(val int) (*LinuxOOMScoreAdj, error) {
|
||||
l := LinuxOOMScoreAdj(val)
|
||||
if err := l.AssertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
func (l LinuxOOMScoreAdj) AssertValid() error {
|
||||
if l < -1000 || l > 1000 {
|
||||
return fmt.Errorf("%s must be between -1000 and 1000, got %d", LinuxOOMScoreAdjName, l)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l LinuxOOMScoreAdj) multipleAllowed() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (l LinuxOOMScoreAdj) Conflicts() []ACIdentifier {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LinuxOOMScoreAdj) UnmarshalJSON(b []byte) error {
|
||||
var v int
|
||||
err := json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*l = LinuxOOMScoreAdj(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l LinuxOOMScoreAdj) AsIsolator() Isolator {
|
||||
b, err := json.Marshal(l)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
rm := json.RawMessage(b)
|
||||
return Isolator{
|
||||
Name: LinuxOOMScoreAdjName,
|
||||
ValueRaw: &rm,
|
||||
value: &l,
|
||||
}
|
||||
}
|
||||
|
||||
type LinuxSELinuxUser string
|
||||
type LinuxSELinuxRole string
|
||||
type LinuxSELinuxType string
|
||||
type LinuxSELinuxLevel string
|
||||
|
||||
type linuxSELinuxValue struct {
|
||||
User LinuxSELinuxUser `json:"user"`
|
||||
Role LinuxSELinuxRole `json:"role"`
|
||||
Type LinuxSELinuxType `json:"type"`
|
||||
Level LinuxSELinuxLevel `json:"level"`
|
||||
}
|
||||
|
||||
type LinuxSELinuxContext struct {
|
||||
val linuxSELinuxValue
|
||||
}
|
||||
|
||||
func (l LinuxSELinuxContext) AssertValid() error {
|
||||
if l.val.User == "" || strings.Contains(string(l.val.User), ":") {
|
||||
return fmt.Errorf("invalid user value %q", l.val.User)
|
||||
}
|
||||
if l.val.Role == "" || strings.Contains(string(l.val.Role), ":") {
|
||||
return fmt.Errorf("invalid role value %q", l.val.Role)
|
||||
}
|
||||
if l.val.Type == "" || strings.Contains(string(l.val.Type), ":") {
|
||||
return fmt.Errorf("invalid type value %q", l.val.Type)
|
||||
}
|
||||
if l.val.Level == "" {
|
||||
return fmt.Errorf("invalid level value %q", l.val.Level)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *LinuxSELinuxContext) UnmarshalJSON(b []byte) error {
|
||||
var v linuxSELinuxValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.val = v
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l LinuxSELinuxContext) User() LinuxSELinuxUser {
|
||||
return l.val.User
|
||||
}
|
||||
|
||||
func (l LinuxSELinuxContext) Role() LinuxSELinuxRole {
|
||||
return l.val.Role
|
||||
}
|
||||
|
||||
func (l LinuxSELinuxContext) Type() LinuxSELinuxType {
|
||||
return l.val.Type
|
||||
}
|
||||
|
||||
func (l LinuxSELinuxContext) Level() LinuxSELinuxLevel {
|
||||
return l.val.Level
|
||||
}
|
||||
|
||||
func (l LinuxSELinuxContext) multipleAllowed() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (l LinuxSELinuxContext) Conflicts() []ACIdentifier {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewLinuxSELinuxContext(selinuxUser, selinuxRole, selinuxType, selinuxLevel string) (*LinuxSELinuxContext, error) {
|
||||
l := LinuxSELinuxContext{
|
||||
linuxSELinuxValue{
|
||||
LinuxSELinuxUser(selinuxUser),
|
||||
LinuxSELinuxRole(selinuxRole),
|
||||
LinuxSELinuxType(selinuxType),
|
||||
LinuxSELinuxLevel(selinuxLevel),
|
||||
},
|
||||
}
|
||||
if err := l.AssertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
func (l LinuxSELinuxContext) AsIsolator() (*Isolator, error) {
|
||||
b, err := json.Marshal(l.val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rm := json.RawMessage(b)
|
||||
return &Isolator{
|
||||
Name: LinuxSELinuxContextName,
|
||||
ValueRaw: &rm,
|
||||
value: &l,
|
||||
}, nil
|
||||
}
|
||||
|
11
vendor/github.com/appc/spec/schema/types/isolator_resources.go
generated
vendored
11
vendor/github.com/appc/spec/schema/types/isolator_resources.go
generated
vendored
@ -19,7 +19,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"github.com/appc/spec/schema/types/resource"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -85,6 +85,15 @@ func (r ResourceBase) AssertValid() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(lucab): both need to be clarified in spec,
|
||||
// see https://github.com/appc/spec/issues/625
|
||||
func (l ResourceBase) multipleAllowed() bool {
|
||||
return true
|
||||
}
|
||||
func (l ResourceBase) Conflicts() []ACIdentifier {
|
||||
return nil
|
||||
}
|
||||
|
||||
type ResourceBlockBandwidth struct {
|
||||
ResourceBase
|
||||
}
|
||||
|
83
vendor/github.com/appc/spec/schema/types/isolator_unix.go
generated
vendored
Normal file
83
vendor/github.com/appc/spec/schema/types/isolator_unix.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright 2016 The appc Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
var (
|
||||
UnixIsolatorNames = make(map[ACIdentifier]struct{})
|
||||
)
|
||||
|
||||
const (
|
||||
//TODO(lucab): add "ulimit" isolators
|
||||
UnixSysctlName = "os/unix/sysctl"
|
||||
)
|
||||
|
||||
func init() {
|
||||
for name, con := range map[ACIdentifier]IsolatorValueConstructor{
|
||||
UnixSysctlName: func() IsolatorValue { return &UnixSysctl{} },
|
||||
} {
|
||||
AddIsolatorName(name, UnixIsolatorNames)
|
||||
AddIsolatorValueConstructor(name, con)
|
||||
}
|
||||
}
|
||||
|
||||
type UnixSysctl map[string]string
|
||||
|
||||
func (s *UnixSysctl) UnmarshalJSON(b []byte) error {
|
||||
var v map[string]string
|
||||
err := json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*s = UnixSysctl(v)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s UnixSysctl) AssertValid() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s UnixSysctl) multipleAllowed() bool {
|
||||
return false
|
||||
}
|
||||
func (s UnixSysctl) Conflicts() []ACIdentifier {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s UnixSysctl) AsIsolator() Isolator {
|
||||
isol := isolatorMap[UnixSysctlName]()
|
||||
|
||||
b, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
valRaw := json.RawMessage(b)
|
||||
return Isolator{
|
||||
Name: UnixSysctlName,
|
||||
ValueRaw: &valRaw,
|
||||
value: isol,
|
||||
}
|
||||
}
|
||||
|
||||
func NewUnixSysctlIsolator(cfg map[string]string) (*UnixSysctl, error) {
|
||||
s := UnixSysctl(cfg)
|
||||
if err := s.AssertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s, nil
|
||||
}
|
74
vendor/github.com/appc/spec/schema/types/labels.go
generated
vendored
74
vendor/github.com/appc/spec/schema/types/labels.go
generated
vendored
@ -21,7 +21,7 @@ import (
|
||||
)
|
||||
|
||||
var ValidOSArch = map[string][]string{
|
||||
"linux": {"amd64", "i386", "aarch64", "aarch64_be", "armv6l", "armv7l", "armv7b"},
|
||||
"linux": {"amd64", "i386", "aarch64", "aarch64_be", "armv6l", "armv7l", "armv7b", "ppc64", "ppc64le", "s390x"},
|
||||
"freebsd": {"amd64", "i386", "arm"},
|
||||
"darwin": {"x86_64", "i386"},
|
||||
}
|
||||
@ -35,6 +35,17 @@ type Label struct {
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// {appc,go}ArchTuple are internal helper types used to translate arch tuple between go and appc
|
||||
type appcArchTuple struct {
|
||||
appcOs string
|
||||
appcArch string
|
||||
}
|
||||
type goArchTuple struct {
|
||||
goOs string
|
||||
goArch string
|
||||
goArchFlavor string
|
||||
}
|
||||
|
||||
// IsValidOsArch checks if a OS-architecture combination is valid given a map
|
||||
// of valid OS-architectures
|
||||
func IsValidOSArch(labels map[ACIdentifier]string, validOSArch map[string][]string) error {
|
||||
@ -132,3 +143,64 @@ func LabelsFromMap(labelsMap map[ACIdentifier]string) (Labels, error) {
|
||||
}
|
||||
return labels, nil
|
||||
}
|
||||
|
||||
// ToAppcOSArch translates a Golang arch tuple (OS, architecture, flavor) into
|
||||
// an appc arch tuple (OS, architecture)
|
||||
func ToAppcOSArch(goOs string, goArch string, goArchFlavor string) (appcOs string, appcArch string, e error) {
|
||||
tabularAppcToGo := map[goArchTuple]appcArchTuple{
|
||||
{"linux", "amd64", ""}: {"linux", "amd64"},
|
||||
{"linux", "386", ""}: {"linux", "i386"},
|
||||
{"linux", "arm64", ""}: {"linux", "aarch64"},
|
||||
{"linux", "arm", ""}: {"linux", "armv6l"},
|
||||
{"linux", "arm", "6"}: {"linux", "armv6l"},
|
||||
{"linux", "arm", "7"}: {"linux", "armv7l"},
|
||||
{"linux", "ppc64", ""}: {"linux", "ppc64"},
|
||||
{"linux", "ppc64le", ""}: {"linux", "ppc64le"},
|
||||
{"linux", "s390x", ""}: {"linux", "s390x"},
|
||||
|
||||
{"freebsd", "amd64", ""}: {"freebsd", "amd64"},
|
||||
{"freebsd", "386", ""}: {"freebsd", "i386"},
|
||||
{"freebsd", "arm", ""}: {"freebsd", "arm"},
|
||||
{"freebsd", "arm", "5"}: {"freebsd", "arm"},
|
||||
{"freebsd", "arm", "6"}: {"freebsd", "arm"},
|
||||
{"freebsd", "arm", "7"}: {"freebsd", "arm"},
|
||||
|
||||
{"darwin", "amd64", ""}: {"darwin", "x86_64"},
|
||||
{"darwin", "386", ""}: {"darwin", "i386"},
|
||||
}
|
||||
archTuple, ok := tabularAppcToGo[goArchTuple{goOs, goArch, goArchFlavor}]
|
||||
if !ok {
|
||||
return "", "", fmt.Errorf("unknown arch tuple: %q - %q - %q", goOs, goArch, goArchFlavor)
|
||||
}
|
||||
return archTuple.appcOs, archTuple.appcArch, nil
|
||||
}
|
||||
|
||||
// ToGoOSArch translates an appc arch tuple (OS, architecture) into
|
||||
// a Golang arch tuple (OS, architecture, flavor)
|
||||
func ToGoOSArch(appcOs string, appcArch string) (goOs string, goArch string, goArchFlavor string, e error) {
|
||||
tabularGoToAppc := map[appcArchTuple]goArchTuple{
|
||||
// {"linux", "aarch64_be"}: nil,
|
||||
// {"linux", "armv7b"}: nil,
|
||||
{"linux", "aarch64"}: {"linux", "arm64", ""},
|
||||
{"linux", "amd64"}: {"linux", "amd64", ""},
|
||||
{"linux", "armv6l"}: {"linux", "arm", "6"},
|
||||
{"linux", "armv7l"}: {"linux", "arm", "7"},
|
||||
{"linux", "i386"}: {"linux", "386", ""},
|
||||
{"linux", "ppc64"}: {"linux", "ppc64", ""},
|
||||
{"linux", "ppc64le"}: {"linux", "ppc64le", ""},
|
||||
{"linux", "s390x"}: {"linux", "s390x", ""},
|
||||
|
||||
{"freebsd", "amd64"}: {"freebsd", "amd64", ""},
|
||||
{"freebsd", "arm"}: {"freebsd", "arm", "6"},
|
||||
{"freebsd", "386"}: {"freebsd", "i386", ""},
|
||||
|
||||
{"darwin", "amd64"}: {"darwin", "x86_64", ""},
|
||||
{"darwin", "386"}: {"darwin", "i386", ""},
|
||||
}
|
||||
|
||||
archTuple, ok := tabularGoToAppc[appcArchTuple{appcOs, appcArch}]
|
||||
if !ok {
|
||||
return "", "", "", fmt.Errorf("unknown arch tuple: %q - %q", appcOs, appcArch)
|
||||
}
|
||||
return archTuple.goOs, archTuple.goArch, archTuple.goArchFlavor, nil
|
||||
}
|
||||
|
1
vendor/github.com/appc/spec/schema/types/mountpoint.go
generated
vendored
1
vendor/github.com/appc/spec/schema/types/mountpoint.go
generated
vendored
@ -23,6 +23,7 @@ import (
|
||||
"github.com/appc/spec/schema/common"
|
||||
)
|
||||
|
||||
// MountPoint is the application-side manifestation of a Volume.
|
||||
type MountPoint struct {
|
||||
Name ACName `json:"name"`
|
||||
Path string `json:"path"`
|
||||
|
8
vendor/github.com/appc/spec/schema/types/port.go
generated
vendored
8
vendor/github.com/appc/spec/schema/types/port.go
generated
vendored
@ -18,12 +18,15 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/appc/spec/schema/common"
|
||||
)
|
||||
|
||||
// Port represents a port as offered by an application *inside*
|
||||
// the pod.
|
||||
type Port struct {
|
||||
Name ACName `json:"name"`
|
||||
Protocol string `json:"protocol"`
|
||||
@ -32,9 +35,14 @@ type Port struct {
|
||||
SocketActivated bool `json:"socketActivated"`
|
||||
}
|
||||
|
||||
// ExposedPort represents a port listening on the host side.
|
||||
// The PodPort is optional -- if missing, then try and find the pod-side
|
||||
// information by matching names
|
||||
type ExposedPort struct {
|
||||
Name ACName `json:"name"`
|
||||
HostPort uint `json:"hostPort"`
|
||||
HostIP net.IP `json:"hostIP,omitempty"` // optional
|
||||
PodPort *Port `json:"podPort,omitempty"` // optional. If missing, try and find a corresponding App's port
|
||||
}
|
||||
|
||||
type port Port
|
||||
|
4
vendor/github.com/appc/spec/schema/types/resource/README.md
generated
vendored
Normal file
4
vendor/github.com/appc/spec/schema/types/resource/README.md
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
This package was copied in from the Kubernetes repo to avoid a cyclic
|
||||
dependency. These files were taken from master from
|
||||
github.com/kubernetes/kubernetes at commit hash
|
||||
b0deb2eb8f4037421077f77cb163dbb4c0a2a9f5.
|
298
vendor/github.com/appc/spec/schema/types/resource/amount.go
generated
vendored
Normal file
298
vendor/github.com/appc/spec/schema/types/resource/amount.go
generated
vendored
Normal file
@ -0,0 +1,298 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resource
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
inf "gopkg.in/inf.v0"
|
||||
)
|
||||
|
||||
// Scale is used for getting and setting the base-10 scaled value.
|
||||
// Base-2 scales are omitted for mathematical simplicity.
|
||||
// See Quantity.ScaledValue for more details.
|
||||
type Scale int32
|
||||
|
||||
// infScale adapts a Scale value to an inf.Scale value.
|
||||
func (s Scale) infScale() inf.Scale {
|
||||
return inf.Scale(-s) // inf.Scale is upside-down
|
||||
}
|
||||
|
||||
const (
|
||||
Nano Scale = -9
|
||||
Micro Scale = -6
|
||||
Milli Scale = -3
|
||||
Kilo Scale = 3
|
||||
Mega Scale = 6
|
||||
Giga Scale = 9
|
||||
Tera Scale = 12
|
||||
Peta Scale = 15
|
||||
Exa Scale = 18
|
||||
)
|
||||
|
||||
var (
|
||||
Zero = int64Amount{}
|
||||
|
||||
// Used by quantity strings - treat as read only
|
||||
zeroBytes = []byte("0")
|
||||
)
|
||||
|
||||
// int64Amount represents a fixed precision numerator and arbitrary scale exponent. It is faster
|
||||
// than operations on inf.Dec for values that can be represented as int64.
|
||||
type int64Amount struct {
|
||||
value int64
|
||||
scale Scale
|
||||
}
|
||||
|
||||
// Sign returns 0 if the value is zero, -1 if it is less than 0, or 1 if it is greater than 0.
|
||||
func (a int64Amount) Sign() int {
|
||||
switch {
|
||||
case a.value == 0:
|
||||
return 0
|
||||
case a.value > 0:
|
||||
return 1
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// AsInt64 returns the current amount as an int64 at scale 0, or false if the value cannot be
|
||||
// represented in an int64 OR would result in a loss of precision. This method is intended as
|
||||
// an optimization to avoid calling AsDec.
|
||||
func (a int64Amount) AsInt64() (int64, bool) {
|
||||
if a.scale == 0 {
|
||||
return a.value, true
|
||||
}
|
||||
if a.scale < 0 {
|
||||
// TODO: attempt to reduce factors, although it is assumed that factors are reduced prior
|
||||
// to the int64Amount being created.
|
||||
return 0, false
|
||||
}
|
||||
return positiveScaleInt64(a.value, a.scale)
|
||||
}
|
||||
|
||||
// AsScaledInt64 returns an int64 representing the value of this amount at the specified scale,
|
||||
// rounding up, or false if that would result in overflow. (1e20).AsScaledInt64(1) would result
|
||||
// in overflow because 1e19 is not representable as an int64. Note that setting a scale larger
|
||||
// than the current value may result in loss of precision - i.e. (1e-6).AsScaledInt64(0) would
|
||||
// return 1, because 0.000001 is rounded up to 1.
|
||||
func (a int64Amount) AsScaledInt64(scale Scale) (result int64, ok bool) {
|
||||
if a.scale < scale {
|
||||
result, _ = negativeScaleInt64(a.value, scale-a.scale)
|
||||
return result, true
|
||||
}
|
||||
return positiveScaleInt64(a.value, a.scale-scale)
|
||||
}
|
||||
|
||||
// AsDec returns an inf.Dec representation of this value.
|
||||
func (a int64Amount) AsDec() *inf.Dec {
|
||||
var base inf.Dec
|
||||
base.SetUnscaled(a.value)
|
||||
base.SetScale(inf.Scale(-a.scale))
|
||||
return &base
|
||||
}
|
||||
|
||||
// Cmp returns 0 if a and b are equal, 1 if a is greater than b, or -1 if a is less than b.
|
||||
func (a int64Amount) Cmp(b int64Amount) int {
|
||||
switch {
|
||||
case a.scale == b.scale:
|
||||
// compare only the unscaled portion
|
||||
case a.scale > b.scale:
|
||||
result, remainder, exact := divideByScaleInt64(b.value, a.scale-b.scale)
|
||||
if !exact {
|
||||
return a.AsDec().Cmp(b.AsDec())
|
||||
}
|
||||
if result == a.value {
|
||||
switch {
|
||||
case remainder == 0:
|
||||
return 0
|
||||
case remainder > 0:
|
||||
return -1
|
||||
default:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
b.value = result
|
||||
default:
|
||||
result, remainder, exact := divideByScaleInt64(a.value, b.scale-a.scale)
|
||||
if !exact {
|
||||
return a.AsDec().Cmp(b.AsDec())
|
||||
}
|
||||
if result == b.value {
|
||||
switch {
|
||||
case remainder == 0:
|
||||
return 0
|
||||
case remainder > 0:
|
||||
return 1
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
a.value = result
|
||||
}
|
||||
|
||||
switch {
|
||||
case a.value == b.value:
|
||||
return 0
|
||||
case a.value < b.value:
|
||||
return -1
|
||||
default:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds two int64Amounts together, matching scales. It will return false and not mutate
|
||||
// a if overflow or underflow would result.
|
||||
func (a *int64Amount) Add(b int64Amount) bool {
|
||||
switch {
|
||||
case b.value == 0:
|
||||
return true
|
||||
case a.value == 0:
|
||||
a.value = b.value
|
||||
a.scale = b.scale
|
||||
return true
|
||||
case a.scale == b.scale:
|
||||
c, ok := int64Add(a.value, b.value)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
a.value = c
|
||||
case a.scale > b.scale:
|
||||
c, ok := positiveScaleInt64(a.value, a.scale-b.scale)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
c, ok = int64Add(c, b.value)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
a.scale = b.scale
|
||||
a.value = c
|
||||
default:
|
||||
c, ok := positiveScaleInt64(b.value, b.scale-a.scale)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
c, ok = int64Add(a.value, c)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
a.value = c
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Sub removes the value of b from the current amount, or returns false if underflow would result.
|
||||
func (a *int64Amount) Sub(b int64Amount) bool {
|
||||
return a.Add(int64Amount{value: -b.value, scale: b.scale})
|
||||
}
|
||||
|
||||
// AsScale adjusts this amount to set a minimum scale, rounding up, and returns true iff no precision
|
||||
// was lost. (1.1e5).AsScale(5) would return 1.1e5, but (1.1e5).AsScale(6) would return 1e6.
|
||||
func (a int64Amount) AsScale(scale Scale) (int64Amount, bool) {
|
||||
if a.scale >= scale {
|
||||
return a, true
|
||||
}
|
||||
result, exact := negativeScaleInt64(a.value, scale-a.scale)
|
||||
return int64Amount{value: result, scale: scale}, exact
|
||||
}
|
||||
|
||||
// AsCanonicalBytes accepts a buffer to write the base-10 string value of this field to, and returns
|
||||
// either that buffer or a larger buffer and the current exponent of the value. The value is adjusted
|
||||
// until the exponent is a multiple of 3 - i.e. 1.1e5 would return "110", 3.
|
||||
func (a int64Amount) AsCanonicalBytes(out []byte) (result []byte, exponent int32) {
|
||||
mantissa := a.value
|
||||
exponent = int32(a.scale)
|
||||
|
||||
amount, times := removeInt64Factors(mantissa, 10)
|
||||
exponent += int32(times)
|
||||
|
||||
// make sure exponent is a multiple of 3
|
||||
var ok bool
|
||||
switch exponent % 3 {
|
||||
case 1, -2:
|
||||
amount, ok = int64MultiplyScale10(amount)
|
||||
if !ok {
|
||||
return infDecAmount{a.AsDec()}.AsCanonicalBytes(out)
|
||||
}
|
||||
exponent = exponent - 1
|
||||
case 2, -1:
|
||||
amount, ok = int64MultiplyScale100(amount)
|
||||
if !ok {
|
||||
return infDecAmount{a.AsDec()}.AsCanonicalBytes(out)
|
||||
}
|
||||
exponent = exponent - 2
|
||||
}
|
||||
return strconv.AppendInt(out, amount, 10), exponent
|
||||
}
|
||||
|
||||
// AsCanonicalBase1024Bytes accepts a buffer to write the base-1024 string value of this field to, and returns
|
||||
// either that buffer or a larger buffer and the current exponent of the value. 2048 is 2 * 1024 ^ 1 and would
|
||||
// return []byte("2048"), 1.
|
||||
func (a int64Amount) AsCanonicalBase1024Bytes(out []byte) (result []byte, exponent int32) {
|
||||
value, ok := a.AsScaledInt64(0)
|
||||
if !ok {
|
||||
return infDecAmount{a.AsDec()}.AsCanonicalBase1024Bytes(out)
|
||||
}
|
||||
amount, exponent := removeInt64Factors(value, 1024)
|
||||
return strconv.AppendInt(out, amount, 10), exponent
|
||||
}
|
||||
|
||||
// infDecAmount implements common operations over an inf.Dec that are specific to the quantity
|
||||
// representation.
|
||||
type infDecAmount struct {
|
||||
*inf.Dec
|
||||
}
|
||||
|
||||
// AsScale adjusts this amount to set a minimum scale, rounding up, and returns true iff no precision
|
||||
// was lost. (1.1e5).AsScale(5) would return 1.1e5, but (1.1e5).AsScale(6) would return 1e6.
|
||||
func (a infDecAmount) AsScale(scale Scale) (infDecAmount, bool) {
|
||||
tmp := &inf.Dec{}
|
||||
tmp.Round(a.Dec, scale.infScale(), inf.RoundUp)
|
||||
return infDecAmount{tmp}, tmp.Cmp(a.Dec) == 0
|
||||
}
|
||||
|
||||
// AsCanonicalBytes accepts a buffer to write the base-10 string value of this field to, and returns
|
||||
// either that buffer or a larger buffer and the current exponent of the value. The value is adjusted
|
||||
// until the exponent is a multiple of 3 - i.e. 1.1e5 would return "110", 3.
|
||||
func (a infDecAmount) AsCanonicalBytes(out []byte) (result []byte, exponent int32) {
|
||||
mantissa := a.Dec.UnscaledBig()
|
||||
exponent = int32(-a.Dec.Scale())
|
||||
amount := big.NewInt(0).Set(mantissa)
|
||||
// move all factors of 10 into the exponent for easy reasoning
|
||||
amount, times := removeBigIntFactors(amount, bigTen)
|
||||
exponent += times
|
||||
|
||||
// make sure exponent is a multiple of 3
|
||||
for exponent%3 != 0 {
|
||||
amount.Mul(amount, bigTen)
|
||||
exponent--
|
||||
}
|
||||
|
||||
return append(out, amount.String()...), exponent
|
||||
}
|
||||
|
||||
// AsCanonicalBase1024Bytes accepts a buffer to write the base-1024 string value of this field to, and returns
|
||||
// either that buffer or a larger buffer and the current exponent of the value. 2048 is 2 * 1024 ^ 1 and would
|
||||
// return []byte("2048"), 1.
|
||||
func (a infDecAmount) AsCanonicalBase1024Bytes(out []byte) (result []byte, exponent int32) {
|
||||
tmp := &inf.Dec{}
|
||||
tmp.Round(a.Dec, 0, inf.RoundUp)
|
||||
amount, exponent := removeBigIntFactors(tmp.UnscaledBig(), big1024)
|
||||
return append(out, amount.String()...), exponent
|
||||
}
|
327
vendor/github.com/appc/spec/schema/types/resource/math.go
generated
vendored
Normal file
327
vendor/github.com/appc/spec/schema/types/resource/math.go
generated
vendored
Normal file
@ -0,0 +1,327 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resource
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
inf "gopkg.in/inf.v0"
|
||||
)
|
||||
|
||||
const (
|
||||
// maxInt64Factors is the highest value that will be checked when removing factors of 10 from an int64.
|
||||
// It is also the maximum decimal digits that can be represented with an int64.
|
||||
maxInt64Factors = 18
|
||||
)
|
||||
|
||||
var (
|
||||
// Commonly needed big.Int values-- treat as read only!
|
||||
bigTen = big.NewInt(10)
|
||||
bigZero = big.NewInt(0)
|
||||
bigOne = big.NewInt(1)
|
||||
bigThousand = big.NewInt(1000)
|
||||
big1024 = big.NewInt(1024)
|
||||
|
||||
// Commonly needed inf.Dec values-- treat as read only!
|
||||
decZero = inf.NewDec(0, 0)
|
||||
decOne = inf.NewDec(1, 0)
|
||||
decMinusOne = inf.NewDec(-1, 0)
|
||||
decThousand = inf.NewDec(1000, 0)
|
||||
dec1024 = inf.NewDec(1024, 0)
|
||||
decMinus1024 = inf.NewDec(-1024, 0)
|
||||
|
||||
// Largest (in magnitude) number allowed.
|
||||
maxAllowed = infDecAmount{inf.NewDec((1<<63)-1, 0)} // == max int64
|
||||
|
||||
// The maximum value we can represent milli-units for.
|
||||
// Compare with the return value of Quantity.Value() to
|
||||
// see if it's safe to use Quantity.MilliValue().
|
||||
MaxMilliValue = int64(((1 << 63) - 1) / 1000)
|
||||
)
|
||||
|
||||
const mostNegative = -(mostPositive + 1)
|
||||
const mostPositive = 1<<63 - 1
|
||||
|
||||
// int64Add returns a+b, or false if that would overflow int64.
|
||||
func int64Add(a, b int64) (int64, bool) {
|
||||
c := a + b
|
||||
switch {
|
||||
case a > 0 && b > 0:
|
||||
if c < 0 {
|
||||
return 0, false
|
||||
}
|
||||
case a < 0 && b < 0:
|
||||
if c > 0 {
|
||||
return 0, false
|
||||
}
|
||||
if a == mostNegative && b == mostNegative {
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
return c, true
|
||||
}
|
||||
|
||||
// int64Multiply returns a*b, or false if that would overflow or underflow int64.
|
||||
func int64Multiply(a, b int64) (int64, bool) {
|
||||
if a == 0 || b == 0 || a == 1 || b == 1 {
|
||||
return a * b, true
|
||||
}
|
||||
if a == mostNegative || b == mostNegative {
|
||||
return 0, false
|
||||
}
|
||||
c := a * b
|
||||
return c, c/b == a
|
||||
}
|
||||
|
||||
// int64MultiplyScale returns a*b, assuming b is greater than one, or false if that would overflow or underflow int64.
|
||||
// Use when b is known to be greater than one.
|
||||
func int64MultiplyScale(a int64, b int64) (int64, bool) {
|
||||
if a == 0 || a == 1 {
|
||||
return a * b, true
|
||||
}
|
||||
if a == mostNegative && b != 1 {
|
||||
return 0, false
|
||||
}
|
||||
c := a * b
|
||||
return c, c/b == a
|
||||
}
|
||||
|
||||
// int64MultiplyScale10 multiplies a by 10, or returns false if that would overflow. This method is faster than
|
||||
// int64Multiply(a, 10) because the compiler can optimize constant factor multiplication.
|
||||
func int64MultiplyScale10(a int64) (int64, bool) {
|
||||
if a == 0 || a == 1 {
|
||||
return a * 10, true
|
||||
}
|
||||
if a == mostNegative {
|
||||
return 0, false
|
||||
}
|
||||
c := a * 10
|
||||
return c, c/10 == a
|
||||
}
|
||||
|
||||
// int64MultiplyScale100 multiplies a by 100, or returns false if that would overflow. This method is faster than
|
||||
// int64Multiply(a, 100) because the compiler can optimize constant factor multiplication.
|
||||
func int64MultiplyScale100(a int64) (int64, bool) {
|
||||
if a == 0 || a == 1 {
|
||||
return a * 100, true
|
||||
}
|
||||
if a == mostNegative {
|
||||
return 0, false
|
||||
}
|
||||
c := a * 100
|
||||
return c, c/100 == a
|
||||
}
|
||||
|
||||
// int64MultiplyScale1000 multiplies a by 1000, or returns false if that would overflow. This method is faster than
|
||||
// int64Multiply(a, 1000) because the compiler can optimize constant factor multiplication.
|
||||
func int64MultiplyScale1000(a int64) (int64, bool) {
|
||||
if a == 0 || a == 1 {
|
||||
return a * 1000, true
|
||||
}
|
||||
if a == mostNegative {
|
||||
return 0, false
|
||||
}
|
||||
c := a * 1000
|
||||
return c, c/1000 == a
|
||||
}
|
||||
|
||||
// positiveScaleInt64 multiplies base by 10^scale, returning false if the
|
||||
// value overflows. Passing a negative scale is undefined.
|
||||
func positiveScaleInt64(base int64, scale Scale) (int64, bool) {
|
||||
switch scale {
|
||||
case 0:
|
||||
return base, true
|
||||
case 1:
|
||||
return int64MultiplyScale10(base)
|
||||
case 2:
|
||||
return int64MultiplyScale100(base)
|
||||
case 3:
|
||||
return int64MultiplyScale1000(base)
|
||||
case 6:
|
||||
return int64MultiplyScale(base, 1000000)
|
||||
case 9:
|
||||
return int64MultiplyScale(base, 1000000000)
|
||||
default:
|
||||
value := base
|
||||
var ok bool
|
||||
for i := Scale(0); i < scale; i++ {
|
||||
if value, ok = int64MultiplyScale(value, 10); !ok {
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
return value, true
|
||||
}
|
||||
}
|
||||
|
||||
// negativeScaleInt64 reduces base by the provided scale, rounding up, until the
|
||||
// value is zero or the scale is reached. Passing a negative scale is undefined.
|
||||
// The value returned, if not exact, is rounded away from zero.
|
||||
func negativeScaleInt64(base int64, scale Scale) (result int64, exact bool) {
|
||||
if scale == 0 {
|
||||
return base, true
|
||||
}
|
||||
|
||||
value := base
|
||||
var fraction bool
|
||||
for i := Scale(0); i < scale; i++ {
|
||||
if !fraction && value%10 != 0 {
|
||||
fraction = true
|
||||
}
|
||||
value = value / 10
|
||||
if value == 0 {
|
||||
if fraction {
|
||||
if base > 0 {
|
||||
return 1, false
|
||||
}
|
||||
return -1, false
|
||||
}
|
||||
return 0, true
|
||||
}
|
||||
}
|
||||
if fraction {
|
||||
if base > 0 {
|
||||
value += 1
|
||||
} else {
|
||||
value += -1
|
||||
}
|
||||
}
|
||||
return value, !fraction
|
||||
}
|
||||
|
||||
func pow10Int64(b int64) int64 {
|
||||
switch b {
|
||||
case 0:
|
||||
return 1
|
||||
case 1:
|
||||
return 10
|
||||
case 2:
|
||||
return 100
|
||||
case 3:
|
||||
return 1000
|
||||
case 4:
|
||||
return 10000
|
||||
case 5:
|
||||
return 100000
|
||||
case 6:
|
||||
return 1000000
|
||||
case 7:
|
||||
return 10000000
|
||||
case 8:
|
||||
return 100000000
|
||||
case 9:
|
||||
return 1000000000
|
||||
case 10:
|
||||
return 10000000000
|
||||
case 11:
|
||||
return 100000000000
|
||||
case 12:
|
||||
return 1000000000000
|
||||
case 13:
|
||||
return 10000000000000
|
||||
case 14:
|
||||
return 100000000000000
|
||||
case 15:
|
||||
return 1000000000000000
|
||||
case 16:
|
||||
return 10000000000000000
|
||||
case 17:
|
||||
return 100000000000000000
|
||||
case 18:
|
||||
return 1000000000000000000
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// powInt64 raises a to the bth power. Is not overflow aware.
|
||||
func powInt64(a, b int64) int64 {
|
||||
p := int64(1)
|
||||
for b > 0 {
|
||||
if b&1 != 0 {
|
||||
p *= a
|
||||
}
|
||||
b >>= 1
|
||||
a *= a
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// negativeScaleInt64 returns the result of dividing base by scale * 10 and the remainder, or
|
||||
// false if no such division is possible. Dividing by negative scales is undefined.
|
||||
func divideByScaleInt64(base int64, scale Scale) (result, remainder int64, exact bool) {
|
||||
if scale == 0 {
|
||||
return base, 0, true
|
||||
}
|
||||
// the max scale representable in base 10 in an int64 is 18 decimal places
|
||||
if scale >= 18 {
|
||||
return 0, base, false
|
||||
}
|
||||
divisor := pow10Int64(int64(scale))
|
||||
return base / divisor, base % divisor, true
|
||||
}
|
||||
|
||||
// removeInt64Factors divides in a loop; the return values have the property that
|
||||
// value == result * base ^ scale
|
||||
func removeInt64Factors(value int64, base int64) (result int64, times int32) {
|
||||
times = 0
|
||||
result = value
|
||||
negative := result < 0
|
||||
if negative {
|
||||
result = -result
|
||||
}
|
||||
switch base {
|
||||
// allow the compiler to optimize the common cases
|
||||
case 10:
|
||||
for result >= 10 && result%10 == 0 {
|
||||
times++
|
||||
result = result / 10
|
||||
}
|
||||
// allow the compiler to optimize the common cases
|
||||
case 1024:
|
||||
for result >= 1024 && result%1024 == 0 {
|
||||
times++
|
||||
result = result / 1024
|
||||
}
|
||||
default:
|
||||
for result >= base && result%base == 0 {
|
||||
times++
|
||||
result = result / base
|
||||
}
|
||||
}
|
||||
if negative {
|
||||
result = -result
|
||||
}
|
||||
return result, times
|
||||
}
|
||||
|
||||
// removeBigIntFactors divides in a loop; the return values have the property that
|
||||
// d == result * factor ^ times
|
||||
// d may be modified in place.
|
||||
// If d == 0, then the return values will be (0, 0)
|
||||
func removeBigIntFactors(d, factor *big.Int) (result *big.Int, times int32) {
|
||||
q := big.NewInt(0)
|
||||
m := big.NewInt(0)
|
||||
for d.Cmp(bigZero) != 0 {
|
||||
q.DivMod(d, factor, m)
|
||||
if m.Cmp(bigZero) != 0 {
|
||||
break
|
||||
}
|
||||
times++
|
||||
d, q = q, d
|
||||
}
|
||||
return d, times
|
||||
}
|
768
vendor/github.com/appc/spec/schema/types/resource/quantity.go
generated
vendored
Normal file
768
vendor/github.com/appc/spec/schema/types/resource/quantity.go
generated
vendored
Normal file
@ -0,0 +1,768 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resource
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
flag "github.com/spf13/pflag"
|
||||
|
||||
inf "gopkg.in/inf.v0"
|
||||
)
|
||||
|
||||
// Quantity is a fixed-point representation of a number.
|
||||
// It provides convenient marshaling/unmarshaling in JSON and YAML,
|
||||
// in addition to String() and Int64() accessors.
|
||||
//
|
||||
// The serialization format is:
|
||||
//
|
||||
// <quantity> ::= <signedNumber><suffix>
|
||||
// (Note that <suffix> may be empty, from the "" case in <decimalSI>.)
|
||||
// <digit> ::= 0 | 1 | ... | 9
|
||||
// <digits> ::= <digit> | <digit><digits>
|
||||
// <number> ::= <digits> | <digits>.<digits> | <digits>. | .<digits>
|
||||
// <sign> ::= "+" | "-"
|
||||
// <signedNumber> ::= <number> | <sign><number>
|
||||
// <suffix> ::= <binarySI> | <decimalExponent> | <decimalSI>
|
||||
// <binarySI> ::= Ki | Mi | Gi | Ti | Pi | Ei
|
||||
// (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)
|
||||
// <decimalSI> ::= m | "" | k | M | G | T | P | E
|
||||
// (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)
|
||||
// <decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber>
|
||||
//
|
||||
// No matter which of the three exponent forms is used, no quantity may represent
|
||||
// a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal
|
||||
// places. Numbers larger or more precise will be capped or rounded up.
|
||||
// (E.g.: 0.1m will rounded up to 1m.)
|
||||
// This may be extended in the future if we require larger or smaller quantities.
|
||||
//
|
||||
// When a Quantity is parsed from a string, it will remember the type of suffix
|
||||
// it had, and will use the same type again when it is serialized.
|
||||
//
|
||||
// Before serializing, Quantity will be put in "canonical form".
|
||||
// This means that Exponent/suffix will be adjusted up or down (with a
|
||||
// corresponding increase or decrease in Mantissa) such that:
|
||||
// a. No precision is lost
|
||||
// b. No fractional digits will be emitted
|
||||
// c. The exponent (or suffix) is as large as possible.
|
||||
// The sign will be omitted unless the number is negative.
|
||||
//
|
||||
// Examples:
|
||||
// 1.5 will be serialized as "1500m"
|
||||
// 1.5Gi will be serialized as "1536Mi"
|
||||
//
|
||||
// NOTE: We reserve the right to amend this canonical format, perhaps to
|
||||
// allow 1.5 to be canonical.
|
||||
// TODO: Remove above disclaimer after all bikeshedding about format is over,
|
||||
// or after March 2015.
|
||||
//
|
||||
// Note that the quantity will NEVER be internally represented by a
|
||||
// floating point number. That is the whole point of this exercise.
|
||||
//
|
||||
// Non-canonical values will still parse as long as they are well formed,
|
||||
// but will be re-emitted in their canonical form. (So always use canonical
|
||||
// form, or don't diff.)
|
||||
//
|
||||
// This format is intended to make it difficult to use these numbers without
|
||||
// writing some sort of special handling code in the hopes that that will
|
||||
// cause implementors to also use a fixed point implementation.
|
||||
//
|
||||
// +gencopy=false
|
||||
// +protobuf=true
|
||||
// +protobuf.embed=string
|
||||
// +protobuf.options.marshal=false
|
||||
// +protobuf.options.(gogoproto.goproto_stringer)=false
|
||||
type Quantity struct {
|
||||
// i is the quantity in int64 scaled form, if d.Dec == nil
|
||||
i int64Amount
|
||||
// d is the quantity in inf.Dec form if d.Dec != nil
|
||||
d infDecAmount
|
||||
// s is the generated value of this quantity to avoid recalculation
|
||||
s string
|
||||
|
||||
// Change Format at will. See the comment for Canonicalize for
|
||||
// more details.
|
||||
Format
|
||||
}
|
||||
|
||||
// CanonicalValue allows a quantity amount to be converted to a string.
|
||||
type CanonicalValue interface {
|
||||
// AsCanonicalBytes returns a byte array representing the string representation
|
||||
// of the value mantissa and an int32 representing its exponent in base-10. Callers may
|
||||
// pass a byte slice to the method to avoid allocations.
|
||||
AsCanonicalBytes(out []byte) ([]byte, int32)
|
||||
// AsCanonicalBase1024Bytes returns a byte array representing the string representation
|
||||
// of the value mantissa and an int32 representing its exponent in base-1024. Callers
|
||||
// may pass a byte slice to the method to avoid allocations.
|
||||
AsCanonicalBase1024Bytes(out []byte) ([]byte, int32)
|
||||
}
|
||||
|
||||
// Format lists the three possible formattings of a quantity.
|
||||
type Format string
|
||||
|
||||
const (
|
||||
DecimalExponent = Format("DecimalExponent") // e.g., 12e6
|
||||
BinarySI = Format("BinarySI") // e.g., 12Mi (12 * 2^20)
|
||||
DecimalSI = Format("DecimalSI") // e.g., 12M (12 * 10^6)
|
||||
)
|
||||
|
||||
// MustParse turns the given string into a quantity or panics; for tests
|
||||
// or others cases where you know the string is valid.
|
||||
func MustParse(str string) Quantity {
|
||||
q, err := ParseQuantity(str)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot parse '%v': %v", str, err))
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
const (
|
||||
// splitREString is used to separate a number from its suffix; as such,
|
||||
// this is overly permissive, but that's OK-- it will be checked later.
|
||||
splitREString = "^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$"
|
||||
)
|
||||
|
||||
var (
|
||||
// splitRE is used to get the various parts of a number.
|
||||
splitRE = regexp.MustCompile(splitREString)
|
||||
|
||||
// Errors that could happen while parsing a string.
|
||||
ErrFormatWrong = errors.New("quantities must match the regular expression '" + splitREString + "'")
|
||||
ErrNumeric = errors.New("unable to parse numeric part of quantity")
|
||||
ErrSuffix = errors.New("unable to parse quantity's suffix")
|
||||
)
|
||||
|
||||
// parseQuantityString is a fast scanner for quantity values.
|
||||
func parseQuantityString(str string) (positive bool, value, num, denom, suffix string, err error) {
|
||||
positive = true
|
||||
pos := 0
|
||||
end := len(str)
|
||||
|
||||
// handle leading sign
|
||||
if pos < end {
|
||||
switch str[0] {
|
||||
case '-':
|
||||
positive = false
|
||||
pos++
|
||||
case '+':
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
// strip leading zeros
|
||||
Zeroes:
|
||||
for i := pos; ; i++ {
|
||||
if i >= end {
|
||||
num = "0"
|
||||
value = num
|
||||
return
|
||||
}
|
||||
switch str[i] {
|
||||
case '0':
|
||||
pos++
|
||||
default:
|
||||
break Zeroes
|
||||
}
|
||||
}
|
||||
|
||||
// extract the numerator
|
||||
Num:
|
||||
for i := pos; ; i++ {
|
||||
if i >= end {
|
||||
num = str[pos:end]
|
||||
value = str[0:end]
|
||||
return
|
||||
}
|
||||
switch str[i] {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
default:
|
||||
num = str[pos:i]
|
||||
pos = i
|
||||
break Num
|
||||
}
|
||||
}
|
||||
|
||||
// if we stripped all numerator positions, always return 0
|
||||
if len(num) == 0 {
|
||||
num = "0"
|
||||
}
|
||||
|
||||
// handle a denominator
|
||||
if pos < end && str[pos] == '.' {
|
||||
pos++
|
||||
Denom:
|
||||
for i := pos; ; i++ {
|
||||
if i >= end {
|
||||
denom = str[pos:end]
|
||||
value = str[0:end]
|
||||
return
|
||||
}
|
||||
switch str[i] {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
default:
|
||||
denom = str[pos:i]
|
||||
pos = i
|
||||
break Denom
|
||||
}
|
||||
}
|
||||
// TODO: we currently allow 1.G, but we may not want to in the future.
|
||||
// if len(denom) == 0 {
|
||||
// err = ErrFormatWrong
|
||||
// return
|
||||
// }
|
||||
}
|
||||
value = str[0:pos]
|
||||
|
||||
// grab the elements of the suffix
|
||||
suffixStart := pos
|
||||
for i := pos; ; i++ {
|
||||
if i >= end {
|
||||
suffix = str[suffixStart:end]
|
||||
return
|
||||
}
|
||||
if !strings.ContainsAny(str[i:i+1], "eEinumkKMGTP") {
|
||||
pos = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if pos < end {
|
||||
switch str[pos] {
|
||||
case '-', '+':
|
||||
pos++
|
||||
}
|
||||
}
|
||||
Suffix:
|
||||
for i := pos; ; i++ {
|
||||
if i >= end {
|
||||
suffix = str[suffixStart:end]
|
||||
return
|
||||
}
|
||||
switch str[i] {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
default:
|
||||
break Suffix
|
||||
}
|
||||
}
|
||||
// we encountered a non decimal in the Suffix loop, but the last character
|
||||
// was not a valid exponent
|
||||
err = ErrFormatWrong
|
||||
return
|
||||
}
|
||||
|
||||
// ParseQuantity turns str into a Quantity, or returns an error.
|
||||
func ParseQuantity(str string) (Quantity, error) {
|
||||
if len(str) == 0 {
|
||||
return Quantity{}, ErrFormatWrong
|
||||
}
|
||||
if str == "0" {
|
||||
return Quantity{Format: DecimalSI, s: str}, nil
|
||||
}
|
||||
|
||||
positive, value, num, denom, suf, err := parseQuantityString(str)
|
||||
if err != nil {
|
||||
return Quantity{}, err
|
||||
}
|
||||
|
||||
base, exponent, format, ok := quantitySuffixer.interpret(suffix(suf))
|
||||
if !ok {
|
||||
return Quantity{}, ErrSuffix
|
||||
}
|
||||
|
||||
precision := int32(0)
|
||||
scale := int32(0)
|
||||
mantissa := int64(1)
|
||||
switch format {
|
||||
case DecimalExponent, DecimalSI:
|
||||
scale = exponent
|
||||
precision = maxInt64Factors - int32(len(num)+len(denom))
|
||||
case BinarySI:
|
||||
scale = 0
|
||||
switch {
|
||||
case exponent >= 0 && len(denom) == 0:
|
||||
// only handle positive binary numbers with the fast path
|
||||
mantissa = int64(int64(mantissa) << uint64(exponent))
|
||||
// 1Mi (2^20) has ~6 digits of decimal precision, so exponent*3/10 -1 is roughly the precision
|
||||
precision = 15 - int32(len(num)) - int32(float32(exponent)*3/10) - 1
|
||||
default:
|
||||
precision = -1
|
||||
}
|
||||
}
|
||||
|
||||
if precision >= 0 {
|
||||
// if we have a denominator, shift the entire value to the left by the number of places in the
|
||||
// denominator
|
||||
scale -= int32(len(denom))
|
||||
if scale >= int32(Nano) {
|
||||
shifted := num + denom
|
||||
|
||||
var value int64
|
||||
value, err := strconv.ParseInt(shifted, 10, 64)
|
||||
if err != nil {
|
||||
return Quantity{}, ErrNumeric
|
||||
}
|
||||
if result, ok := int64Multiply(value, int64(mantissa)); ok {
|
||||
if !positive {
|
||||
result = -result
|
||||
}
|
||||
// if the number is in canonical form, reuse the string
|
||||
switch format {
|
||||
case BinarySI:
|
||||
if exponent%10 == 0 && (value&0x07 != 0) {
|
||||
return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format, s: str}, nil
|
||||
}
|
||||
default:
|
||||
if scale%3 == 0 && !strings.HasSuffix(shifted, "000") && shifted[0] != '0' {
|
||||
return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format, s: str}, nil
|
||||
}
|
||||
}
|
||||
return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
amount := new(inf.Dec)
|
||||
if _, ok := amount.SetString(value); !ok {
|
||||
return Quantity{}, ErrNumeric
|
||||
}
|
||||
|
||||
// So that no one but us has to think about suffixes, remove it.
|
||||
if base == 10 {
|
||||
amount.SetScale(amount.Scale() + Scale(exponent).infScale())
|
||||
} else if base == 2 {
|
||||
// numericSuffix = 2 ** exponent
|
||||
numericSuffix := big.NewInt(1).Lsh(bigOne, uint(exponent))
|
||||
ub := amount.UnscaledBig()
|
||||
amount.SetUnscaledBig(ub.Mul(ub, numericSuffix))
|
||||
}
|
||||
|
||||
// Cap at min/max bounds.
|
||||
sign := amount.Sign()
|
||||
if sign == -1 {
|
||||
amount.Neg(amount)
|
||||
}
|
||||
|
||||
// This rounds non-zero values up to the minimum representable value, under the theory that
|
||||
// if you want some resources, you should get some resources, even if you asked for way too small
|
||||
// of an amount. Arguably, this should be inf.RoundHalfUp (normal rounding), but that would have
|
||||
// the side effect of rounding values < .5n to zero.
|
||||
if v, ok := amount.Unscaled(); v != int64(0) || !ok {
|
||||
amount.Round(amount, Nano.infScale(), inf.RoundUp)
|
||||
}
|
||||
|
||||
// The max is just a simple cap.
|
||||
// TODO: this prevents accumulating quantities greater than int64, for instance quota across a cluster
|
||||
if format == BinarySI && amount.Cmp(maxAllowed.Dec) > 0 {
|
||||
amount.Set(maxAllowed.Dec)
|
||||
}
|
||||
|
||||
if format == BinarySI && amount.Cmp(decOne) < 0 && amount.Cmp(decZero) > 0 {
|
||||
// This avoids rounding and hopefully confusion, too.
|
||||
format = DecimalSI
|
||||
}
|
||||
if sign == -1 {
|
||||
amount.Neg(amount)
|
||||
}
|
||||
|
||||
return Quantity{d: infDecAmount{amount}, Format: format}, nil
|
||||
}
|
||||
|
||||
// CanonicalizeBytes returns the canonical form of q and its suffix (see comment on Quantity).
|
||||
//
|
||||
// Note about BinarySI:
|
||||
// * If q.Format is set to BinarySI and q.Amount represents a non-zero value between
|
||||
// -1 and +1, it will be emitted as if q.Format were DecimalSI.
|
||||
// * Otherwise, if q.Format is set to BinarySI, frational parts of q.Amount will be
|
||||
// rounded up. (1.1i becomes 2i.)
|
||||
func (q *Quantity) CanonicalizeBytes(out []byte) (result, suffix []byte) {
|
||||
if q.IsZero() {
|
||||
return zeroBytes, nil
|
||||
}
|
||||
|
||||
var rounded CanonicalValue
|
||||
format := q.Format
|
||||
switch format {
|
||||
case DecimalExponent, DecimalSI:
|
||||
case BinarySI:
|
||||
if q.CmpInt64(-1024) > 0 && q.CmpInt64(1024) < 0 {
|
||||
// This avoids rounding and hopefully confusion, too.
|
||||
format = DecimalSI
|
||||
} else {
|
||||
var exact bool
|
||||
if rounded, exact = q.AsScale(0); !exact {
|
||||
// Don't lose precision-- show as DecimalSI
|
||||
format = DecimalSI
|
||||
}
|
||||
}
|
||||
default:
|
||||
format = DecimalExponent
|
||||
}
|
||||
|
||||
// TODO: If BinarySI formatting is requested but would cause rounding, upgrade to
|
||||
// one of the other formats.
|
||||
switch format {
|
||||
case DecimalExponent, DecimalSI:
|
||||
number, exponent := q.AsCanonicalBytes(out)
|
||||
suffix, _ := quantitySuffixer.constructBytes(10, exponent, format)
|
||||
return number, suffix
|
||||
default:
|
||||
// format must be BinarySI
|
||||
number, exponent := rounded.AsCanonicalBase1024Bytes(out)
|
||||
suffix, _ := quantitySuffixer.constructBytes(2, exponent*10, format)
|
||||
return number, suffix
|
||||
}
|
||||
}
|
||||
|
||||
// AsInt64 returns a representation of the current value as an int64 if a fast conversion
|
||||
// is possible. If false is returned, callers must use the inf.Dec form of this quantity.
|
||||
func (q *Quantity) AsInt64() (int64, bool) {
|
||||
if q.d.Dec != nil {
|
||||
return 0, false
|
||||
}
|
||||
return q.i.AsInt64()
|
||||
}
|
||||
|
||||
// ToDec promotes the quantity in place to use an inf.Dec representation and returns itself.
|
||||
func (q *Quantity) ToDec() *Quantity {
|
||||
if q.d.Dec == nil {
|
||||
q.d.Dec = q.i.AsDec()
|
||||
q.i = int64Amount{}
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
// AsDec returns the quantity as represented by a scaled inf.Dec.
|
||||
func (q *Quantity) AsDec() *inf.Dec {
|
||||
if q.d.Dec != nil {
|
||||
return q.d.Dec
|
||||
}
|
||||
q.d.Dec = q.i.AsDec()
|
||||
q.i = int64Amount{}
|
||||
return q.d.Dec
|
||||
}
|
||||
|
||||
// AsCanonicalBytes returns the canonical byte representation of this quantity as a mantissa
|
||||
// and base 10 exponent. The out byte slice may be passed to the method to avoid an extra
|
||||
// allocation.
|
||||
func (q *Quantity) AsCanonicalBytes(out []byte) (result []byte, exponent int32) {
|
||||
if q.d.Dec != nil {
|
||||
return q.d.AsCanonicalBytes(out)
|
||||
}
|
||||
return q.i.AsCanonicalBytes(out)
|
||||
}
|
||||
|
||||
// IsZero returns true if the quantity is equal to zero.
|
||||
func (q *Quantity) IsZero() bool {
|
||||
if q.d.Dec != nil {
|
||||
return q.d.Dec.Sign() == 0
|
||||
}
|
||||
return q.i.value == 0
|
||||
}
|
||||
|
||||
// Sign returns 0 if the quantity is zero, -1 if the quantity is less than zero, or 1 if the
|
||||
// quantity is greater than zero.
|
||||
func (q *Quantity) Sign() int {
|
||||
if q.d.Dec != nil {
|
||||
return q.d.Dec.Sign()
|
||||
}
|
||||
return q.i.Sign()
|
||||
}
|
||||
|
||||
// AsScaled returns the current value, rounded up to the provided scale, and returns
|
||||
// false if the scale resulted in a loss of precision.
|
||||
func (q *Quantity) AsScale(scale Scale) (CanonicalValue, bool) {
|
||||
if q.d.Dec != nil {
|
||||
return q.d.AsScale(scale)
|
||||
}
|
||||
return q.i.AsScale(scale)
|
||||
}
|
||||
|
||||
// RoundUp updates the quantity to the provided scale, ensuring that the value is at
|
||||
// least 1. False is returned if the rounding operation resulted in a loss of precision.
|
||||
// Negative numbers are rounded away from zero (-9 scale 1 rounds to -10).
|
||||
func (q *Quantity) RoundUp(scale Scale) bool {
|
||||
if q.d.Dec != nil {
|
||||
q.s = ""
|
||||
d, exact := q.d.AsScale(scale)
|
||||
q.d = d
|
||||
return exact
|
||||
}
|
||||
// avoid clearing the string value if we have already calculated it
|
||||
if q.i.scale >= scale {
|
||||
return true
|
||||
}
|
||||
q.s = ""
|
||||
i, exact := q.i.AsScale(scale)
|
||||
q.i = i
|
||||
return exact
|
||||
}
|
||||
|
||||
// Add adds the provide y quantity to the current value. If the current value is zero,
|
||||
// the format of the quantity will be updated to the format of y.
|
||||
func (q *Quantity) Add(y Quantity) {
|
||||
q.s = ""
|
||||
if q.d.Dec == nil && y.d.Dec == nil {
|
||||
if q.i.value == 0 {
|
||||
q.Format = y.Format
|
||||
}
|
||||
if q.i.Add(y.i) {
|
||||
return
|
||||
}
|
||||
} else if q.IsZero() {
|
||||
q.Format = y.Format
|
||||
}
|
||||
q.ToDec().d.Dec.Add(q.d.Dec, y.AsDec())
|
||||
}
|
||||
|
||||
// Sub subtracts the provided quantity from the current value in place. If the current
|
||||
// value is zero, the format of the quantity will be updated to the format of y.
|
||||
func (q *Quantity) Sub(y Quantity) {
|
||||
q.s = ""
|
||||
if q.IsZero() {
|
||||
q.Format = y.Format
|
||||
}
|
||||
if q.d.Dec == nil && y.d.Dec == nil && q.i.Sub(y.i) {
|
||||
return
|
||||
}
|
||||
q.ToDec().d.Dec.Sub(q.d.Dec, y.AsDec())
|
||||
}
|
||||
|
||||
// Cmp returns 0 if the quantity is equal to y, -1 if the quantity is less than y, or 1 if the
|
||||
// quantity is greater than y.
|
||||
func (q *Quantity) Cmp(y Quantity) int {
|
||||
if q.d.Dec == nil && y.d.Dec == nil {
|
||||
return q.i.Cmp(y.i)
|
||||
}
|
||||
return q.AsDec().Cmp(y.AsDec())
|
||||
}
|
||||
|
||||
// CmpInt64 returns 0 if the quantity is equal to y, -1 if the quantity is less than y, or 1 if the
|
||||
// quantity is greater than y.
|
||||
func (q *Quantity) CmpInt64(y int64) int {
|
||||
if q.d.Dec != nil {
|
||||
return q.d.Dec.Cmp(inf.NewDec(y, inf.Scale(0)))
|
||||
}
|
||||
return q.i.Cmp(int64Amount{value: y})
|
||||
}
|
||||
|
||||
// Neg sets quantity to be the negative value of itself.
|
||||
func (q *Quantity) Neg() {
|
||||
q.s = ""
|
||||
if q.d.Dec == nil {
|
||||
q.i.value = -q.i.value
|
||||
return
|
||||
}
|
||||
q.d.Dec.Neg(q.d.Dec)
|
||||
}
|
||||
|
||||
// int64QuantityExpectedBytes is the expected width in bytes of the canonical string representation
|
||||
// of most Quantity values.
|
||||
const int64QuantityExpectedBytes = 18
|
||||
|
||||
// String formats the Quantity as a string, caching the result if not calculated.
|
||||
// String is an expensive operation and caching this result significantly reduces the cost of
|
||||
// normal parse / marshal operations on Quantity.
|
||||
func (q *Quantity) String() string {
|
||||
if len(q.s) == 0 {
|
||||
result := make([]byte, 0, int64QuantityExpectedBytes)
|
||||
number, suffix := q.CanonicalizeBytes(result)
|
||||
number = append(number, suffix...)
|
||||
q.s = string(number)
|
||||
}
|
||||
return q.s
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaller interface.
|
||||
func (q Quantity) MarshalJSON() ([]byte, error) {
|
||||
if len(q.s) > 0 {
|
||||
out := make([]byte, len(q.s)+2)
|
||||
out[0], out[len(out)-1] = '"', '"'
|
||||
copy(out[1:], q.s)
|
||||
return out, nil
|
||||
}
|
||||
result := make([]byte, int64QuantityExpectedBytes, int64QuantityExpectedBytes)
|
||||
result[0] = '"'
|
||||
number, suffix := q.CanonicalizeBytes(result[1:1])
|
||||
// if the same slice was returned to us that we passed in, avoid another allocation by copying number into
|
||||
// the source slice and returning that
|
||||
if len(number) > 0 && &number[0] == &result[1] && (len(number)+len(suffix)+2) <= int64QuantityExpectedBytes {
|
||||
number = append(number, suffix...)
|
||||
number = append(number, '"')
|
||||
return result[:1+len(number)], nil
|
||||
}
|
||||
// if CanonicalizeBytes needed more space than our slice provided, we may need to allocate again so use
|
||||
// append
|
||||
result = result[:1]
|
||||
result = append(result, number...)
|
||||
result = append(result, suffix...)
|
||||
result = append(result, '"')
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaller interface.
|
||||
// TODO: Remove support for leading/trailing whitespace
|
||||
func (q *Quantity) UnmarshalJSON(value []byte) error {
|
||||
l := len(value)
|
||||
if l == 4 && bytes.Equal(value, []byte("null")) {
|
||||
q.d.Dec = nil
|
||||
q.i = int64Amount{}
|
||||
return nil
|
||||
}
|
||||
if l >= 2 && value[0] == '"' && value[l-1] == '"' {
|
||||
value = value[1 : l-1]
|
||||
}
|
||||
|
||||
parsed, err := ParseQuantity(strings.TrimSpace(string(value)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// This copy is safe because parsed will not be referred to again.
|
||||
*q = parsed
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewQuantity returns a new Quantity representing the given
|
||||
// value in the given format.
|
||||
func NewQuantity(value int64, format Format) *Quantity {
|
||||
return &Quantity{
|
||||
i: int64Amount{value: value},
|
||||
Format: format,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMilliQuantity returns a new Quantity representing the given
|
||||
// value * 1/1000 in the given format. Note that BinarySI formatting
|
||||
// will round fractional values, and will be changed to DecimalSI for
|
||||
// values x where (-1 < x < 1) && (x != 0).
|
||||
func NewMilliQuantity(value int64, format Format) *Quantity {
|
||||
return &Quantity{
|
||||
i: int64Amount{value: value, scale: -3},
|
||||
Format: format,
|
||||
}
|
||||
}
|
||||
|
||||
// NewScaledQuantity returns a new Quantity representing the given
|
||||
// value * 10^scale in DecimalSI format.
|
||||
func NewScaledQuantity(value int64, scale Scale) *Quantity {
|
||||
return &Quantity{
|
||||
i: int64Amount{value: value, scale: scale},
|
||||
Format: DecimalSI,
|
||||
}
|
||||
}
|
||||
|
||||
// Value returns the value of q; any fractional part will be lost.
|
||||
func (q *Quantity) Value() int64 {
|
||||
return q.ScaledValue(0)
|
||||
}
|
||||
|
||||
// MilliValue returns the value of ceil(q * 1000); this could overflow an int64;
|
||||
// if that's a concern, call Value() first to verify the number is small enough.
|
||||
func (q *Quantity) MilliValue() int64 {
|
||||
return q.ScaledValue(Milli)
|
||||
}
|
||||
|
||||
// ScaledValue returns the value of ceil(q * 10^scale); this could overflow an int64.
|
||||
// To detect overflow, call Value() first and verify the expected magnitude.
|
||||
func (q *Quantity) ScaledValue(scale Scale) int64 {
|
||||
if q.d.Dec == nil {
|
||||
i, _ := q.i.AsScaledInt64(scale)
|
||||
return i
|
||||
}
|
||||
dec := q.d.Dec
|
||||
return scaledValue(dec.UnscaledBig(), int(dec.Scale()), int(scale.infScale()))
|
||||
}
|
||||
|
||||
// Set sets q's value to be value.
|
||||
func (q *Quantity) Set(value int64) {
|
||||
q.SetScaled(value, 0)
|
||||
}
|
||||
|
||||
// SetMilli sets q's value to be value * 1/1000.
|
||||
func (q *Quantity) SetMilli(value int64) {
|
||||
q.SetScaled(value, Milli)
|
||||
}
|
||||
|
||||
// SetScaled sets q's value to be value * 10^scale
|
||||
func (q *Quantity) SetScaled(value int64, scale Scale) {
|
||||
q.s = ""
|
||||
q.d.Dec = nil
|
||||
q.i = int64Amount{value: value, scale: scale}
|
||||
}
|
||||
|
||||
// Copy is a convenience function that makes a deep copy for you. Non-deep
|
||||
// copies of quantities share pointers and you will regret that.
|
||||
func (q *Quantity) Copy() *Quantity {
|
||||
if q.d.Dec == nil {
|
||||
return &Quantity{
|
||||
s: q.s,
|
||||
i: q.i,
|
||||
Format: q.Format,
|
||||
}
|
||||
}
|
||||
tmp := &inf.Dec{}
|
||||
return &Quantity{
|
||||
s: q.s,
|
||||
d: infDecAmount{tmp.Set(q.d.Dec)},
|
||||
Format: q.Format,
|
||||
}
|
||||
}
|
||||
|
||||
// qFlag is a helper type for the Flag function
|
||||
type qFlag struct {
|
||||
dest *Quantity
|
||||
}
|
||||
|
||||
// Sets the value of the internal Quantity. (used by flag & pflag)
|
||||
func (qf qFlag) Set(val string) error {
|
||||
q, err := ParseQuantity(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// This copy is OK because q will not be referenced again.
|
||||
*qf.dest = q
|
||||
return nil
|
||||
}
|
||||
|
||||
// Converts the value of the internal Quantity to a string. (used by flag & pflag)
|
||||
func (qf qFlag) String() string {
|
||||
return qf.dest.String()
|
||||
}
|
||||
|
||||
// States the type of flag this is (Quantity). (used by pflag)
|
||||
func (qf qFlag) Type() string {
|
||||
return "quantity"
|
||||
}
|
||||
|
||||
// QuantityFlag is a helper that makes a quantity flag (using standard flag package).
|
||||
// Will panic if defaultValue is not a valid quantity.
|
||||
func QuantityFlag(flagName, defaultValue, description string) *Quantity {
|
||||
q := MustParse(defaultValue)
|
||||
flag.Var(NewQuantityFlagValue(&q), flagName, description)
|
||||
return &q
|
||||
}
|
||||
|
||||
// NewQuantityFlagValue returns an object that can be used to back a flag,
|
||||
// pointing at the given Quantity variable.
|
||||
func NewQuantityFlagValue(q *Quantity) flag.Value {
|
||||
return qFlag{q}
|
||||
}
|
95
vendor/github.com/appc/spec/schema/types/resource/scale_int.go
generated
vendored
Normal file
95
vendor/github.com/appc/spec/schema/types/resource/scale_int.go
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resource
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/big"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// A sync pool to reduce allocation.
|
||||
intPool sync.Pool
|
||||
maxInt64 = big.NewInt(math.MaxInt64)
|
||||
)
|
||||
|
||||
func init() {
|
||||
intPool.New = func() interface{} {
|
||||
return &big.Int{}
|
||||
}
|
||||
}
|
||||
|
||||
// scaledValue scales given unscaled value from scale to new Scale and returns
|
||||
// an int64. It ALWAYS rounds up the result when scale down. The final result might
|
||||
// overflow.
|
||||
//
|
||||
// scale, newScale represents the scale of the unscaled decimal.
|
||||
// The mathematical value of the decimal is unscaled * 10**(-scale).
|
||||
func scaledValue(unscaled *big.Int, scale, newScale int) int64 {
|
||||
dif := scale - newScale
|
||||
if dif == 0 {
|
||||
return unscaled.Int64()
|
||||
}
|
||||
|
||||
// Handle scale up
|
||||
// This is an easy case, we do not need to care about rounding and overflow.
|
||||
// If any intermediate operation causes overflow, the result will overflow.
|
||||
if dif < 0 {
|
||||
return unscaled.Int64() * int64(math.Pow10(-dif))
|
||||
}
|
||||
|
||||
// Handle scale down
|
||||
// We have to be careful about the intermediate operations.
|
||||
|
||||
// fast path when unscaled < max.Int64 and exp(10,dif) < max.Int64
|
||||
const log10MaxInt64 = 19
|
||||
if unscaled.Cmp(maxInt64) < 0 && dif < log10MaxInt64 {
|
||||
divide := int64(math.Pow10(dif))
|
||||
result := unscaled.Int64() / divide
|
||||
mod := unscaled.Int64() % divide
|
||||
if mod != 0 {
|
||||
return result + 1
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// We should only convert back to int64 when getting the result.
|
||||
divisor := intPool.Get().(*big.Int)
|
||||
exp := intPool.Get().(*big.Int)
|
||||
result := intPool.Get().(*big.Int)
|
||||
defer func() {
|
||||
intPool.Put(divisor)
|
||||
intPool.Put(exp)
|
||||
intPool.Put(result)
|
||||
}()
|
||||
|
||||
// divisor = 10^(dif)
|
||||
// TODO: create loop up table if exp costs too much.
|
||||
divisor.Exp(bigTen, exp.SetInt64(int64(dif)), nil)
|
||||
// reuse exp
|
||||
remainder := exp
|
||||
|
||||
// result = unscaled / divisor
|
||||
// remainder = unscaled % divisor
|
||||
result.DivMod(unscaled, divisor, remainder)
|
||||
if remainder.Sign() != 0 {
|
||||
return result.Int64() + 1
|
||||
}
|
||||
|
||||
return result.Int64()
|
||||
}
|
198
vendor/github.com/appc/spec/schema/types/resource/suffix.go
generated
vendored
Normal file
198
vendor/github.com/appc/spec/schema/types/resource/suffix.go
generated
vendored
Normal file
@ -0,0 +1,198 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resource
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type suffix string
|
||||
|
||||
// suffixer can interpret and construct suffixes.
|
||||
type suffixer interface {
|
||||
interpret(suffix) (base, exponent int32, fmt Format, ok bool)
|
||||
construct(base, exponent int32, fmt Format) (s suffix, ok bool)
|
||||
constructBytes(base, exponent int32, fmt Format) (s []byte, ok bool)
|
||||
}
|
||||
|
||||
// quantitySuffixer handles suffixes for all three formats that quantity
|
||||
// can handle.
|
||||
var quantitySuffixer = newSuffixer()
|
||||
|
||||
type bePair struct {
|
||||
base, exponent int32
|
||||
}
|
||||
|
||||
type listSuffixer struct {
|
||||
suffixToBE map[suffix]bePair
|
||||
beToSuffix map[bePair]suffix
|
||||
beToSuffixBytes map[bePair][]byte
|
||||
}
|
||||
|
||||
func (ls *listSuffixer) addSuffix(s suffix, pair bePair) {
|
||||
if ls.suffixToBE == nil {
|
||||
ls.suffixToBE = map[suffix]bePair{}
|
||||
}
|
||||
if ls.beToSuffix == nil {
|
||||
ls.beToSuffix = map[bePair]suffix{}
|
||||
}
|
||||
if ls.beToSuffixBytes == nil {
|
||||
ls.beToSuffixBytes = map[bePair][]byte{}
|
||||
}
|
||||
ls.suffixToBE[s] = pair
|
||||
ls.beToSuffix[pair] = s
|
||||
ls.beToSuffixBytes[pair] = []byte(s)
|
||||
}
|
||||
|
||||
func (ls *listSuffixer) lookup(s suffix) (base, exponent int32, ok bool) {
|
||||
pair, ok := ls.suffixToBE[s]
|
||||
if !ok {
|
||||
return 0, 0, false
|
||||
}
|
||||
return pair.base, pair.exponent, true
|
||||
}
|
||||
|
||||
func (ls *listSuffixer) construct(base, exponent int32) (s suffix, ok bool) {
|
||||
s, ok = ls.beToSuffix[bePair{base, exponent}]
|
||||
return
|
||||
}
|
||||
|
||||
func (ls *listSuffixer) constructBytes(base, exponent int32) (s []byte, ok bool) {
|
||||
s, ok = ls.beToSuffixBytes[bePair{base, exponent}]
|
||||
return
|
||||
}
|
||||
|
||||
type suffixHandler struct {
|
||||
decSuffixes listSuffixer
|
||||
binSuffixes listSuffixer
|
||||
}
|
||||
|
||||
type fastLookup struct {
|
||||
*suffixHandler
|
||||
}
|
||||
|
||||
func (l fastLookup) interpret(s suffix) (base, exponent int32, format Format, ok bool) {
|
||||
switch s {
|
||||
case "":
|
||||
return 10, 0, DecimalSI, true
|
||||
case "n":
|
||||
return 10, -9, DecimalSI, true
|
||||
case "u":
|
||||
return 10, -6, DecimalSI, true
|
||||
case "m":
|
||||
return 10, -3, DecimalSI, true
|
||||
case "k":
|
||||
return 10, 3, DecimalSI, true
|
||||
case "M":
|
||||
return 10, 6, DecimalSI, true
|
||||
case "G":
|
||||
return 10, 9, DecimalSI, true
|
||||
}
|
||||
return l.suffixHandler.interpret(s)
|
||||
}
|
||||
|
||||
func newSuffixer() suffixer {
|
||||
sh := &suffixHandler{}
|
||||
|
||||
// IMPORTANT: if you change this section you must change fastLookup
|
||||
|
||||
sh.binSuffixes.addSuffix("Ki", bePair{2, 10})
|
||||
sh.binSuffixes.addSuffix("Mi", bePair{2, 20})
|
||||
sh.binSuffixes.addSuffix("Gi", bePair{2, 30})
|
||||
sh.binSuffixes.addSuffix("Ti", bePair{2, 40})
|
||||
sh.binSuffixes.addSuffix("Pi", bePair{2, 50})
|
||||
sh.binSuffixes.addSuffix("Ei", bePair{2, 60})
|
||||
// Don't emit an error when trying to produce
|
||||
// a suffix for 2^0.
|
||||
sh.decSuffixes.addSuffix("", bePair{2, 0})
|
||||
|
||||
sh.decSuffixes.addSuffix("n", bePair{10, -9})
|
||||
sh.decSuffixes.addSuffix("u", bePair{10, -6})
|
||||
sh.decSuffixes.addSuffix("m", bePair{10, -3})
|
||||
sh.decSuffixes.addSuffix("", bePair{10, 0})
|
||||
sh.decSuffixes.addSuffix("k", bePair{10, 3})
|
||||
sh.decSuffixes.addSuffix("M", bePair{10, 6})
|
||||
sh.decSuffixes.addSuffix("G", bePair{10, 9})
|
||||
sh.decSuffixes.addSuffix("T", bePair{10, 12})
|
||||
sh.decSuffixes.addSuffix("P", bePair{10, 15})
|
||||
sh.decSuffixes.addSuffix("E", bePair{10, 18})
|
||||
|
||||
return fastLookup{sh}
|
||||
}
|
||||
|
||||
func (sh *suffixHandler) construct(base, exponent int32, fmt Format) (s suffix, ok bool) {
|
||||
switch fmt {
|
||||
case DecimalSI:
|
||||
return sh.decSuffixes.construct(base, exponent)
|
||||
case BinarySI:
|
||||
return sh.binSuffixes.construct(base, exponent)
|
||||
case DecimalExponent:
|
||||
if base != 10 {
|
||||
return "", false
|
||||
}
|
||||
if exponent == 0 {
|
||||
return "", true
|
||||
}
|
||||
return suffix("e" + strconv.FormatInt(int64(exponent), 10)), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func (sh *suffixHandler) constructBytes(base, exponent int32, format Format) (s []byte, ok bool) {
|
||||
switch format {
|
||||
case DecimalSI:
|
||||
return sh.decSuffixes.constructBytes(base, exponent)
|
||||
case BinarySI:
|
||||
return sh.binSuffixes.constructBytes(base, exponent)
|
||||
case DecimalExponent:
|
||||
if base != 10 {
|
||||
return nil, false
|
||||
}
|
||||
if exponent == 0 {
|
||||
return nil, true
|
||||
}
|
||||
result := make([]byte, 8, 8)
|
||||
result[0] = 'e'
|
||||
number := strconv.AppendInt(result[1:1], int64(exponent), 10)
|
||||
if &result[1] == &number[0] {
|
||||
return result[:1+len(number)], true
|
||||
}
|
||||
result = append(result[:1], number...)
|
||||
return result, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (sh *suffixHandler) interpret(suffix suffix) (base, exponent int32, fmt Format, ok bool) {
|
||||
// Try lookup tables first
|
||||
if b, e, ok := sh.decSuffixes.lookup(suffix); ok {
|
||||
return b, e, DecimalSI, true
|
||||
}
|
||||
if b, e, ok := sh.binSuffixes.lookup(suffix); ok {
|
||||
return b, e, BinarySI, true
|
||||
}
|
||||
|
||||
if len(suffix) > 1 && (suffix[0] == 'E' || suffix[0] == 'e') {
|
||||
parsed, err := strconv.ParseInt(string(suffix[1:]), 10, 64)
|
||||
if err != nil {
|
||||
return 0, 0, DecimalExponent, false
|
||||
}
|
||||
return 10, int32(parsed), DecimalExponent, true
|
||||
}
|
||||
|
||||
return 0, 0, DecimalExponent, false
|
||||
}
|
18
vendor/github.com/appc/spec/schema/types/user_annotations.go
generated
vendored
Normal file
18
vendor/github.com/appc/spec/schema/types/user_annotations.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2016 The appc Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package types
|
||||
|
||||
// UserAnnotations are arbitrary key-value pairs, to be supplied and interpreted by the user
|
||||
type UserAnnotations map[string]string
|
18
vendor/github.com/appc/spec/schema/types/user_labels.go
generated
vendored
Normal file
18
vendor/github.com/appc/spec/schema/types/user_labels.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2015 The appc Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package types
|
||||
|
||||
// UserLabels are arbitrary key-value pairs, to be supplied and interpreted by the user
|
||||
type UserLabels map[string]string
|
29
vendor/github.com/appc/spec/schema/types/volume.go
generated
vendored
29
vendor/github.com/appc/spec/schema/types/volume.go
generated
vendored
@ -40,8 +40,9 @@ type Volume struct {
|
||||
|
||||
// currently used only by "host"
|
||||
// TODO(jonboulle): factor out?
|
||||
Source string `json:"source,omitempty"`
|
||||
ReadOnly *bool `json:"readOnly,omitempty"`
|
||||
Source string `json:"source,omitempty"`
|
||||
ReadOnly *bool `json:"readOnly,omitempty"`
|
||||
Recursive *bool `json:"recursive,omitempty"`
|
||||
|
||||
// currently used only by "empty"
|
||||
Mode *string `json:"mode,omitempty"`
|
||||
@ -128,6 +129,10 @@ func (v Volume) String() string {
|
||||
s = append(s, ",readOnly=")
|
||||
s = append(s, strconv.FormatBool(*v.ReadOnly))
|
||||
}
|
||||
if v.Recursive != nil {
|
||||
s = append(s, ",recursive=")
|
||||
s = append(s, strconv.FormatBool(*v.Recursive))
|
||||
}
|
||||
switch v.Kind {
|
||||
case "empty":
|
||||
if *v.Mode != emptyVolumeDefaultMode {
|
||||
@ -149,10 +154,8 @@ func (v Volume) String() string {
|
||||
// VolumeFromString takes a command line volume parameter and returns a volume
|
||||
//
|
||||
// Example volume parameters:
|
||||
// database,kind=host,source=/tmp,readOnly=true
|
||||
// database,kind=host,source=/tmp,readOnly=true,recursive=true
|
||||
func VolumeFromString(vp string) (*Volume, error) {
|
||||
var vol Volume
|
||||
|
||||
vp = "name=" + vp
|
||||
vpQuery, err := common.MakeQueryString(vp)
|
||||
if err != nil {
|
||||
@ -163,7 +166,12 @@ func VolumeFromString(vp string) (*Volume, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for key, val := range v {
|
||||
return VolumeFromParams(v)
|
||||
}
|
||||
|
||||
func VolumeFromParams(params map[string][]string) (*Volume, error) {
|
||||
var vol Volume
|
||||
for key, val := range params {
|
||||
val := val
|
||||
if len(val) > 1 {
|
||||
return nil, fmt.Errorf("label %s with multiple values %q", key, val)
|
||||
@ -186,6 +194,12 @@ func VolumeFromString(vp string) (*Volume, error) {
|
||||
return nil, err
|
||||
}
|
||||
vol.ReadOnly = &ro
|
||||
case "recursive":
|
||||
rec, err := strconv.ParseBool(val[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vol.Recursive = &rec
|
||||
case "mode":
|
||||
vol.Mode = &val[0]
|
||||
case "uid":
|
||||
@ -207,8 +221,7 @@ func VolumeFromString(vp string) (*Volume, error) {
|
||||
|
||||
maybeSetDefaults(&vol)
|
||||
|
||||
err = vol.assertValid()
|
||||
if err != nil {
|
||||
if err := vol.assertValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
2
vendor/github.com/appc/spec/schema/version.go
generated
vendored
2
vendor/github.com/appc/spec/schema/version.go
generated
vendored
@ -22,7 +22,7 @@ const (
|
||||
// version represents the canonical version of the appc spec and tooling.
|
||||
// For now, the schema and tooling is coupled with the spec itself, so
|
||||
// this must be kept in sync with the VERSION file in the root of the repo.
|
||||
version string = "0.8.1+git"
|
||||
version string = "0.8.9+git"
|
||||
)
|
||||
|
||||
var (
|
||||
|
Loading…
Reference in New Issue
Block a user