vitess.io/vitess@v0.16.2/go/vt/vtadmin/cluster/discovery/discovery_dynamic_test.go (about)

     1  /*
     2  Copyright 2022 The Vitess 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 discovery
    18  
    19  import (
    20  	"context"
    21  	"testing"
    22  
    23  	"github.com/stretchr/testify/assert"
    24  	"github.com/stretchr/testify/require"
    25  
    26  	vtadminpb "vitess.io/vitess/go/vt/proto/vtadmin"
    27  )
    28  
    29  func TestDynamicDiscoverVTGate(t *testing.T) {
    30  	t.Parallel()
    31  
    32  	tests := []struct {
    33  		name      string
    34  		contents  []byte
    35  		expected  *vtadminpb.VTGate
    36  		tags      []string
    37  		shouldErr bool
    38  	}{
    39  		{
    40  			name:      "empty config",
    41  			contents:  []byte(`{}`),
    42  			expected:  nil,
    43  			shouldErr: true,
    44  		},
    45  		{
    46  			name: "one gate",
    47  			contents: []byte(`
    48  				{
    49  					"vtgates": [{
    50  						"host": {
    51  							"hostname": "127.0.0.1:12345"
    52  						}
    53  					}]
    54  				}
    55  			`),
    56  			expected: &vtadminpb.VTGate{
    57  				Hostname: "127.0.0.1:12345",
    58  			},
    59  		},
    60  		{
    61  			name: "filtered by tags (one match)",
    62  			contents: []byte(`
    63  				{
    64  					"vtgates": [
    65  						{
    66  							"host": {
    67  								"hostname": "127.0.0.1:11111"
    68  							},
    69  							"tags": ["cell:cellA"]
    70  						}, 
    71  						{
    72  							"host": {
    73  								"hostname": "127.0.0.1:22222"
    74  							},
    75  							"tags": ["cell:cellB"]
    76  						},
    77  						{
    78  							"host": {
    79  								"hostname": "127.0.0.1:33333"
    80  							},
    81  							"tags": ["cell:cellA"]
    82  						}
    83  					]
    84  				}
    85  			`),
    86  			expected: &vtadminpb.VTGate{
    87  				Hostname: "127.0.0.1:22222",
    88  			},
    89  			tags: []string{"cell:cellB"},
    90  		},
    91  	}
    92  
    93  	ctx := context.Background()
    94  
    95  	for _, tt := range tests {
    96  		tt := tt
    97  
    98  		t.Run(tt.name, func(t *testing.T) {
    99  			t.Parallel()
   100  			disco := &DynamicDiscovery{}
   101  			err := disco.parseConfig(tt.contents)
   102  			require.NoError(t, err)
   103  
   104  			gate, err := disco.DiscoverVTGate(ctx, tt.tags)
   105  			if tt.shouldErr {
   106  				assert.Error(t, err)
   107  				return
   108  			}
   109  
   110  			assert.NoError(t, err)
   111  			assert.Equal(t, tt.expected, gate)
   112  		})
   113  	}
   114  }
   115  
   116  func TestDynamicDiscoverVTGates(t *testing.T) {
   117  	t.Parallel()
   118  
   119  	tests := []struct {
   120  		name     string
   121  		contents []byte
   122  		tags     []string
   123  		expected []*vtadminpb.VTGate
   124  		// True if the test should produce an error on the DiscoverVTGates call
   125  		shouldErr bool
   126  		// True if the test should produce an error on the disco.parseConfig step
   127  		shouldErrConfig bool
   128  	}{
   129  		{
   130  			name:      "empty config",
   131  			contents:  []byte(`{}`),
   132  			expected:  []*vtadminpb.VTGate{},
   133  			shouldErr: false,
   134  		},
   135  		{
   136  			name: "no tags",
   137  			contents: []byte(`
   138  				{
   139  					"vtgates": [
   140  						{
   141  							"host": {
   142  								"hostname": "127.0.0.1:12345"
   143  							}
   144  						},
   145  						{
   146  							"host": {
   147  								"hostname": "127.0.0.1:67890"
   148  							}
   149  						}
   150  					]
   151  				}
   152  			`),
   153  			expected: []*vtadminpb.VTGate{
   154  				{Hostname: "127.0.0.1:12345"},
   155  				{Hostname: "127.0.0.1:67890"},
   156  			},
   157  			shouldErr: false,
   158  		},
   159  		{
   160  			name: "filtered by tags",
   161  			contents: []byte(`
   162  				{
   163  					"vtgates": [
   164  						{
   165  							"host": {
   166  								"hostname": "127.0.0.1:11111"
   167  							},
   168  							"tags": ["cell:cellA"]
   169  						},
   170  						{
   171  							"host": {
   172  								"hostname": "127.0.0.1:22222"
   173  							},
   174  							"tags": ["cell:cellB"]
   175  						},
   176  						{
   177  							"host": {
   178  								"hostname": "127.0.0.1:33333"
   179  							},
   180  							"tags": ["cell:cellA"]
   181  						}
   182  					]
   183  				}
   184  			`),
   185  			tags: []string{"cell:cellA"},
   186  			expected: []*vtadminpb.VTGate{
   187  				{Hostname: "127.0.0.1:11111"},
   188  				{Hostname: "127.0.0.1:33333"},
   189  			},
   190  			shouldErr: false,
   191  		},
   192  		{
   193  			name: "filtered by multiple tags",
   194  			contents: []byte(`
   195  				{
   196  					"vtgates": [
   197  						{
   198  							"host": {
   199  								"hostname": "127.0.0.1:11111"
   200  							},
   201  							"tags": ["cell:cellA"]
   202  						},
   203  						{
   204  							"host": {
   205  								"hostname": "127.0.0.1:22222"
   206  							},
   207  							"tags": ["cell:cellA", "pool:poolZ"]
   208  						},
   209  						{
   210  							"host": {
   211  								"hostname": "127.0.0.1:33333"
   212  							},
   213  							"tags": ["pool:poolZ"]
   214  						}
   215  					]
   216  				}
   217  			`),
   218  			tags: []string{"cell:cellA", "pool:poolZ"},
   219  			expected: []*vtadminpb.VTGate{
   220  				{Hostname: "127.0.0.1:22222"},
   221  			},
   222  			shouldErr: false,
   223  		},
   224  		{
   225  			name: "invalid json",
   226  			contents: []byte(`
   227  				{
   228  					"vtgates": "malformed"
   229  				}
   230  			`),
   231  			tags:            []string{},
   232  			shouldErr:       false,
   233  			shouldErrConfig: true,
   234  		},
   235  	}
   236  
   237  	ctx := context.Background()
   238  
   239  	for _, tt := range tests {
   240  		tt := tt
   241  
   242  		t.Run(tt.name, func(t *testing.T) {
   243  			t.Parallel()
   244  
   245  			disco := &DynamicDiscovery{}
   246  
   247  			err := disco.parseConfig(tt.contents)
   248  			if tt.shouldErrConfig {
   249  				assert.Error(t, err)
   250  			} else {
   251  				require.NoError(t, err)
   252  			}
   253  
   254  			gates, err := disco.DiscoverVTGates(ctx, tt.tags)
   255  			if tt.shouldErr {
   256  				assert.Error(t, err)
   257  				return
   258  			}
   259  
   260  			assert.NoError(t, err)
   261  			assert.ElementsMatch(t, tt.expected, gates)
   262  		})
   263  	}
   264  }
   265  
   266  func TestDynamicDiscoverVtctld(t *testing.T) {
   267  	t.Parallel()
   268  
   269  	tests := []struct {
   270  		name      string
   271  		contents  []byte
   272  		expected  *vtadminpb.Vtctld
   273  		tags      []string
   274  		shouldErr bool
   275  	}{
   276  		{
   277  			name:      "empty config",
   278  			contents:  []byte(`{}`),
   279  			expected:  nil,
   280  			shouldErr: true,
   281  		},
   282  		{
   283  			name: "one vtctld",
   284  			contents: []byte(`
   285  				{
   286  					"vtctlds": [{
   287  						"host": {
   288  							"hostname": "127.0.0.1:12345"
   289  						}
   290  					}]
   291  				}
   292  			`),
   293  			expected: &vtadminpb.Vtctld{
   294  				Hostname: "127.0.0.1:12345",
   295  			},
   296  		},
   297  		{
   298  			name: "filtered by tags (one match)",
   299  			contents: []byte(`
   300  				{
   301  					"vtctlds": [
   302  						{
   303  							"host": {
   304  								"hostname": "127.0.0.1:11111"
   305  							},
   306  							"tags": ["cell:cellA"]
   307  						}, 
   308  						{
   309  							"host": {
   310  								"hostname": "127.0.0.1:22222"
   311  							},
   312  							"tags": ["cell:cellB"]
   313  						},
   314  						{
   315  							"host": {
   316  								"hostname": "127.0.0.1:33333"
   317  							},
   318  							"tags": ["cell:cellA"]
   319  						}
   320  					]
   321  				}
   322  			`),
   323  			expected: &vtadminpb.Vtctld{
   324  				Hostname: "127.0.0.1:22222",
   325  			},
   326  			tags: []string{"cell:cellB"},
   327  		},
   328  	}
   329  
   330  	ctx := context.Background()
   331  
   332  	for _, tt := range tests {
   333  		tt := tt
   334  
   335  		t.Run(tt.name, func(t *testing.T) {
   336  			t.Parallel()
   337  
   338  			disco := &DynamicDiscovery{}
   339  			err := disco.parseConfig(tt.contents)
   340  			require.NoError(t, err)
   341  
   342  			vtctld, err := disco.DiscoverVtctld(ctx, tt.tags)
   343  			if tt.shouldErr {
   344  				assert.Error(t, err)
   345  				return
   346  			}
   347  
   348  			assert.NoError(t, err)
   349  			assert.Equal(t, tt.expected, vtctld)
   350  		})
   351  	}
   352  }
   353  
   354  func TestDynamicDiscoverVtctlds(t *testing.T) {
   355  	t.Parallel()
   356  
   357  	tests := []struct {
   358  		name     string
   359  		contents []byte
   360  		tags     []string
   361  		expected []*vtadminpb.Vtctld
   362  		// True if the test should produce an error on the DiscoverVTGates call
   363  		shouldErr bool
   364  		// True if the test should produce an error on the disco.parseConfig step
   365  		shouldErrConfig bool
   366  	}{
   367  		{
   368  			name:      "empty config",
   369  			contents:  []byte(`{}`),
   370  			expected:  []*vtadminpb.Vtctld{},
   371  			shouldErr: false,
   372  		},
   373  		{
   374  			name: "no tags",
   375  			contents: []byte(`
   376  				{
   377  					"vtctlds": [
   378  						{
   379  							"host": {
   380  								"hostname": "127.0.0.1:12345"
   381  							}
   382  						},
   383  						{
   384  							"host": {
   385  								"hostname": "127.0.0.1:67890"
   386  							}
   387  						}
   388  					]
   389  				}
   390  			`),
   391  			expected: []*vtadminpb.Vtctld{
   392  				{Hostname: "127.0.0.1:12345"},
   393  				{Hostname: "127.0.0.1:67890"},
   394  			},
   395  			shouldErr: false,
   396  		},
   397  		{
   398  			name: "filtered by tags",
   399  			contents: []byte(`
   400  				{
   401  					"vtctlds": [
   402  						{
   403  							"host": {
   404  								"hostname": "127.0.0.1:11111"
   405  							},
   406  							"tags": ["cell:cellA"]
   407  						},
   408  						{
   409  							"host": {
   410  								"hostname": "127.0.0.1:22222"
   411  							},
   412  							"tags": ["cell:cellB"]
   413  						},
   414  						{
   415  							"host": {
   416  								"hostname": "127.0.0.1:33333"
   417  							},
   418  							"tags": ["cell:cellA"]
   419  						}
   420  					]
   421  				}
   422  			`),
   423  			tags: []string{"cell:cellA"},
   424  			expected: []*vtadminpb.Vtctld{
   425  				{Hostname: "127.0.0.1:11111"},
   426  				{Hostname: "127.0.0.1:33333"},
   427  			},
   428  			shouldErr: false,
   429  		},
   430  		{
   431  			name: "filtered by multiple tags",
   432  			contents: []byte(`
   433  				{
   434  					"vtctlds": [
   435  						{
   436  							"host": {
   437  								"hostname": "127.0.0.1:11111"
   438  							},
   439  							"tags": ["cell:cellA"]
   440  						},
   441  						{
   442  							"host": {
   443  								"hostname": "127.0.0.1:22222"
   444  							},
   445  							"tags": ["cell:cellA", "pool:poolZ"]
   446  						},
   447  						{
   448  							"host": {
   449  								"hostname": "127.0.0.1:33333"
   450  							},
   451  							"tags": ["pool:poolZ"]
   452  						}
   453  					]
   454  				}
   455  			`),
   456  			tags: []string{"cell:cellA", "pool:poolZ"},
   457  			expected: []*vtadminpb.Vtctld{
   458  				{Hostname: "127.0.0.1:22222"},
   459  			},
   460  			shouldErr: false,
   461  		},
   462  		{
   463  			name: "invalid json",
   464  			contents: []byte(`
   465  				{
   466  					"vtctlds": "malformed"
   467  				}
   468  			`),
   469  			tags:            []string{},
   470  			shouldErr:       false,
   471  			shouldErrConfig: true,
   472  		},
   473  	}
   474  
   475  	ctx := context.Background()
   476  
   477  	for _, tt := range tests {
   478  		tt := tt
   479  
   480  		t.Run(tt.name, func(t *testing.T) {
   481  			t.Parallel()
   482  
   483  			disco := &DynamicDiscovery{}
   484  
   485  			err := disco.parseConfig(tt.contents)
   486  			if tt.shouldErrConfig {
   487  				assert.Error(t, err)
   488  			} else {
   489  				require.NoError(t, err)
   490  			}
   491  
   492  			vtctlds, err := disco.DiscoverVtctlds(ctx, tt.tags)
   493  			if tt.shouldErr {
   494  				assert.Error(t, err)
   495  				return
   496  			}
   497  
   498  			assert.NoError(t, err)
   499  			assert.ElementsMatch(t, tt.expected, vtctlds)
   500  		})
   501  	}
   502  }