github.com/cilium/cilium@v1.16.2/pkg/hubble/exporter/config_watcher_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package exporter 5 6 import ( 7 "context" 8 "testing" 9 10 "github.com/stretchr/testify/assert" 11 12 "github.com/cilium/cilium/api/v1/flow" 13 "github.com/cilium/cilium/pkg/time" 14 ) 15 16 func TestYamlConfigFileUnmarshalling(t *testing.T) { 17 // given 18 fileName := "testdata/valid-flowlogs-config.yaml" 19 20 sut := configWatcher{configFilePath: fileName} 21 22 // when 23 config, hash, err := sut.readConfig() 24 assert.NoError(t, err) 25 26 // then 27 assert.Equal(t, 3, len(config.FlowLogs)) 28 29 assert.Equal(t, uint64(0x31b7b661343ab32f), hash) 30 31 expectedDate := time.Date(2023, 10, 9, 23, 59, 59, 0, time.FixedZone("", -7*60*60)) 32 33 expectedConfigs := []FlowLogConfig{ 34 { 35 Name: "test001", 36 FilePath: "/var/log/network/flow-log/pa/test001.log", 37 FieldMask: FieldMask{}, 38 IncludeFilters: FlowFilters{}, 39 ExcludeFilters: FlowFilters{}, 40 End: &expectedDate, 41 }, 42 { 43 Name: "test002", 44 FilePath: "/var/log/network/flow-log/pa/test002.log", 45 FieldMask: FieldMask{"source.namespace", "source.pod_name", "destination.namespace", "destination.pod_name", "verdict"}, 46 IncludeFilters: FlowFilters{ 47 { 48 SourcePod: []string{"default/"}, 49 EventType: []*flow.EventTypeFilter{ 50 {Type: 1}, 51 }, 52 }, 53 { 54 DestinationPod: []string{"frontend/nginx-975996d4c-7hhgt"}, 55 }, 56 }, 57 ExcludeFilters: FlowFilters{}, 58 End: &expectedDate, 59 }, 60 { 61 Name: "test003", 62 FilePath: "/var/log/network/flow-log/pa/test003.log", 63 FieldMask: FieldMask{"source", "destination", "verdict"}, 64 IncludeFilters: FlowFilters{}, 65 ExcludeFilters: FlowFilters{ 66 { 67 DestinationPod: []string{"ingress/"}, 68 }, 69 }, 70 End: nil, 71 }, 72 } 73 74 for i := range expectedConfigs { 75 assertFlowLogConfig(t, expectedConfigs[i], *config.FlowLogs[i]) 76 } 77 } 78 79 func TestEmptyYamlConfigFileUnmarshalling(t *testing.T) { 80 // given 81 fileName := "testdata/empty-flowlogs-config.yaml" 82 83 sut := configWatcher{configFilePath: fileName} 84 85 // when 86 config, hash, err := sut.readConfig() 87 assert.NoError(t, err) 88 89 // then 90 assert.Equal(t, 0, len(config.FlowLogs)) 91 assert.Equal(t, uint64(0x4b2008fd98c1dd4), hash) 92 } 93 94 func TestInvalidConfigFile(t *testing.T) { 95 cases := []struct { 96 name string 97 watcher *configWatcher 98 expectedErrorMsg string 99 }{ 100 { 101 name: "missing file", 102 watcher: &configWatcher{configFilePath: "non-existing-file-name"}, 103 expectedErrorMsg: "cannot read file", 104 }, 105 { 106 name: "invalid yaml", 107 watcher: &configWatcher{configFilePath: "testdata/invalid-flowlogs-config.yaml"}, 108 expectedErrorMsg: "cannot parse yaml", 109 }, 110 { 111 name: "duplicated name", 112 watcher: &configWatcher{configFilePath: "testdata/duplicate-names-flowlogs-config.yaml"}, 113 expectedErrorMsg: "invalid yaml config file: duplicated flowlog name test001", 114 }, 115 { 116 name: "duplicated path", 117 watcher: &configWatcher{configFilePath: "testdata/duplicate-paths-flowlogs-config.yaml"}, 118 expectedErrorMsg: "invalid yaml config file: duplicated flowlog path /var/log/network/flow-log/pa/test001.log", 119 }, 120 } 121 122 for _, tc := range cases { 123 t.Run(tc.name, func(t *testing.T) { 124 config, _, err := tc.watcher.readConfig() 125 assert.Nil(t, config) 126 assert.Contains(t, err.Error(), tc.expectedErrorMsg) 127 }) 128 } 129 } 130 131 func TestReloadNotificationReceived(t *testing.T) { 132 // given 133 fileName := "testdata/valid-flowlogs-config.yaml" 134 135 configReceived := false 136 137 // when 138 reloadInterval = 1 * time.Millisecond 139 sut := NewConfigWatcher(fileName, func(_ context.Context, _ uint64, config DynamicExportersConfig) { 140 configReceived = true 141 }) 142 defer sut.Stop() 143 144 // then 145 assert.Eventually(t, func() bool { 146 return configReceived 147 }, 1*time.Second, 1*time.Millisecond) 148 149 } 150 151 func assertFlowLogConfig(t *testing.T, expected, actual FlowLogConfig) { 152 153 assert.Equal(t, expected.Name, actual.Name) 154 assert.Equal(t, expected.FilePath, actual.FilePath) 155 assert.Equal(t, expected.FieldMask, actual.FieldMask) 156 assert.Equal(t, len(expected.IncludeFilters), len(actual.IncludeFilters)) 157 for i := range expected.IncludeFilters { 158 assert.Equal(t, expected.IncludeFilters[i].String(), actual.IncludeFilters[i].String()) 159 } 160 assert.Equal(t, len(expected.ExcludeFilters), len(actual.ExcludeFilters)) 161 for i := range expected.ExcludeFilters { 162 assert.Equal(t, expected.ExcludeFilters[i].String(), actual.ExcludeFilters[i].String()) 163 } 164 if expected.End == nil { 165 assert.Nil(t, actual.End) 166 } else { 167 assert.True(t, expected.End.Equal(*actual.End), "expected %s vs actual %s", expected.End.String(), actual.End.String()) 168 } 169 170 }