Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cel.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func GetCelEnv(environment map[string]any) []cel.EnvOption {
opts = append(opts, kubernetes.Library()...)
opts = append(opts, ext.Strings(), ext.Encoders(), ext.Lists(), ext.Math(), ext.Sets())
opts = append(opts, cel.StdLib())
opts = append(opts, cel.OptionalTypes())
opts = append(opts, cel.OptionalTypes(cel.OptionalTypesVersion(1)))
opts = append(opts, nilsafe.Library(nilsafe.WithZeroValues()))
opts = append(opts, strings.Library...)
opts = append(opts, typeAdapters...)
Expand Down
127 changes: 63 additions & 64 deletions funcs/coll.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,70 +219,6 @@ func (CollFuncs) Last(in interface{}) interface{} {
return coll.Last(in)
}

// celFirstLast extracts the first or last element from a CEL list, string, or map.
func celFirstLast(arg ref.Val, first bool) ref.Val {
if arg == types.NullValue {
return types.NullValue
}
if s, ok := arg.(types.String); ok {
runes := []rune(string(s))
if len(runes) == 0 {
return types.String("")
}
if first {
return types.String(string(runes[0]))
}
return types.String(string(runes[len(runes)-1]))
}
if m, ok := arg.(traits.Mapper); ok {
if m.Size() == types.IntZero {
return types.NullValue
}
keys, err := m.ConvertToNative(typeMapStringAny)
if err != nil {
return types.WrapErr(err)
}
native, ok := keys.(map[string]any)
if !ok {
return types.NullValue
}
result := coll.First(native)
if !first {
result = coll.Last(native)
}
return types.DefaultTypeAdapter.NativeToValue(result)
}
if l, ok := arg.(traits.Lister); ok {
sz := l.Size()
if sz == types.IntZero {
return types.NullValue
}
if first {
return l.Get(types.IntZero)
}
return l.Get(sz.(types.Int) - 1)
}
return types.NullValue
}

var celFirst = cel.Function("first",
cel.Overload("first_dyn", []*cel.Type{cel.DynType}, cel.DynType,
cel.UnaryBinding(func(arg ref.Val) ref.Val { return celFirstLast(arg, true) }),
),
cel.MemberOverload("dyn_first", []*cel.Type{cel.DynType}, cel.DynType,
cel.UnaryBinding(func(arg ref.Val) ref.Val { return celFirstLast(arg, true) }),
),
)

var celLast = cel.Function("last",
cel.Overload("last_dyn", []*cel.Type{cel.DynType}, cel.DynType,
cel.UnaryBinding(func(arg ref.Val) ref.Val { return celFirstLast(arg, false) }),
),
cel.MemberOverload("dyn_last", []*cel.Type{cel.DynType}, cel.DynType,
cel.UnaryBinding(func(arg ref.Val) ref.Val { return celFirstLast(arg, false) }),
),
)

// celCoalesceFirst returns the first non-null, non-empty ref.Val from args,
// unwrapping CEL optional<T> values along the way.
// optional.none() and plain null are skipped; optional.of(v) is unwrapped and
Expand Down Expand Up @@ -330,6 +266,69 @@ var celCoalesce = cel.Function("coalesce",
),
)

func celFirstLastImpl(arg ref.Val, first bool) ref.Val {
if opt, ok := arg.(*types.Optional); ok {
if !opt.HasValue() {
return types.NullValue
}
arg = opt.GetValue()
}
if arg == types.NullValue {
return types.NullValue
}

if l, ok := arg.(traits.Lister); ok {
if l.Size() == types.IntZero {
return types.NullValue
}
if first {
return l.Get(types.IntZero)
}
return l.Get(l.Size().(types.Int) - 1)
}

if _, ok := arg.(traits.Mapper); ok {
m, err := convertMap(arg)
if err != nil {
return types.WrapErr(err)
}
var result any
if first {
result = coll.First(m)
} else {
result = coll.Last(m)
}
if result == nil {
return types.NullValue
}
return types.DefaultTypeAdapter.NativeToValue(result)
}

return types.NullValue
}

var celFirst = cel.Function("first",
cel.Overload("first_dyn", []*cel.Type{cel.DynType}, cel.DynType,
cel.OverloadIsNonStrict(),
cel.UnaryBinding(func(arg ref.Val) ref.Val { return celFirstLastImpl(arg, true) }),
),
cel.MemberOverload("dyn_first", []*cel.Type{cel.DynType}, cel.DynType,
cel.OverloadIsNonStrict(),
cel.UnaryBinding(func(arg ref.Val) ref.Val { return celFirstLastImpl(arg, true) }),
),
)

var celLast = cel.Function("last",
cel.Overload("last_dyn", []*cel.Type{cel.DynType}, cel.DynType,
cel.OverloadIsNonStrict(),
cel.UnaryBinding(func(arg ref.Val) ref.Val { return celFirstLastImpl(arg, false) }),
),
cel.MemberOverload("dyn_last", []*cel.Type{cel.DynType}, cel.DynType,
cel.OverloadIsNonStrict(),
cel.UnaryBinding(func(arg ref.Val) ref.Val { return celFirstLastImpl(arg, false) }),
),
)

var celLabelsMatch = cel.Function("matchLabel",
cel.Overload("matchLabel_map_string_string",
[]*cel.Type{
Expand Down
131 changes: 66 additions & 65 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,116 +5,117 @@ go 1.25.1
require (
github.com/Masterminds/goutils v1.1.1
github.com/Masterminds/semver/v3 v3.4.0
github.com/flanksource/commons v1.44.0
github.com/flanksource/is-healthy v1.0.59
github.com/flanksource/commons v1.46.1
github.com/flanksource/is-healthy v1.0.82
github.com/flanksource/kubectl-neat v1.0.4
github.com/google/cel-go v0.22.1
github.com/google/cel-go v0.27.0
github.com/google/go-cmp v0.7.0
github.com/google/uuid v1.6.0
github.com/gosimple/slug v1.13.1
github.com/gosimple/slug v1.15.0
github.com/hairyhenderson/toml v0.4.2-0.20210923231440-40456b8e66cf
github.com/itchyny/gojq v0.12.17
github.com/itchyny/gojq v0.12.18
github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24
github.com/mitchellh/reflectwalk v1.0.2
github.com/ohler55/ojg v1.25.0
github.com/onsi/ginkgo/v2 v2.20.1
github.com/onsi/gomega v1.34.1
github.com/ohler55/ojg v1.28.0
github.com/onsi/ginkgo/v2 v2.27.2
github.com/onsi/gomega v1.38.2
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
github.com/robertkrimen/otto v0.2.1
github.com/samber/oops v1.17.0
github.com/stretchr/testify v1.10.0
github.com/ugorji/go/codec v1.2.12
golang.org/x/text v0.23.0
golang.org/x/tools v0.28.0
google.golang.org/protobuf v1.34.2
github.com/robertkrimen/otto v0.5.1
github.com/samber/oops v1.21.0
github.com/stretchr/testify v1.11.1
github.com/ugorji/go/codec v1.3.1
golang.org/x/text v0.34.0
golang.org/x/tools v0.42.0
google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v3 v3.0.1
gotest.tools/v3 v3.5.1
k8s.io/api v0.31.1
k8s.io/apimachinery v0.31.1
sigs.k8s.io/yaml v1.4.0
k8s.io/api v0.35.1
k8s.io/apimachinery v0.35.1
sigs.k8s.io/yaml v1.6.0
)

// TODO: replace with gopkg.in/yaml.v3 after https://github.com/go-yaml/yaml/pull/862
// is merged
require github.com/hairyhenderson/yaml v0.0.0-20220618171115-2d35fca545ce

require (
cel.dev/expr v0.18.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
cel.dev/expr v0.25.1 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bsm/gomega v1.27.10 // indirect
github.com/cert-manager/cert-manager v1.16.1 // indirect
github.com/cert-manager/cert-manager v1.19.4 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/emirpasic/gods/v2 v2.0.0-alpha // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/goccy/go-yaml v1.16.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
github.com/gosimple/unidecode v1.0.1 // indirect
github.com/henvic/httpretty v0.1.4 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/itchyny/timefmt-go v0.1.6 // indirect
github.com/jeremywohl/flatten v0.0.0-20180923035001-588fe0d4c603 // indirect
github.com/itchyny/timefmt-go v0.1.7 // indirect
github.com/jeremywohl/flatten v1.0.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lmittmann/tint v1.0.5 // indirect
github.com/lmittmann/tint v1.1.3 // indirect
github.com/lrita/cmap v0.0.0-20231108122212-cb084a67f554 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/oklog/ulid/v2 v2.1.0 // indirect
github.com/oklog/ulid/v2 v2.1.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/samber/lo v1.49.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/cobra v1.8.1 // indirect
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.67.5 // indirect
github.com/prometheus/procfs v0.20.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/samber/lo v1.52.0 // indirect
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/spf13/cobra v1.10.2 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/tidwall/gjson v1.14.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.2.0 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.0.4 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/yuin/gopher-lua v1.1.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.32.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
github.com/yuin/gopher-lua v1.1.1 // indirect
go.opentelemetry.io/otel v1.40.0 // indirect
go.opentelemetry.io/otel/trace v1.40.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect
golang.org/x/mod v0.33.0 // indirect
golang.org/x/net v0.51.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.41.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260223185530-2f722ef697dc // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260223185530-2f722ef697dc // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/sourcemap.v1 v1.0.5 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/apiextensions-apiserver v0.31.1 // indirect
k8s.io/client-go v0.31.1 // indirect
k8s.io/apiextensions-apiserver v0.35.1 // indirect
k8s.io/client-go v0.35.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 // indirect
k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 // indirect
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect
layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf // indirect
sigs.k8s.io/gateway-api v1.1.0 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/gateway-api v1.4.1 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect
)

// replace github.com/flanksource/commons => ../commons
Loading
Loading