Skip to content

Commit

Permalink
feat: add cpu/memory funcs for k8s (#21)
Browse files Browse the repository at this point in the history
* feat: add cpu/memory funcs for k8s

* chore: fix lint + tests

* chore: use lower case for k8s memory conversion

* chore: add tests for lowercase memory
  • Loading branch information
yashmehrotra authored Sep 29, 2023
1 parent 41f8577 commit dd952f5
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 30 deletions.
54 changes: 52 additions & 2 deletions celext/cel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func panIf(err error) {
}
}

func executeTemplate(t *testing.T, i int, input string, output any, environment map[string]any) {
func executeTemplate(t *testing.T, i int, input string, expectedOutput any, environment map[string]any) {
env, err := cel.NewEnv(GetCelEnv(environment)...)
panIf(err)

Expand All @@ -30,7 +30,7 @@ func executeTemplate(t *testing.T, i int, input string, output any, environment
out, _, err := prg.Eval(environment)
panIf(err)

assert.EqualValues(t, output, out.Value(), fmt.Sprintf("Test:%d failed", i+1))
assert.EqualValues(t, expectedOutput, out.Value(), fmt.Sprintf("Test:%d failed", i+1))
}

func TestCelNamespace(t *testing.T) {
Expand Down Expand Up @@ -129,3 +129,53 @@ func TestCelJSON(t *testing.T) {
executeTemplate(t, i, td.Input, td.Output, nil)
}
}

func TestCelK8sCPUResourceUnits(t *testing.T) {
testData := []struct {
Input string
Output any
}{
{Input: `k8s.cpuAsMillicores("10m")`, Output: int64(10)},
{Input: `k8s.cpuAsMillicores("100m")`, Output: int64(100)},
{Input: `k8s.cpuAsMillicores("1000m")`, Output: int64(1000)},
{Input: `k8s.cpuAsMillicores("0.5")`, Output: int64(500)},
{Input: `k8s.cpuAsMillicores("1")`, Output: int64(1000)},
{Input: `k8s.cpuAsMillicores("1.5")`, Output: int64(1500)},
{Input: `k8s.cpuAsMillicores("1.234")`, Output: int64(1234)},
{Input: `k8s.cpuAsMillicores("5")`, Output: int64(5000)},
}

for i, td := range testData {
executeTemplate(t, i, td.Input, td.Output, nil)
}
}

func TestCelK8sMemoryResourceUnits(t *testing.T) {
testData := []struct {
Input string
Output any
}{
{Input: `k8s.memoryAsBytes("10Ki")`, Output: int64(10240)},
{Input: `k8s.memoryAsBytes("100Ki")`, Output: int64(102400)},
{Input: `k8s.memoryAsBytes("1000Ki")`, Output: int64(1024000)},
{Input: `k8s.memoryAsBytes("50Mi")`, Output: int64(52428800)},
{Input: `k8s.memoryAsBytes("500Mi")`, Output: int64(524288000)},
{Input: `k8s.memoryAsBytes("512Mi")`, Output: int64(536870912)},
{Input: `k8s.memoryAsBytes("1Gi")`, Output: int64(1073741824)},
{Input: `k8s.memoryAsBytes("1.234Gi")`, Output: int64(1324997410)},
{Input: `k8s.memoryAsBytes("5Gi")`, Output: int64(5368709120)},
{Input: `k8s.memoryAsBytes("10ki")`, Output: int64(10240)},
{Input: `k8s.memoryAsBytes("100ki")`, Output: int64(102400)},
{Input: `k8s.memoryAsBytes("1000ki")`, Output: int64(1024000)},
{Input: `k8s.memoryAsBytes("50mi")`, Output: int64(52428800)},
{Input: `k8s.memoryAsBytes("500mi")`, Output: int64(524288000)},
{Input: `k8s.memoryAsBytes("512mi")`, Output: int64(536870912)},
{Input: `k8s.memoryAsBytes("1gi")`, Output: int64(1073741824)},
{Input: `k8s.memoryAsBytes("1.234gi")`, Output: int64(1324997410)},
{Input: `k8s.memoryAsBytes("5gi")`, Output: int64(5368709120)},
}

for i, td := range testData {
executeTemplate(t, i, td.Input, td.Output, nil)
}
}
32 changes: 4 additions & 28 deletions celext/celfuncs.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"reflect"

"github.com/flanksource/gomplate/v3/funcs"
"github.com/flanksource/gomplate/v3/k8s"
pkgStrings "github.com/flanksource/gomplate/v3/strings"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
Expand All @@ -14,10 +13,12 @@ import (
)

var customCelFuncs = []cel.EnvOption{
k8sHealth(),
k8sIsHealthy(),
toJSONArray(),
toJSON(),
k8sHealth(),
k8sIsHealthy(),
k8sCPUAsMillicores(),
k8sMemoryAsBytes(),
}

func GetCelEnv(environment map[string]any) []cel.EnvOption {
Expand All @@ -41,31 +42,6 @@ func GetCelEnv(environment map[string]any) []cel.EnvOption {
return opts
}

func k8sHealth() cel.EnvOption {
return cel.Function("k8s.health",
cel.Overload("k8s.health_any",
[]*cel.Type{cel.AnyType},
cel.AnyType,
cel.UnaryBinding(func(obj ref.Val) ref.Val {
jsonObj, _ := anyToMapStringAny(k8s.GetHealth(obj.Value()))
return types.NewDynamicMap(types.DefaultTypeAdapter, jsonObj)
}),
),
)
}

func k8sIsHealthy() cel.EnvOption {
return cel.Function("k8s.is_healthy",
cel.Overload("k8s.is_healthy_any",
[]*cel.Type{cel.AnyType},
cel.StringType,
cel.UnaryBinding(func(obj ref.Val) ref.Val {
return types.Bool(k8s.GetHealth(obj.Value()).OK)
}),
),
)
}

func toJSONArray() cel.EnvOption {
return cel.Function("toJSONArray",
cel.MemberOverload("dyn_toJSONArray_string",
Expand Down
80 changes: 80 additions & 0 deletions celext/kubernetes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package celext

import (
"strings"

"github.com/flanksource/gomplate/v3/conv"
"github.com/flanksource/gomplate/v3/k8s"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)

func k8sHealth() cel.EnvOption {
return cel.Function("k8s.health",
cel.Overload("k8s.health_any",
[]*cel.Type{cel.AnyType},
cel.AnyType,
cel.UnaryBinding(func(obj ref.Val) ref.Val {
jsonObj, _ := anyToMapStringAny(k8s.GetHealth(obj.Value()))
return types.NewDynamicMap(types.DefaultTypeAdapter, jsonObj)
}),
),
)
}

func k8sIsHealthy() cel.EnvOption {
return cel.Function("k8s.is_healthy",
cel.Overload("k8s.is_healthy_any",
[]*cel.Type{cel.AnyType},
cel.StringType,
cel.UnaryBinding(func(obj ref.Val) ref.Val {
return types.Bool(k8s.GetHealth(obj.Value()).OK)
}),
),
)
}

func k8sCPUAsMillicores() cel.EnvOption {
return cel.Function("k8s.cpuAsMillicores",
cel.Overload("k8s.cpuAsMillicores_string",
[]*cel.Type{cel.StringType},
cel.IntType,
cel.UnaryBinding(func(obj ref.Val) ref.Val {
objVal := conv.ToString(obj.Value())
var cpu int64
if strings.HasSuffix(objVal, "m") {
cpu = conv.ToInt64(strings.ReplaceAll(objVal, "m", ""))
} else {
cpu = int64(conv.ToFloat64(objVal) * 1000)
}
return types.Int(cpu)
}),
),
)
}

func k8sMemoryAsBytes() cel.EnvOption {
return cel.Function("k8s.memoryAsBytes",
cel.Overload("k8s.memoryAsBytes_string",
[]*cel.Type{cel.StringType},
cel.IntType,
cel.UnaryBinding(func(obj ref.Val) ref.Val {
objVal := strings.ToLower(conv.ToString(obj.Value()))
var memory int64
switch {
case strings.HasSuffix(objVal, "gi"):
memory = int64(conv.ToFloat64(strings.ReplaceAll(objVal, "gi", "")) * 1024 * 1024 * 1024)
case strings.HasSuffix(objVal, "mi"):
memory = int64(conv.ToFloat64(strings.ReplaceAll(objVal, "mi", "")) * 1024 * 1024)
case strings.HasSuffix(objVal, "ki"):
memory = int64(conv.ToFloat64(strings.ReplaceAll(objVal, "ki", "")) * 1024)
default:
memory = conv.ToInt64(objVal)
}

return types.Int(memory)
}),
),
)
}

0 comments on commit dd952f5

Please sign in to comment.