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  }