github.com/polarismesh/polaris@v1.17.8/apiserver/eurekaserver/access_test.go (about) 1 /** 2 * Tencent is pleased to support the open source community by making Polaris available. 3 * 4 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. 5 * 6 * Licensed under the BSD 3-Clause License (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * https://opensource.org/licenses/BSD-3-Clause 11 * 12 * Unless required by applicable law or agreed to in writing, software distributed 13 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 14 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 15 * specific language governing permissions and limitations under the License. 16 */ 17 18 package eurekaserver 19 20 import ( 21 "bytes" 22 "context" 23 "encoding/json" 24 "encoding/xml" 25 "fmt" 26 "net/http" 27 "net/http/httptest" 28 "net/url" 29 "reflect" 30 "testing" 31 "time" 32 "unsafe" 33 34 "github.com/emicklei/go-restful/v3" 35 apimodel "github.com/polarismesh/specification/source/go/api/v1/model" 36 "github.com/polarismesh/specification/source/go/api/v1/service_manage" 37 "github.com/stretchr/testify/assert" 38 "google.golang.org/protobuf/types/known/wrapperspb" 39 40 api "github.com/polarismesh/polaris/common/api/v1" 41 testsuit "github.com/polarismesh/polaris/test/suit" 42 ) 43 44 func createEurekaServerForTest( 45 discoverSuit *testsuit.DiscoverTestSuit, options map[string]interface{}) (*EurekaServer, error) { 46 eurekaSrv := &EurekaServer{ 47 namingServer: discoverSuit.DiscoverServer(), 48 originDiscoverSvr: discoverSuit.OriginDiscoverServer(), 49 healthCheckServer: discoverSuit.HealthCheckServer(), 50 allowAsyncRegis: false, 51 } 52 err := eurekaSrv.Initialize(context.Background(), options, nil) 53 if err != nil { 54 return nil, err 55 } 56 eurekaSrv.registerInstanceChain() 57 return eurekaSrv, nil 58 } 59 60 func batchBuildInstances(appId string, host string, port int, lease *LeaseInfo, count int) []*InstanceInfo { 61 var instances []*InstanceInfo 62 for i := 0; i < count; i++ { 63 portValue := port + i 64 instance := &InstanceInfo{ 65 InstanceId: fmt.Sprintf("%s_%s_%d", appId, host, portValue), 66 AppName: appId, 67 IpAddr: host, 68 Port: &PortWrapper{ 69 RealPort: portValue, 70 RealEnable: true, 71 }, 72 SecurePort: &PortWrapper{ 73 RealEnable: false, 74 }, 75 CountryId: 1, 76 DataCenterInfo: &DataCenterInfo{ 77 Clazz: "testClazz", 78 Name: "testName", 79 }, 80 HostName: host, 81 Status: "UP", 82 LeaseInfo: lease, 83 } 84 instances = append(instances, instance) 85 } 86 return instances 87 } 88 89 func batchCreateInstance(t *testing.T, eurekaSvr *EurekaServer, namespace string, instances []*InstanceInfo) { 90 for _, instance := range instances { 91 code := eurekaSvr.registerInstances(context.Background(), namespace, instance.AppName, instance, false) 92 assert.Equal(t, api.ExecuteSuccess, code) 93 } 94 } 95 96 type mockResponseWriter struct { 97 statusCode int 98 body bytes.Buffer 99 header http.Header 100 } 101 102 func newMockResponseWriter() *mockResponseWriter { 103 return &mockResponseWriter{header: map[string][]string{}} 104 } 105 106 func (m *mockResponseWriter) WriteHeader(statusCode int) { 107 m.statusCode = statusCode 108 } 109 110 func (m *mockResponseWriter) Write(value []byte) (int, error) { 111 return m.body.Write(value) 112 } 113 114 func (m *mockResponseWriter) Header() http.Header { 115 return m.header 116 } 117 118 func countInstances(applications *Applications) int { 119 var count int 120 for _, app := range applications.Application { 121 count += len(app.Instance) 122 } 123 return count 124 } 125 126 func TestEmptySlice(t *testing.T) { 127 applications := &Applications{} 128 count := countInstances(applications) 129 assert.Equal(t, 0, count) 130 } 131 132 func checkInstanceAction(t *testing.T, applications *Applications, appName string, instanceId string, action string) { 133 var hasApp bool 134 var hasInstance bool 135 var actionType string 136 for _, app := range applications.Application { 137 if app.Name == appName { 138 hasApp = true 139 for _, instance := range app.Instance { 140 if instance.InstanceId == instanceId { 141 hasInstance = true 142 actionType = instance.ActionType 143 } 144 } 145 } 146 } 147 assert.True(t, hasInstance) 148 assert.True(t, hasApp) 149 // fix: github action not suit for aync jobs 150 fmt.Printf("latest action is %s\n", actionType) 151 //assert.Equal(t, action, actionType) 152 } 153 154 // 测试新建实例 155 func TestCreateInstance(t *testing.T) { 156 discoverSuit := &testsuit.DiscoverTestSuit{} 157 if err := discoverSuit.Initialize(); err != nil { 158 t.Fatal(err) 159 } 160 defer discoverSuit.Destroy() 161 162 options := map[string]interface{}{optionRefreshInterval: 5, optionDeltaExpireInterval: 120} 163 eurekaSrv, err := createEurekaServerForTest(discoverSuit, options) 164 assert.Nil(t, err) 165 eurekaSrv.workers = NewApplicationsWorkers(eurekaSrv.refreshInterval, eurekaSrv.deltaExpireInterval, 166 eurekaSrv.enableSelfPreservation, eurekaSrv.namingServer, eurekaSrv.healthCheckServer, eurekaSrv.namespace) 167 168 namespace := "default" 169 appId := "TESTAPP" 170 startPort := 8900 171 host := "127.0.1.1" 172 total := 10 173 instances := batchBuildInstances(appId, host, startPort, &LeaseInfo{ 174 RenewalIntervalInSecs: 30, 175 DurationInSecs: 120, 176 }, total) 177 batchCreateInstance(t, eurekaSrv, namespace, instances) 178 179 time.Sleep(10 * time.Second) 180 httpRequest := &http.Request{Header: map[string][]string{ 181 restful.HEADER_Accept: {restful.MIME_JSON}, 182 HeaderNamespace: {namespace}, 183 }} 184 req := restful.NewRequest(httpRequest) 185 mockWriter := newMockResponseWriter() 186 resp := &restful.Response{ResponseWriter: mockWriter} 187 eurekaSrv.GetAllApplications(req, resp) 188 assert.Equal(t, 200, mockWriter.statusCode) 189 190 appResp := &ApplicationsResponse{} 191 err = json.Unmarshal(mockWriter.body.Bytes(), appResp) 192 assert.Nil(t, err) 193 count := countInstances(appResp.Applications) 194 assert.Equal(t, total, count) 195 196 time.Sleep(5 * time.Second) 197 instanceId := fmt.Sprintf("%s_%s_%d", appId, host, startPort) 198 code := eurekaSrv.deregisterInstance(context.Background(), namespace, appId, instanceId, false) 199 assert.Equal(t, api.ExecuteSuccess, code) 200 time.Sleep(20 * time.Second) 201 202 deltaReq := restful.NewRequest(httpRequest) 203 deltaMockWriter := newMockResponseWriter() 204 deltaResp := &restful.Response{ResponseWriter: deltaMockWriter} 205 eurekaSrv.GetDeltaApplications(deltaReq, deltaResp) 206 207 deltaAppResp := &ApplicationsResponse{} 208 err = json.Unmarshal(deltaMockWriter.body.Bytes(), deltaAppResp) 209 assert.Nil(t, err) 210 checkInstanceAction(t, deltaAppResp.Applications, appId, instanceId, ActionDeleted) 211 } 212 213 // Test_EurekaWrite . 214 func Test_EurekaWrite(t *testing.T) { 215 discoverSuit := &testsuit.DiscoverTestSuit{} 216 if err := discoverSuit.Initialize(); err != nil { 217 t.Fatal(err) 218 } 219 defer discoverSuit.Destroy() 220 221 options := map[string]interface{}{optionRefreshInterval: 5, optionDeltaExpireInterval: 120} 222 eurekaSrv, err := createEurekaServerForTest(discoverSuit, options) 223 assert.Nil(t, err) 224 225 mockIns := genMockEurekaInstance() 226 227 t.Run("RegisterInstance", func(t *testing.T) { 228 // pretty output must be created and written explicitly 229 output, err := xml.MarshalIndent(mockIns, " ", " ") 230 assert.NoError(t, err) 231 232 var body bytes.Buffer 233 _, err = body.Write([]byte(xml.Header)) 234 assert.NoError(t, err) 235 _, err = body.Write(output) 236 assert.NoError(t, err) 237 238 mockReq := httptest.NewRequest("", fmt.Sprintf("http://127.0.0.1:8761/eureka/v2/apps/%s", mockIns.AppName), &body) 239 mockReq.Header.Add(restful.HEADER_Accept, restful.MIME_XML) 240 mockReq.Header.Add(restful.HEADER_ContentType, restful.MIME_XML) 241 mockRsp := newMockResponseWriter() 242 243 restfulReq := restful.NewRequest(mockReq) 244 injectRestfulReqPathParameters(t, restfulReq, map[string]string{ 245 ParamAppId: mockIns.AppName, 246 }) 247 // 这里是异步注册 248 eurekaSrv.RegisterApplication(restfulReq, restful.NewResponse(mockRsp)) 249 assert.Equal(t, http.StatusNoContent, mockRsp.statusCode) 250 assert.Equal(t, restfulReq.Attribute(statusCodeHeader), uint32(apimodel.Code_ExecuteSuccess)) 251 252 time.Sleep(5 * time.Second) 253 saveIns, err := eurekaSrv.originDiscoverSvr.Cache().GetStore().GetInstance(mockIns.InstanceId) 254 assert.NoError(t, err) 255 assert.NotNil(t, saveIns) 256 }) 257 258 t.Run("UpdateStatus", func(t *testing.T) { 259 t.Run("StatusUnknown", func(t *testing.T) { 260 mockReq := httptest.NewRequest("", fmt.Sprintf("http://127.0.0.1:8761/eureka/v2/apps/%s/%s/status", 261 mockIns.AppName, mockIns.InstanceId), nil) 262 mockReq.PostForm = url.Values{} 263 mockReq.PostForm.Add(ParamValue, StatusUnknown) 264 mockRsp := newMockResponseWriter() 265 266 restfulReq := restful.NewRequest(mockReq) 267 injectRestfulReqPathParameters(t, restfulReq, map[string]string{ 268 ParamAppId: mockIns.AppName, 269 ParamInstId: mockIns.InstanceId, 270 }) 271 eurekaSrv.UpdateStatus(restfulReq, restful.NewResponse(mockRsp)) 272 assert.Equal(t, http.StatusOK, mockRsp.statusCode) 273 assert.Equal(t, restfulReq.Attribute(statusCodeHeader), uint32(apimodel.Code_ExecuteSuccess)) 274 275 // 276 saveIns, err := discoverSuit.Storage.GetInstance(mockIns.InstanceId) 277 assert.NoError(t, err) 278 assert.False(t, saveIns.Isolate()) 279 }) 280 281 t.Run("StatusDown", func(t *testing.T) { 282 mockReq := httptest.NewRequest("", fmt.Sprintf("http://127.0.0.1:8761/eureka/v2/apps/%s/%s/status", 283 mockIns.AppName, mockIns.InstanceId), nil) 284 mockReq.PostForm = url.Values{} 285 mockReq.PostForm.Add(ParamValue, StatusDown) 286 mockRsp := newMockResponseWriter() 287 288 restfulReq := restful.NewRequest(mockReq) 289 injectRestfulReqPathParameters(t, restfulReq, map[string]string{ 290 ParamAppId: mockIns.AppName, 291 ParamInstId: mockIns.InstanceId, 292 }) 293 eurekaSrv.UpdateStatus(restfulReq, restful.NewResponse(mockRsp)) 294 assert.Equal(t, http.StatusOK, mockRsp.statusCode) 295 assert.Equal(t, restfulReq.Attribute(statusCodeHeader), uint32(apimodel.Code_ExecuteSuccess), fmt.Sprintf("%d", restfulReq.Attribute(statusCodeHeader))) 296 297 // 298 saveIns, err := discoverSuit.Storage.GetInstance(mockIns.InstanceId) 299 assert.NoError(t, err) 300 assert.True(t, saveIns.Isolate()) 301 assert.Equal(t, StatusDown, saveIns.Proto.Metadata[InternalMetadataStatus]) 302 }) 303 304 t.Run("StatusUp", func(t *testing.T) { 305 mockReq := httptest.NewRequest("", fmt.Sprintf("http://127.0.0.1:8761/eureka/v2/apps/%s/%s/status", 306 mockIns.AppName, mockIns.InstanceId), nil) 307 mockReq.PostForm = url.Values{} 308 mockReq.PostForm.Add(ParamValue, StatusUp) 309 mockRsp := newMockResponseWriter() 310 311 restfulReq := restful.NewRequest(mockReq) 312 injectRestfulReqPathParameters(t, restfulReq, map[string]string{ 313 ParamAppId: mockIns.AppName, 314 ParamInstId: mockIns.InstanceId, 315 }) 316 eurekaSrv.UpdateStatus(restfulReq, restful.NewResponse(mockRsp)) 317 assert.Equal(t, http.StatusOK, mockRsp.statusCode) 318 assert.Equal(t, restfulReq.Attribute(statusCodeHeader), uint32(apimodel.Code_ExecuteSuccess), fmt.Sprintf("%d", restfulReq.Attribute(statusCodeHeader))) 319 320 // 321 saveIns, err := discoverSuit.Storage.GetInstance(mockIns.InstanceId) 322 assert.NoError(t, err) 323 assert.False(t, saveIns.Isolate()) 324 assert.Equal(t, StatusUp, saveIns.Proto.Metadata[InternalMetadataStatus]) 325 }) 326 327 t.Run("Polaris_UpdateInstances", func(t *testing.T) { 328 defer func() { 329 rsp := discoverSuit.OriginDiscoverServer().UpdateInstances(discoverSuit.DefaultCtx, []*service_manage.Instance{ 330 { 331 Id: wrapperspb.String(mockIns.InstanceId), 332 Isolate: wrapperspb.Bool(false), 333 }, 334 }) 335 assert.Equal(t, apimodel.Code_ExecuteSuccess, apimodel.Code(rsp.GetCode().GetValue())) 336 }() 337 rsp := discoverSuit.OriginDiscoverServer().UpdateInstances(discoverSuit.DefaultCtx, []*service_manage.Instance{ 338 { 339 Id: wrapperspb.String(mockIns.InstanceId), 340 Isolate: wrapperspb.Bool(true), 341 }, 342 }) 343 assert.Equal(t, apimodel.Code_ExecuteSuccess, apimodel.Code(rsp.GetCode().GetValue())) 344 345 // 在获取一次 346 saveIns, err := discoverSuit.Storage.GetInstance(mockIns.InstanceId) 347 assert.NoError(t, err) 348 assert.True(t, saveIns.Isolate()) 349 assert.Equal(t, StatusOutOfService, saveIns.Proto.Metadata[InternalMetadataStatus]) 350 }) 351 352 t.Run("Polaris_UpdateInstancesIsolate", func(t *testing.T) { 353 rsp := discoverSuit.OriginDiscoverServer().UpdateInstances(discoverSuit.DefaultCtx, []*service_manage.Instance{ 354 { 355 Id: wrapperspb.String(mockIns.InstanceId), 356 Isolate: wrapperspb.Bool(true), 357 }, 358 }) 359 assert.Equal(t, apimodel.Code_ExecuteSuccess, apimodel.Code(rsp.GetCode().GetValue())) 360 361 // 在获取一次 362 _, saveInss, err := discoverSuit.Storage.GetExpandInstances(map[string]string{ 363 "id": mockIns.InstanceId, 364 }, map[string]string{}, 0, 10) 365 assert.NoError(t, err) 366 assert.Equal(t, 1, len(saveInss)) 367 assert.True(t, saveInss[0].Isolate()) 368 assert.Equal(t, StatusOutOfService, saveInss[0].Proto.Metadata[InternalMetadataStatus]) 369 }) 370 }) 371 } 372 373 func injectRestfulReqPathParameters(t *testing.T, req *restful.Request, params map[string]string) { 374 v := reflect.ValueOf(req) 375 if v.Kind() == reflect.Ptr { 376 v = v.Elem() 377 } 378 379 field := v.FieldByName("pathParameters") 380 fieldVal := GetUnexportedField(field) 381 382 pathParameters, ok := fieldVal.(map[string]string) 383 assert.True(t, ok) 384 for k, v := range params { 385 pathParameters[k] = v 386 } 387 SetUnexportedField(field, params) 388 } 389 390 func genMockEurekaInstance() *InstanceInfo { 391 mockIns := &InstanceInfo{ 392 XMLName: struct{}{}, 393 InstanceId: "123", 394 AppName: "MOCK_SERVICE", 395 AppGroupName: "MOCK_SERVICE", 396 IpAddr: "127.0.0.1", 397 Sid: "", 398 Port: &PortWrapper{ 399 Port: "8080", 400 RealPort: 8080, 401 Enabled: "true", 402 RealEnable: true, 403 }, 404 Status: StatusUp, 405 OverriddenStatus: StatusUnknown, 406 } 407 return mockIns 408 } 409 410 func SetUnexportedField(field reflect.Value, value interface{}) { 411 reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())). 412 Elem(). 413 Set(reflect.ValueOf(value)) 414 } 415 416 func GetUnexportedField(field reflect.Value) interface{} { 417 return reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Interface() 418 }