Skip to content

Commit

Permalink
SDL units: suffixes, fractional CPU
Browse files Browse the repository at this point in the history
fixes #267
fixes #227
  • Loading branch information
boz committed Jul 9, 2018
1 parent bae6a77 commit 766ffc7
Show file tree
Hide file tree
Showing 13 changed files with 322 additions and 51 deletions.
12 changes: 6 additions & 6 deletions _docs/deployment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,16 @@ profiles:
compute:
web:
cpu: 2
memory: 3
disk: 5
memory: "128Mi"
disk: "5Gi"
db:
cpu: 10
memory: 10
disk: 20
memory: "10Gi"
disk: "20Gi"
db-pool:
cpu: 5
memory: 10
disk: 5
memory: "3Gi"
disk: "5Gi"

placement:
westcoast:
Expand Down
36 changes: 33 additions & 3 deletions _docs/sdl.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,44 @@ uses uses the profile.

Example:

This defines a profile named `web` having resource requirements of 2 vCPUs, 2 gigabytes of memory, and 5 gigabytes of disk space available.


```yaml
web:
cpu: 2
memory: 3GB
disk: 5GB
memory: "2Gi"
disk: "5Gi"
```
This defines a profile named `web` having resource requirements of 2 vCPUs, 2 gigabytes of memory, and 5 gigabytes of disk space available.
`cpu` units represent a vCPU share and can be fractional. When no suffix is present the value represents
a fraction of a whole CPU share. With a `m` suffix, the value represnts the number of milli-CPU shares (1/1000 of a CPU share).

Example:

| Value | CPU-Share |
| --- | --- |
| `1` | 1 |
| `0.5` | 1/2 |
| `"100m"` | 1/10 |
| `"50m"` | 1/20 |

`memory`, `disk` units are described in bytes. The following suffixes are allowed for simplification:

| Suffix | Value |
| --- | --- |
| `k` | 1000 |
| `Ki` | 1024 |
| `M` | 1000^2 |
| `Mi` | 1024^2 |
| `G` | 1000^3 |
| `Gi` | 1024^3 |
| `T` | 1000^4 |
| `Ti` | 1024^4 |
| `P` | 1000^5 |
| `Pi` | 1024^5 |
| `E` | 1000^6 |
| `Ei` | 1024^6 |

#### profiles.placement

Expand Down
6 changes: 3 additions & 3 deletions _run/multi/deployment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ services:
profiles:
compute:
web:
cpu: 1
memory: 512
disk: 5
cpu: "0.25"
memory: "128Mi"
disk: "5Gi"
placement:
westcoast:
attributes:
Expand Down
4 changes: 2 additions & 2 deletions provider/cluster/kube/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ func (b *deploymentBuilder) update(obj *appsv1.Deployment) (*appsv1.Deployment,

func (b *deploymentBuilder) container() corev1.Container {

qcpu := resource.NewQuantity(int64(b.service.Unit.CPU), resource.DecimalSI)
qmem := resource.NewScaledQuantity(int64(b.service.Unit.Memory), resource.Mega)
qcpu := resource.NewScaledQuantity(int64(b.service.Unit.CPU), resource.Milli)
qmem := resource.NewQuantity(int64(b.service.Unit.Memory), resource.DecimalSI)

kcontainer := corev1.Container{
Name: b.service.Name,
Expand Down
4 changes: 2 additions & 2 deletions sdl/_testdata/simple.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ profiles:
compute:
web:
cpu: 2
memory: 3
disk: 5
memory: "128Mi"
disk: "5Gi"

placement:
westcoast:
Expand Down
143 changes: 143 additions & 0 deletions sdl/units.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package sdl

import (
"fmt"
"strconv"
"strings"
)

var (
errNegativeValue = fmt.Errorf("invalid: negative value not allowed")
)

const (
unitk = 1000
unitKi = 1024

unitM = unitk * unitk
unitMi = unitKi * unitKi

unitG = unitM * unitk
unitGi = unitMi * unitKi

unitT = unitG * unitk
unitTi = unitGi * unitKi

unitP = unitT * unitk
unitPi = unitTi * unitKi

unitE = unitP * unitk
unitEi = unitPi * unitKi
)

var suffixes = []struct {
symbol string
unit uint64
}{
{"k", unitk},
{"Ki", unitKi},

{"M", unitM},
{"Mi", unitMi},

{"G", unitG},
{"Gi", unitGi},

{"T", unitT},
{"Ti", unitTi},

{"P", unitP},
{"Pi", unitPi},

{"E", unitE},
{"Ei", unitEi},
}

// CPU shares. One CPUQuantity = 1/1000 of a CPU
type cpuQuantity uint32

func (u *cpuQuantity) UnmarshalYAML(unmarshal func(interface{}) error) error {

var sval string
if err := unmarshal(&sval); err != nil {
return err
}

if strings.HasSuffix(sval, "m") {
sval = strings.TrimSuffix(sval, "m")
val, err := strconv.ParseUint(sval, 10, 32)
if err != nil {
return err
}
if val < 0 {
return errNegativeValue
}
*u = cpuQuantity(val)
return nil
}

val, err := strconv.ParseFloat(sval, 64)
if err != nil {
return err
}

val *= 1000

if val < 0 {
return errNegativeValue
}

*u = cpuQuantity(val)

return nil
}

// Memory,Disk size in bytes.
type byteQuantity uint64

func (u *byteQuantity) UnmarshalYAML(unmarshal func(interface{}) error) error {
var sval string
if err := unmarshal(&sval); err != nil {
return err
}
val, err := parseWithSuffix(sval)
if err != nil {
return err
}
*u = byteQuantity(val)
return nil
}

func parseWithSuffix(sval string) (uint64, error) {
for _, suffix := range suffixes {
if !strings.HasSuffix(sval, suffix.symbol) {
continue
}

sval := strings.TrimSuffix(sval, suffix.symbol)

val, err := strconv.ParseFloat(sval, 64)
if err != nil {
return 0, err
}

val *= float64(suffix.unit)

if val < 0 {
return 0, errNegativeValue
}

return uint64(val), nil
}

val, err := strconv.ParseFloat(sval, 64)
if err != nil {
return 0, err
}

if val < 0 {
return 0, errNegativeValue
}

return uint64(val), nil
}
97 changes: 97 additions & 0 deletions sdl/units_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package sdl

import (
"testing"

"github.com/stretchr/testify/assert"
yaml "gopkg.in/yaml.v2"
)

func TestCPUQuantity(t *testing.T) {

type vtype struct {
Val cpuQuantity `yaml:"val"`
}

tests := []struct {
text string
value uint32
err bool
}{
{`val: 1`, 1000, false},
{`val: -1`, 1000, true},

{`val: 0.5`, 500, false},
{`val: -0.5`, 500, true},

{`val: "100m"`, 100, false},
{`val: "-100m"`, 100, true},

{`val: ""`, 0, true},
}

for idx, test := range tests {
buf := []byte(test.text)
obj := &vtype{}

err := yaml.UnmarshalStrict(buf, obj)

if test.err {
assert.Error(t, err, "idx:%v text:`%v`", idx, test.text)
continue
}

if !assert.NoError(t, err, "idx:%v text:`%v`", idx, test.text) {
continue
}

assert.Equal(t, cpuQuantity(test.value), obj.Val, "idx:%v text:`%v`", idx, test.text)
}
}

func TestByteQuantity(t *testing.T) {
type vtype struct {
Val byteQuantity `yaml:"val"`
}

tests := []struct {
text string
value uint64
err bool
}{
{`val: 1`, 1, false},
{`val: -1`, 1, true},

{`val: "1M"`, unitM, false},
{`val: "-1M"`, 0, true},

{`val: "0.5M"`, unitM / 2, false},
{`val: "-0.5M"`, 0, true},

{`val: "3M"`, 3 * unitM, false},
{`val: "3G"`, 3 * unitG, false},
{`val: "3T"`, 3 * unitT, false},
{`val: "3P"`, 3 * unitP, false},
{`val: "3E"`, 3 * unitE, false},

{`val: ""`, 0, true},
}

for idx, test := range tests {
buf := []byte(test.text)
obj := &vtype{}

err := yaml.UnmarshalStrict(buf, obj)

if test.err {
assert.Error(t, err, "idx:%v text:`%v`", idx, test.text)
continue
}

if !assert.NoError(t, err, "idx:%v text:`%v`", idx, test.text) {
continue
}

assert.Equal(t, byteQuantity(test.value), obj.Val, "idx:%v text:`%v`", idx, test.text)
}
}
18 changes: 9 additions & 9 deletions sdl/v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ type v1Profiles struct {
}

type v1ComputeProfile struct {
CPU uint32 `yaml:"cpu"`
Memory uint32
Disk uint64
CPU cpuQuantity `yaml:"cpu"`
Memory byteQuantity
Disk byteQuantity
}

type v1PlacementProfile struct {
Expand Down Expand Up @@ -119,9 +119,9 @@ func (sdl *v1) DeploymentGroups() ([]*types.GroupSpec, error) {

group.Resources = append(group.Resources, types.ResourceGroup{
Unit: types.ResourceUnit{
CPU: compute.CPU,
Memory: compute.Memory,
Disk: compute.Disk,
CPU: uint32(compute.CPU),
Memory: uint64(compute.Memory),
Disk: uint64(compute.Disk),
},
Price: price,
Count: svcdepl.Count,
Expand Down Expand Up @@ -178,9 +178,9 @@ func (sdl *v1) Manifest() (*types.Manifest, error) {
Args: svc.Args,
Env: svc.Env,
Unit: &types.ResourceUnit{
CPU: compute.CPU,
Memory: compute.Memory,
Disk: compute.Disk,
CPU: uint32(compute.CPU),
Memory: uint64(compute.Memory),
Disk: uint64(compute.Disk),
},
Count: svcdepl.Count,
}
Expand Down
Loading

0 comments on commit 766ffc7

Please sign in to comment.