| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424 |
- package external
- import (
- "fmt"
- "testing"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- )
- // newSource returns a ConfigMapSource for the given config.
- func newSource(cfg *Config) *ConfigMapSource {
- return &ConfigMapSource{cfg: cfg}
- }
- func TestConfigMapSource_NilConfig(t *testing.T) {
- s := newSource(nil)
- res, err := s.ExtractNodeLabels(map[string]string{})
- require.Error(t, err)
- assert.Nil(t, res)
- assert.EqualError(t, err, "nil config")
- }
- // --- traditional ConfigMap (Key and Route both empty) ---
- func TestConfigMapSource_Traditional_PassesDataDirectly(t *testing.T) {
- nlCfg := NewNodeLabelConfig("my-cm", "", "", "")
- cfg := NewConfig(nlCfg)
- s := newSource(cfg)
- res, err := s.ExtractNodeLabels(map[string]string{"env": "prod", "region": "us-east-1"})
- require.NoError(t, err)
- assert.Equal(t, map[string]string{"env": "prod", "region": "us-east-1"}, res)
- }
- func TestConfigMapSource_Traditional_EmptyData(t *testing.T) {
- nlCfg := NewNodeLabelConfig("my-cm", "", "", "")
- cfg := NewConfig(nlCfg)
- s := newSource(cfg)
- res, err := s.ExtractNodeLabels(map[string]string{})
- require.NoError(t, err)
- assert.Empty(t, res)
- }
- // --- block-scalar ConfigMap ---
- func TestConfigMapSource_BlockScalar_EmptyLabelsMap(t *testing.T) {
- nlCfg := NewNodeLabelConfig("my-cm", "", "config.yaml", "labels")
- cfg := NewConfig(nlCfg)
- s := newSource(cfg)
- yamlData := `
- labels: {}
- `
- res, err := s.ExtractNodeLabels(map[string]string{
- "config.yaml": yamlData,
- })
- require.NoError(t, err)
- assert.Empty(t, res)
- assert.NotNil(t, res)
- }
- const prometheusConfig = `
- prometheusK8s:
- externalLabels:
- cluster: prod-cluster
- region: eu-west-1
- `
- func TestConfigMapSource_BlockScalar_SingleLevel(t *testing.T) {
- nlCfg := NewNodeLabelConfig("prometheus-cm", "", "config.yaml", "prometheusK8s.externalLabels")
- cfg := NewConfig(nlCfg)
- s := newSource(cfg)
- res, err := s.ExtractNodeLabels(map[string]string{"config.yaml": prometheusConfig})
- require.NoError(t, err)
- assert.Equal(t, map[string]string{
- "cluster": "prod-cluster",
- "region": "eu-west-1",
- }, res)
- }
- func TestConfigMapSource_BlockScalar_TopLevelRoute(t *testing.T) {
- nlCfg := NewNodeLabelConfig("my-cm", "", "data", "labels")
- cfg := NewConfig(nlCfg)
- s := newSource(cfg)
- yaml := `
- labels:
- team: platform
- env: staging
- `
- res, err := s.ExtractNodeLabels(map[string]string{"data": yaml})
- require.NoError(t, err)
- assert.Equal(t, map[string]string{"team": "platform", "env": "staging"}, res)
- }
- // --- error cases ---
- func TestConfigMapSource_BlockScalar_EmptyMapDataInConfigYaml(t *testing.T) {
- nlCfg := NewNodeLabelConfig("my-cm", "", "config.yaml", "labels")
- cfg := NewConfig(nlCfg)
- s := newSource(cfg)
- res, err := s.ExtractNodeLabels(map[string]string{
- "config.yaml": "",
- })
- require.Error(t, err)
- assert.Nil(t, res)
- assert.Contains(t, err.Error(), "failed to locate route")
- }
- func TestConfigMapSource_BlockScalar_MissingKey(t *testing.T) {
- nlCfg := NewNodeLabelConfig("my-cm", "", "config.yaml", "externalLabels")
- cfg := NewConfig(nlCfg)
- s := newSource(cfg)
- _, err := s.ExtractNodeLabels(map[string]string{"other-key": "value"})
- require.Error(t, err)
- assert.Contains(t, err.Error(), `key "config.yaml" not found`)
- }
- func TestConfigMapSource_BlockScalar_InvalidYAML(t *testing.T) {
- nlCfg := NewNodeLabelConfig("my-cm", "", "config.yaml", "labels")
- cfg := NewConfig(nlCfg)
- s := newSource(cfg)
- _, err := s.ExtractNodeLabels(map[string]string{"config.yaml": ":\tinvalid: yaml: {"})
- require.Error(t, err)
- assert.Contains(t, err.Error(), "found character that cannot start any token")
- }
- func TestConfigMapSource_BlockScalar_RouteSegmentNotFound(t *testing.T) {
- nlCfg := NewNodeLabelConfig("my-cm", "", "config.yaml", "does.not.exist")
- cfg := NewConfig(nlCfg)
- s := newSource(cfg)
- _, err := s.ExtractNodeLabels(map[string]string{"config.yaml": "foo: bar\n"})
- require.Error(t, err)
- assert.Contains(t, err.Error(), `failed to locate route`)
- }
- func TestConfigMapSource_BlockScalar_RouteSegmentNotANodeSequenceType(t *testing.T) {
- nlCfg := NewNodeLabelConfig("my-cm", "", "config.yaml", "labels.nested")
- cfg := NewConfig(nlCfg)
- s := newSource(cfg)
- // labels is a string, not a map — traversing into it should fail
- _, err := s.ExtractNodeLabels(map[string]string{"config.yaml": "labels: just-a-string\n"})
- require.Error(t, err)
- assert.Contains(t, err.Error(), "is not a map")
- }
- func TestConfigMapSource_BlockScalar_RoutePointsToNodeScalarType(t *testing.T) {
- nlCfg := NewNodeLabelConfig("my-cm", "", "config.yaml", "labels")
- cfg := NewConfig(nlCfg)
- s := newSource(cfg)
- // labels is a string, not a map — final conversion should fail
- _, err := s.ExtractNodeLabels(map[string]string{"config.yaml": "labels: just-a-string\n"})
- require.Error(t, err)
- assert.Contains(t, err.Error(), "is not a map")
- }
- func TestConfigMapSource_BlockScalar_BoolValuesConvertedToString(t *testing.T) {
- nlCfg := NewNodeLabelConfig("my-cm", "", "config.yaml", "labels")
- cfg := NewConfig(nlCfg)
- s := newSource(cfg)
- yaml := `
- labels:
- active: true
- deprecated: false
- `
- res, err := s.ExtractNodeLabels(map[string]string{"config.yaml": yaml})
- require.NoError(t, err)
- assert.Equal(t, "true", res["active"])
- assert.Equal(t, "false", res["deprecated"])
- }
- func TestConfigMapSource_BlockScalar_IntValuesConvertedToString(t *testing.T) {
- nlCfg := NewNodeLabelConfig("my-cm", "", "config.yaml", "labels")
- cfg := NewConfig(nlCfg)
- s := newSource(cfg)
- yaml := `
- labels:
- priority: 42
- replicas: 3
- `
- res, err := s.ExtractNodeLabels(map[string]string{"config.yaml": yaml})
- require.NoError(t, err)
- assert.Equal(t, "42", res["priority"])
- assert.Equal(t, "3", res["replicas"])
- }
- func TestConfigMapSource_BlockScalar_SequenceValueRejected(t *testing.T) {
- nlCfg := NewNodeLabelConfig("my-cm", "", "config.yaml", "labels")
- cfg := NewConfig(nlCfg)
- s := newSource(cfg)
- yamlData := `
- labels:
- environments:
- - dev
- - staging
- - prod
- `
- res, err := s.ExtractNodeLabels(map[string]string{
- "config.yaml": yamlData,
- })
- require.Error(t, err)
- assert.Nil(t, res)
- assert.Contains(t, err.Error(), `cannot unmarshal !!seq into string`)
- }
- func TestConfigMapSource_BlockScalar_NestedMapValueRejected(t *testing.T) {
- nlCfg := NewNodeLabelConfig("my-cm", "", "config.yaml", "labels")
- cfg := NewConfig(nlCfg)
- s := newSource(cfg)
- yamlData := `
- labels:
- inner-map:
- my-key: value
- other-key: value
- `
- res, err := s.ExtractNodeLabels(map[string]string{
- "config.yaml": yamlData,
- })
- require.Error(t, err)
- assert.Nil(t, res)
- assert.Contains(t, err.Error(), `unmarshal !!map into string`)
- }
- func TestConfigMapSource_BlockScalar_NullValues(t *testing.T) {
- tests := []struct {
- name string
- nullValue string
- }{
- {
- name: "explicit null",
- nullValue: "null",
- },
- {
- name: "tilde",
- nullValue: "~",
- },
- {
- name: "empty value",
- nullValue: "",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- nlCfg := NewNodeLabelConfig("my-cm", "", "config.yaml", "labels")
- cfg := NewConfig(nlCfg)
- s := newSource(cfg)
- yamlData := fmt.Sprintf(`
- labels:
- priority: %s
- `, tt.nullValue)
- res, err := s.ExtractNodeLabels(map[string]string{
- "config.yaml": yamlData,
- })
- require.NoError(t, err)
- assert.Equal(t, "", res["priority"])
- })
- }
- }
- func TestConfigMapSource_BlockScalar_AliasValue(t *testing.T) {
- nlCfg := NewNodeLabelConfig("my-cm", "", "config.yaml", "labels")
- cfg := NewConfig(nlCfg)
- s := newSource(cfg)
- yamlData := `
- defaultEnvironment: &defaultEnvironment production
- labels:
- environment: *defaultEnvironment
- `
- res, err := s.ExtractNodeLabels(map[string]string{
- "config.yaml": yamlData,
- })
- require.NoError(t, err)
- assert.Equal(t, "production", res["environment"])
- }
- func TestConfigMapSource_BlockScalar_ComplexYamlWithTwoUnsupportedNodeType(t *testing.T) {
- nlCfg := NewNodeLabelConfig("my-cm", "", "config.yaml", "labels")
- cfg := NewConfig(nlCfg)
- s := newSource(cfg)
- yaml := `
- labels:
- name: complex
- priority: 42
- replicas: 3
- other:
- - "hello"
- - "this"
- - "is"
- - "a"
- - "test"
- inner-map:
- my-key: "value"
- other-key: "value"
- `
- res, err := s.ExtractNodeLabels(map[string]string{"config.yaml": yaml})
- require.Error(t, err)
- assert.Nil(t, res)
- }
- // --- filterValidLabels ---
- func TestFilterValidLabels_AllValid_ReturnedUnchanged(t *testing.T) {
- in := map[string]string{
- "env": "prod",
- "region": "us-east-1",
- "app.kubernetes.io/name": "opencost",
- "my-key": "my-value",
- "a": "b",
- }
- out := filterValidLabels(in)
- assert.Equal(t, in, out)
- }
- func TestFilterValidLabels_EmptyInput_ReturnsEmptyMap(t *testing.T) {
- out := filterValidLabels(map[string]string{})
- assert.NotNil(t, out)
- assert.Empty(t, out)
- }
- func TestFilterValidLabels_InvalidKey_EntryDropped(t *testing.T) {
- in := map[string]string{
- "valid-key": "value",
- "": "empty-key-dropped",
- "-bad-start": "dropped",
- }
- out := filterValidLabels(in)
- assert.Equal(t, map[string]string{"valid-key": "value"}, out)
- }
- func TestFilterValidLabels_InvalidValue_EntryDropped(t *testing.T) {
- in := map[string]string{
- "good-key": "good-value",
- "bad-value": "-starts-with-dash",
- }
- out := filterValidLabels(in)
- assert.Equal(t, map[string]string{"good-key": "good-value"}, out)
- }
- func TestFilterValidLabels_ValueTooLong_EntryDropped(t *testing.T) {
- longValue := string(make([]byte, 64))
- for i := range longValue {
- longValue = longValue[:i] + "a" + longValue[i+1:]
- }
- in := map[string]string{
- "ok": "fine",
- "toolong": longValue,
- }
- out := filterValidLabels(in)
- assert.Equal(t, map[string]string{"ok": "fine"}, out)
- }
- func TestFilterValidLabels_KeyTooLong_EntryDropped(t *testing.T) {
- longKey := string(make([]byte, 64))
- for i := range longKey {
- longKey = longKey[:i] + "a" + longKey[i+1:]
- }
- in := map[string]string{
- "ok": "fine",
- longKey: "value",
- }
- out := filterValidLabels(in)
- assert.Equal(t, map[string]string{"ok": "fine"}, out)
- }
- func TestFilterValidLabels_PrefixedKey_Valid(t *testing.T) {
- in := map[string]string{
- "app.kubernetes.io/name": "opencost",
- "example.com/env": "staging",
- }
- out := filterValidLabels(in)
- assert.Equal(t, in, out)
- }
- func TestFilterValidLabels_PrefixedKey_InvalidPrefix_EntryDropped(t *testing.T) {
- in := map[string]string{
- "valid": "kept",
- "-bad.prefix/k": "dropped",
- }
- out := filterValidLabels(in)
- assert.Equal(t, map[string]string{"valid": "kept"}, out)
- }
- func TestFilterValidLabels_MixedValidAndInvalid_OnlyValidReturned(t *testing.T) {
- in := map[string]string{
- "cluster": "prod",
- "": "no-key",
- "bad-value": "-oops",
- "region": "eu-west-1",
- }
- out := filterValidLabels(in)
- assert.Equal(t, map[string]string{"cluster": "prod", "region": "eu-west-1"}, out)
- }
|