github.com/m3db/m3@v1.5.0/src/integration/resources/inprocess/coordinator_test.go (about)

     1  // +build test_harness
     2  // Copyright (c) 2021  Uber Technologies, Inc.
     3  //
     4  // Permission is hereby granted, free of charge, to any person obtaining a copy
     5  // of this software and associated documentation files (the "Software"), to deal
     6  // in the Software without restriction, including without limitation the rights
     7  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8  // copies of the Software, and to permit persons to whom the Software is
     9  // furnished to do so, subject to the following conditions:
    10  //
    11  // The above copyright notice and this permission notice shall be included in
    12  // all copies or substantial portions of the Software.
    13  //
    14  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    20  // THE SOFTWARE.
    21  
    22  package inprocess
    23  
    24  import (
    25  	"testing"
    26  
    27  	"github.com/m3db/m3/src/cluster/generated/proto/placementpb"
    28  	"github.com/m3db/m3/src/cluster/placement"
    29  	"github.com/m3db/m3/src/integration/resources"
    30  	"github.com/m3db/m3/src/msg/generated/proto/topicpb"
    31  	"github.com/m3db/m3/src/msg/topic"
    32  	"github.com/m3db/m3/src/query/generated/proto/admin"
    33  	"github.com/m3db/m3/src/query/generated/proto/prompb"
    34  	"github.com/m3db/m3/src/query/storage"
    35  	xtime "github.com/m3db/m3/src/x/time"
    36  
    37  	"github.com/prometheus/common/model"
    38  	"github.com/stretchr/testify/assert"
    39  	"github.com/stretchr/testify/require"
    40  )
    41  
    42  func TestNewCoordinator(t *testing.T) {
    43  	dbnode, err := NewDBNodeFromYAML(defaultDBNodeConfig, DBNodeOptions{Start: true})
    44  	require.NoError(t, err)
    45  	defer func() {
    46  		assert.NoError(t, dbnode.Close())
    47  	}()
    48  
    49  	coord, err := NewCoordinatorFromYAML(defaultCoordConfig, CoordinatorOptions{Start: true})
    50  	require.NoError(t, err)
    51  	require.NoError(t, coord.Close())
    52  
    53  	// Restart and shut down again to test restarting.
    54  	coord, err = NewCoordinatorFromYAML(defaultCoordConfig, CoordinatorOptions{Start: true})
    55  	require.NoError(t, err)
    56  	require.NoError(t, coord.Close())
    57  }
    58  
    59  func TestNewEmbeddedCoordinator(t *testing.T) {
    60  	dbnode, err := NewDBNodeFromYAML(embeddedCoordConfig, DBNodeOptions{Start: true})
    61  	require.NoError(t, err)
    62  	defer func() {
    63  		assert.NoError(t, dbnode.Close())
    64  	}()
    65  
    66  	d, ok := dbnode.(*DBNode)
    67  	require.True(t, ok)
    68  	require.True(t, d.started)
    69  
    70  	_, err = NewEmbeddedCoordinator(d)
    71  	require.NoError(t, err)
    72  }
    73  
    74  func TestNewEmbeddedCoordinatorNotStarted(t *testing.T) {
    75  	var dbnode DBNode
    76  	_, err := NewEmbeddedCoordinator(&dbnode)
    77  	require.Error(t, err)
    78  }
    79  
    80  func TestCoordinatorAPIs(t *testing.T) {
    81  	_, coord, closer := setupNodeAndCoordinator(t)
    82  	defer closer()
    83  
    84  	testM3msgTopicFunctions(t, coord)
    85  	testAggPlacementFunctions(t, coord)
    86  	testMetadataAPIs(t, coord)
    87  }
    88  
    89  func testMetadataAPIs(t *testing.T, coordinator resources.Coordinator) {
    90  	err := coordinator.WriteProm("cpu", map[string]string{"pod": "foo-1234"}, []prompb.Sample{
    91  		{Value: 1, Timestamp: storage.TimeToPromTimestamp(xtime.Now())},
    92  	}, nil)
    93  	require.NoError(t, err)
    94  
    95  	names, err := coordinator.LabelNames(resources.LabelNamesRequest{}, nil)
    96  	require.NoError(t, err)
    97  	require.Equal(t, model.LabelNames{
    98  		"__name__",
    99  		"pod",
   100  	}, names)
   101  
   102  	values, err := coordinator.LabelValues(resources.LabelValuesRequest{
   103  		LabelName: "__name__",
   104  	}, nil)
   105  	require.NoError(t, err)
   106  	require.Equal(t, model.LabelValues{"cpu"}, values)
   107  
   108  	series, err := coordinator.Series(resources.SeriesRequest{
   109  		MetadataRequest: resources.MetadataRequest{
   110  			Match: "cpu",
   111  		},
   112  	}, nil)
   113  	require.NoError(t, err)
   114  	require.Equal(t, []model.Metric{
   115  		{
   116  			"__name__": "cpu",
   117  			"pod":      "foo-1234",
   118  		},
   119  	}, series)
   120  }
   121  
   122  func testM3msgTopicFunctions(t *testing.T, coord resources.Coordinator) {
   123  	// init an m3msg topic
   124  	m3msgTopicOpts := resources.M3msgTopicOptions{
   125  		Zone:      "embedded",
   126  		Env:       "default_env",
   127  		TopicName: "testtopic",
   128  	}
   129  	initResp, err := coord.InitM3msgTopic(
   130  		m3msgTopicOpts,
   131  		admin.TopicInitRequest{NumberOfShards: 16},
   132  	)
   133  	expectedInitResp := admin.TopicGetResponse{
   134  		Topic: &topicpb.Topic{
   135  			Name:             "testtopic",
   136  			NumberOfShards:   16,
   137  			ConsumerServices: nil,
   138  		},
   139  		Version: 1,
   140  	}
   141  	require.NoError(t, err)
   142  	validateEqualTopicResp(t, expectedInitResp, initResp)
   143  
   144  	// add a consumer service
   145  	consumer := topicpb.ConsumerService{
   146  		ServiceId: &topicpb.ServiceID{
   147  			Name:        "testservice",
   148  			Environment: m3msgTopicOpts.Env,
   149  			Zone:        m3msgTopicOpts.Zone,
   150  		},
   151  		ConsumptionType: topicpb.ConsumptionType_SHARED,
   152  		MessageTtlNanos: 1,
   153  	}
   154  	addResp, err := coord.AddM3msgTopicConsumer(
   155  		m3msgTopicOpts,
   156  		admin.TopicAddRequest{ConsumerService: &consumer},
   157  	)
   158  	expectedAddResp := admin.TopicGetResponse{
   159  		Topic: &topicpb.Topic{
   160  			Name:           "testtopic",
   161  			NumberOfShards: 16,
   162  			ConsumerServices: []*topicpb.ConsumerService{
   163  				&consumer,
   164  			},
   165  		},
   166  		Version: 2,
   167  	}
   168  	require.NoError(t, err)
   169  	validateEqualTopicResp(t, expectedAddResp, addResp)
   170  
   171  	// get an m3msg topic
   172  	getResp, err := coord.GetM3msgTopic(m3msgTopicOpts)
   173  	require.NoError(t, err)
   174  	validateEqualTopicResp(t, expectedAddResp, getResp)
   175  }
   176  
   177  func validateEqualTopicResp(t *testing.T, expected, actual admin.TopicGetResponse) {
   178  	require.Equal(t, expected.Version, actual.Version)
   179  
   180  	t1, err := topic.NewTopicFromProto(expected.Topic)
   181  	require.NoError(t, err)
   182  	t2, err := topic.NewTopicFromProto(actual.Topic)
   183  	require.NoError(t, err)
   184  	require.Equal(t, t1, t2)
   185  }
   186  
   187  func testAggPlacementFunctions(t *testing.T, coord resources.Coordinator) {
   188  	placementOpts := resources.PlacementRequestOptions{
   189  		Service: resources.ServiceTypeM3Aggregator,
   190  		Env:     "default_env",
   191  		Zone:    "embedded",
   192  	}
   193  	initRequest := admin.PlacementInitRequest{
   194  		Instances: []*placementpb.Instance{
   195  			{
   196  				Id:             "host1",
   197  				IsolationGroup: "rack1",
   198  				Zone:           "embedded",
   199  				Weight:         1,
   200  				Endpoint:       "http://host1:1234",
   201  				Hostname:       "host1",
   202  				Port:           1234,
   203  				Metadata: &placementpb.InstanceMetadata{
   204  					DebugPort: 0,
   205  				},
   206  			},
   207  			{
   208  				Id:             "host2",
   209  				IsolationGroup: "rack2",
   210  				Zone:           "embedded",
   211  				Weight:         1,
   212  				Endpoint:       "http://host2:1234",
   213  				Hostname:       "host2",
   214  				Port:           1234,
   215  				Metadata: &placementpb.InstanceMetadata{
   216  					DebugPort: 0,
   217  				},
   218  			},
   219  		},
   220  		NumShards:         1,
   221  		ReplicationFactor: 2,
   222  	}
   223  	instanceMap := make(map[string]*placementpb.Instance, len(initRequest.Instances))
   224  	for _, ins := range initRequest.Instances {
   225  		newIns := *ins
   226  		newIns.ShardSetId = 1 // initialized
   227  		newIns.Shards = []*placementpb.Shard{
   228  			{
   229  				Id:                0,
   230  				State:             placementpb.ShardState_INITIALIZING,
   231  				SourceId:          "",
   232  				CutoverNanos:      0,
   233  				CutoffNanos:       0,
   234  				RedirectToShardId: nil,
   235  			},
   236  		}
   237  		instanceMap[ins.Hostname] = &newIns
   238  	}
   239  	initResp, err := coord.InitPlacement(placementOpts, initRequest)
   240  	require.NoError(t, err)
   241  	expectedPlacement := &placementpb.Placement{
   242  		Instances:     instanceMap,
   243  		ReplicaFactor: uint32(initRequest.ReplicationFactor),
   244  		NumShards:     uint32(initRequest.NumShards),
   245  		MaxShardSetId: uint32(initRequest.NumShards),
   246  		IsSharded:     true,
   247  		IsMirrored:    true,
   248  	}
   249  	require.Equal(t, int32(0), initResp.Version)
   250  	validateEqualAggPlacement(t, expectedPlacement, initResp.Placement)
   251  
   252  	getResp, err := coord.GetPlacement(placementOpts)
   253  
   254  	require.NoError(t, err)
   255  	require.Equal(t, int32(1), getResp.Version)
   256  	validateEqualAggPlacement(t, expectedPlacement, getResp.Placement)
   257  
   258  	wrongPlacementOpts := resources.PlacementRequestOptions{
   259  		Service: resources.ServiceTypeM3Aggregator,
   260  		Env:     "default_env_wrong",
   261  		Zone:    "embedded",
   262  	}
   263  	_, err = coord.GetPlacement(wrongPlacementOpts)
   264  	require.NotNil(t, err)
   265  }
   266  
   267  func validateEqualAggPlacement(t *testing.T, expected, actual *placementpb.Placement) {
   268  	p1, err := placement.NewPlacementFromProto(expected)
   269  	require.NoError(t, err)
   270  	p2, err := placement.NewPlacementFromProto(actual)
   271  	require.NoError(t, err)
   272  	require.Equal(t, p1.String(), p2.String())
   273  }
   274  
   275  const defaultCoordConfig = `
   276  clusters:
   277    - client:
   278        config:
   279          service:
   280            env: default_env
   281            zone: embedded
   282            service: m3db
   283            etcdClusters:
   284              - zone: embedded
   285                endpoints:
   286                  - 127.0.0.1:2379
   287  `
   288  
   289  const embeddedCoordConfig = `
   290  coordinator:
   291    clusters:
   292      - client:
   293          config:
   294            service:
   295              env: default_env
   296              zone: embedded
   297              service: m3db
   298              etcdClusters:
   299                - zone: embedded
   300                  endpoints:
   301                    - 127.0.0.1:2379
   302  
   303  db: {}
   304  `