github.com/kubewharf/katalyst-core@v0.5.3/pkg/agent/resourcemanager/fetcher/manager_test.go (about) 1 /* 2 Copyright 2022 The Katalyst Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package fetcher 18 19 import ( 20 "context" 21 "io/ioutil" 22 "os" 23 "testing" 24 "time" 25 26 "github.com/stretchr/testify/require" 27 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/util/sets" 29 "k8s.io/client-go/tools/record" 30 "k8s.io/kubernetes/pkg/kubelet/config" 31 "k8s.io/kubernetes/pkg/kubelet/pluginmanager" 32 plugincache "k8s.io/kubernetes/pkg/kubelet/pluginmanager/cache" 33 34 "github.com/kubewharf/katalyst-api/pkg/plugins/registration" 35 "github.com/kubewharf/katalyst-api/pkg/plugins/skeleton" 36 "github.com/kubewharf/katalyst-api/pkg/protocol/reporterplugin/v1alpha1" 37 "github.com/kubewharf/katalyst-core/pkg/agent/resourcemanager/fetcher/plugin" 38 "github.com/kubewharf/katalyst-core/pkg/agent/resourcemanager/reporter" 39 katalystconfig "github.com/kubewharf/katalyst-core/pkg/config" 40 "github.com/kubewharf/katalyst-core/pkg/config/agent" 41 "github.com/kubewharf/katalyst-core/pkg/config/agent/metaserver" 42 reporterconfig "github.com/kubewharf/katalyst-core/pkg/config/agent/reporter" 43 "github.com/kubewharf/katalyst-core/pkg/metrics" 44 ) 45 46 const ( 47 testPluginName = "fake-reporter-plugin-1" 48 testPluginNameSecond = "fake-reporter-plugin-2" 49 testPluginNameThird = "fake-reporter-plugin-3" 50 ) 51 52 var testGroupVersionKind = v1.GroupVersionKind{ 53 Group: "test-group", 54 Kind: "test-kind", 55 Version: "test-version", 56 } 57 58 func tmpSocketDir() (socketDir string, err error) { 59 socketDir, err = ioutil.TempDir("", "reporter_plugin") 60 if err != nil { 61 return 62 } 63 _ = os.MkdirAll(socketDir, 0o755) 64 return 65 } 66 67 func generateTestConfiguration(dir string) *katalystconfig.Configuration { 68 return &katalystconfig.Configuration{ 69 AgentConfiguration: &agent.AgentConfiguration{ 70 GenericAgentConfiguration: &agent.GenericAgentConfiguration{ 71 MetaServerConfiguration: &metaserver.MetaServerConfiguration{CheckpointManagerDir: dir}, 72 GenericReporterConfiguration: &reporterconfig.GenericReporterConfiguration{ 73 CollectInterval: 5 * time.Second, 74 }, 75 }, 76 }, 77 } 78 } 79 80 func TestNewManagerImpl(t *testing.T) { 81 t.Parallel() 82 83 socketDir, err := tmpSocketDir() 84 testReporter := reporter.NewReporterManagerStub() 85 require.NoError(t, err) 86 defer os.RemoveAll(socketDir) 87 _, err = NewReporterPluginManager(testReporter, metrics.DummyMetrics{}, nil, generateTestConfiguration(socketDir)) 88 require.NoError(t, err) 89 os.RemoveAll(socketDir) 90 } 91 92 // Tests that the device plugin manager correctly handles registration and re-registration by 93 // making sure that after registration, devices are correctly updated and if a re-registration 94 // happens, we will NOT delete devices; and no orphaned devices left. 95 func TestReporterPluginReRegistration(t *testing.T) { 96 t.Parallel() 97 98 socketDir, err := tmpSocketDir() 99 require.NoError(t, err) 100 defer os.RemoveAll(socketDir) 101 102 content1 := []*v1alpha1.ReportContent{ 103 { 104 GroupVersionKind: &testGroupVersionKind, 105 Field: []*v1alpha1.ReportField{ 106 { 107 FieldType: v1alpha1.FieldType_Spec, 108 FieldName: "fieldName_a", 109 Value: []byte("Value_a"), 110 }, 111 }, 112 }, 113 } 114 115 testReporter := reporter.NewReporterManagerStub() 116 117 ctx, cancel := context.WithCancel(context.TODO()) 118 defer cancel() 119 _, ch, p1 := setup(t, ctx, content1, nil, socketDir, testPluginName, testReporter) 120 121 select { 122 case <-ch: 123 case <-time.After(5 * time.Second): 124 t.Fatalf("timeout while waiting for manager update") 125 } 126 127 p1GetReportContentResponse := testReporter.GetReportContentResponse(p1.Name()) 128 require.NotNil(t, p1GetReportContentResponse) 129 reporterContentsEqual(t, content1, p1GetReportContentResponse.Content) 130 131 content2 := []*v1alpha1.ReportContent{ 132 { 133 GroupVersionKind: &testGroupVersionKind, 134 Field: []*v1alpha1.ReportField{ 135 { 136 FieldType: v1alpha1.FieldType_Spec, 137 FieldName: "fieldName_b", 138 Value: []byte("Value_b"), 139 }, 140 }, 141 }, 142 } 143 144 p2 := setupReporterPlugin(t, content2, socketDir, testPluginNameSecond) 145 146 select { 147 case <-ch: 148 case <-time.After(5 * time.Second): 149 t.Fatalf("timeout while waiting for manager update") 150 } 151 152 p1GetReportContentResponse = testReporter.GetReportContentResponse(p1.Name()) 153 require.NotNil(t, p1GetReportContentResponse) 154 reporterContentsEqual(t, content1, p1GetReportContentResponse.Content) 155 156 p2GetReportContentResponse := testReporter.GetReportContentResponse(p2.Name()) 157 require.NotNil(t, p2GetReportContentResponse) 158 reporterContentsEqual(t, content2, p2GetReportContentResponse.Content) 159 160 // test the scenario that plugin de-register and graceful shut down 161 _ = p1.Stop() 162 _ = p2.Stop() 163 } 164 165 func TestHealthz(t *testing.T) { 166 t.Parallel() 167 168 socketDir, err := tmpSocketDir() 169 require.NoError(t, err) 170 defer os.RemoveAll(socketDir) 171 172 content1 := []*v1alpha1.ReportContent{ 173 { 174 GroupVersionKind: &testGroupVersionKind, 175 Field: []*v1alpha1.ReportField{ 176 { 177 FieldType: v1alpha1.FieldType_Spec, 178 FieldName: "fieldName_a", 179 Value: []byte("Value_a"), 180 }, 181 }, 182 }, 183 } 184 185 testReporter := reporter.NewReporterManagerStub() 186 187 ctx, cancel := context.WithCancel(context.TODO()) 188 defer cancel() 189 _, ch, p := setup(t, ctx, content1, nil, socketDir, testPluginNameThird, testReporter) 190 191 select { 192 case <-ch: 193 case <-time.After(6 * time.Second): 194 t.Fatalf("timeout while waiting for manager update") 195 } 196 197 _ = p.Stop() 198 } 199 200 func setup(t *testing.T, ctx context.Context, content []*v1alpha1.ReportContent, callback plugin.ListAndWatchCallback, socketDir string, pluginSocketName string, reporter reporter.Manager) (registration.AgentPluginHandler, <-chan interface{}, skeleton.GenericPlugin) { 201 m, updateChan := setupReporterManager(t, ctx, content, socketDir, callback, reporter) 202 p := setupReporterPlugin(t, content, socketDir, pluginSocketName) 203 return m, updateChan, p 204 } 205 206 func setupReporterManager(t *testing.T, ctx context.Context, content []*v1alpha1.ReportContent, socketDir string, callback plugin.ListAndWatchCallback, reporter reporter.Manager) (registration.AgentPluginHandler, <-chan interface{}) { 207 m, err := NewReporterPluginManager(reporter, metrics.DummyMetrics{}, nil, generateTestConfiguration(socketDir)) 208 require.NoError(t, err) 209 updateChan := make(chan interface{}) 210 211 if callback != nil { 212 m.callback = callback 213 } 214 215 originalCallback := m.callback 216 m.callback = func(pluginName string, response *v1alpha1.GetReportContentResponse) { 217 originalCallback(pluginName, response) 218 updateChan <- new(interface{}) 219 } 220 221 go m.Run(ctx) 222 223 pluginManager := pluginmanager.NewPluginManager( 224 socketDir, 225 &record.FakeRecorder{}, 226 ) 227 228 pluginManager.AddHandler(m.GetHandlerType(), plugincache.PluginHandler(m)) 229 230 go pluginManager.Run(config.NewSourcesReady(func(_ sets.String) bool { return true }), ctx.Done()) 231 232 return m, updateChan 233 } 234 235 func setupReporterPlugin(t *testing.T, content []*v1alpha1.ReportContent, socketDir string, pluginName string) skeleton.GenericPlugin { 236 p, _ := skeleton.NewRegistrationPluginWrapper( 237 skeleton.NewReporterPluginStub(content, pluginName), 238 []string{socketDir}, nil) 239 err := p.Start() 240 require.NoError(t, err) 241 return p 242 } 243 244 func reporterContentsEqual(t *testing.T, expected, actual []*v1alpha1.ReportContent) { 245 require.Equal(t, len(expected), len(actual)) 246 for idx := range expected { 247 require.Equal(t, expected[idx].GroupVersionKind, actual[idx].GroupVersionKind) 248 require.Equal(t, len(expected[idx].Field), len(actual[idx].Field)) 249 for fieldIdx := range expected[idx].Field { 250 require.Equal(t, expected[idx].Field[fieldIdx].FieldType, actual[idx].Field[fieldIdx].FieldType) 251 require.Equal(t, expected[idx].Field[fieldIdx].FieldName, actual[idx].Field[fieldIdx].FieldName) 252 require.Equal(t, expected[idx].Field[fieldIdx].Value, actual[idx].Field[fieldIdx].Value) 253 } 254 } 255 }