github.com/clly/consul@v1.4.5/agent/sidecar_service_test.go (about)

     1  package agent
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/hashicorp/consul/agent/structs"
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func TestAgent_sidecarServiceFromNodeService(t *testing.T) {
    13  	tests := []struct {
    14  		name              string
    15  		maxPort           int
    16  		preRegister       *structs.ServiceDefinition
    17  		sd                *structs.ServiceDefinition
    18  		token             string
    19  		autoPortsDisabled bool
    20  		wantNS            *structs.NodeService
    21  		wantChecks        []*structs.CheckType
    22  		wantToken         string
    23  		wantErr           string
    24  	}{
    25  		{
    26  			name: "no sidecar",
    27  			sd: &structs.ServiceDefinition{
    28  				Name: "web",
    29  				Port: 1111,
    30  			},
    31  			token:      "foo",
    32  			wantNS:     nil,
    33  			wantChecks: nil,
    34  			wantToken:  "",
    35  			wantErr:    "", // Should NOT error
    36  		},
    37  		{
    38  			name: "all the defaults",
    39  			sd: &structs.ServiceDefinition{
    40  				ID:   "web1",
    41  				Name: "web",
    42  				Port: 1111,
    43  				Connect: &structs.ServiceConnect{
    44  					SidecarService: &structs.ServiceDefinition{},
    45  				},
    46  			},
    47  			token: "foo",
    48  			wantNS: &structs.NodeService{
    49  				Kind:                       structs.ServiceKindConnectProxy,
    50  				ID:                         "web1-sidecar-proxy",
    51  				Service:                    "web-sidecar-proxy",
    52  				Port:                       2222,
    53  				LocallyRegisteredAsSidecar: true,
    54  				Proxy: structs.ConnectProxyConfig{
    55  					DestinationServiceName: "web",
    56  					DestinationServiceID:   "web1",
    57  					LocalServiceAddress:    "127.0.0.1",
    58  					LocalServicePort:       1111,
    59  				},
    60  			},
    61  			wantChecks: []*structs.CheckType{
    62  				&structs.CheckType{
    63  					Name:     "Connect Sidecar Listening",
    64  					TCP:      "127.0.0.1:2222",
    65  					Interval: 10 * time.Second,
    66  				},
    67  				&structs.CheckType{
    68  					Name:         "Connect Sidecar Aliasing web1",
    69  					AliasService: "web1",
    70  				},
    71  			},
    72  			wantToken: "foo",
    73  		},
    74  		{
    75  			name: "all the allowed overrides",
    76  			sd: &structs.ServiceDefinition{
    77  				ID:   "web1",
    78  				Name: "web",
    79  				Port: 1111,
    80  				Tags: []string{"baz"},
    81  				Meta: map[string]string{"foo": "baz"},
    82  				Connect: &structs.ServiceConnect{
    83  					SidecarService: &structs.ServiceDefinition{
    84  						Name:    "motorbike1",
    85  						Port:    3333,
    86  						Tags:    []string{"foo", "bar"},
    87  						Address: "127.127.127.127",
    88  						Meta:    map[string]string{"foo": "bar"},
    89  						Check: structs.CheckType{
    90  							ScriptArgs: []string{"sleep", "1"},
    91  							Interval:   999 * time.Second,
    92  						},
    93  						Token:             "custom-token",
    94  						EnableTagOverride: true,
    95  						Proxy: &structs.ConnectProxyConfig{
    96  							DestinationServiceName: "web",
    97  							DestinationServiceID:   "web1",
    98  							LocalServiceAddress:    "127.0.127.0",
    99  							LocalServicePort:       9999,
   100  							Config:                 map[string]interface{}{"baz": "qux"},
   101  							Upstreams:              structs.TestUpstreams(t),
   102  						},
   103  					},
   104  				},
   105  			},
   106  			token: "foo",
   107  			wantNS: &structs.NodeService{
   108  				Kind:    structs.ServiceKindConnectProxy,
   109  				ID:      "web1-sidecar-proxy",
   110  				Service: "motorbike1",
   111  				Port:    3333,
   112  				Tags:    []string{"foo", "bar"},
   113  				Address: "127.127.127.127",
   114  				Meta: map[string]string{
   115  					"foo": "bar",
   116  				},
   117  				LocallyRegisteredAsSidecar: true,
   118  				EnableTagOverride:          true,
   119  				Proxy: structs.ConnectProxyConfig{
   120  					DestinationServiceName: "web",
   121  					DestinationServiceID:   "web1",
   122  					LocalServiceAddress:    "127.0.127.0",
   123  					LocalServicePort:       9999,
   124  					Config:                 map[string]interface{}{"baz": "qux"},
   125  					Upstreams:              structs.TestAddDefaultsToUpstreams(t, structs.TestUpstreams(t)),
   126  				},
   127  			},
   128  			wantChecks: []*structs.CheckType{
   129  				&structs.CheckType{
   130  					ScriptArgs: []string{"sleep", "1"},
   131  					Interval:   999 * time.Second,
   132  				},
   133  			},
   134  			wantToken: "custom-token",
   135  		},
   136  		{
   137  			name: "no auto ports available",
   138  			// register another sidecar consuming our 1 and only allocated auto port.
   139  			preRegister: &structs.ServiceDefinition{
   140  				Kind: structs.ServiceKindConnectProxy,
   141  				Name: "api-proxy-sidecar",
   142  				Port: 2222, // Consume the one available auto-port
   143  				Proxy: &structs.ConnectProxyConfig{
   144  					DestinationServiceName: "api",
   145  				},
   146  			},
   147  			sd: &structs.ServiceDefinition{
   148  				ID:   "web1",
   149  				Name: "web",
   150  				Port: 1111,
   151  				Connect: &structs.ServiceConnect{
   152  					SidecarService: &structs.ServiceDefinition{},
   153  				},
   154  			},
   155  			token:   "foo",
   156  			wantErr: "none left in the configured range [2222, 2222]",
   157  		},
   158  		{
   159  			name:              "auto ports disabled",
   160  			autoPortsDisabled: true,
   161  			sd: &structs.ServiceDefinition{
   162  				ID:   "web1",
   163  				Name: "web",
   164  				Port: 1111,
   165  				Connect: &structs.ServiceConnect{
   166  					SidecarService: &structs.ServiceDefinition{},
   167  				},
   168  			},
   169  			token:   "foo",
   170  			wantErr: "auto-assignment disabled in config",
   171  		},
   172  		{
   173  			name: "inherit tags and meta",
   174  			sd: &structs.ServiceDefinition{
   175  				ID:   "web1",
   176  				Name: "web",
   177  				Port: 1111,
   178  				Tags: []string{"foo"},
   179  				Meta: map[string]string{"foo": "bar"},
   180  				Connect: &structs.ServiceConnect{
   181  					SidecarService: &structs.ServiceDefinition{},
   182  				},
   183  			},
   184  			wantNS: &structs.NodeService{
   185  				Kind:                       structs.ServiceKindConnectProxy,
   186  				ID:                         "web1-sidecar-proxy",
   187  				Service:                    "web-sidecar-proxy",
   188  				Port:                       2222,
   189  				Tags:                       []string{"foo"},
   190  				Meta:                       map[string]string{"foo": "bar"},
   191  				LocallyRegisteredAsSidecar: true,
   192  				Proxy: structs.ConnectProxyConfig{
   193  					DestinationServiceName: "web",
   194  					DestinationServiceID:   "web1",
   195  					LocalServiceAddress:    "127.0.0.1",
   196  					LocalServicePort:       1111,
   197  				},
   198  			},
   199  			wantChecks: []*structs.CheckType{
   200  				&structs.CheckType{
   201  					Name:     "Connect Sidecar Listening",
   202  					TCP:      "127.0.0.1:2222",
   203  					Interval: 10 * time.Second,
   204  				},
   205  				&structs.CheckType{
   206  					Name:         "Connect Sidecar Aliasing web1",
   207  					AliasService: "web1",
   208  				},
   209  			},
   210  		},
   211  		{
   212  			name: "invalid check type",
   213  			sd: &structs.ServiceDefinition{
   214  				ID:   "web1",
   215  				Name: "web",
   216  				Port: 1111,
   217  				Connect: &structs.ServiceConnect{
   218  					SidecarService: &structs.ServiceDefinition{
   219  						Check: structs.CheckType{
   220  							TCP: "foo",
   221  							// Invalid since no interval specified
   222  						},
   223  					},
   224  				},
   225  			},
   226  			token:   "foo",
   227  			wantErr: "Interval must be > 0",
   228  		},
   229  		{
   230  			name: "invalid meta",
   231  			sd: &structs.ServiceDefinition{
   232  				ID:   "web1",
   233  				Name: "web",
   234  				Port: 1111,
   235  				Connect: &structs.ServiceConnect{
   236  					SidecarService: &structs.ServiceDefinition{
   237  						Meta: map[string]string{
   238  							"consul-reserved-key-should-be-rejected": "true",
   239  						},
   240  					},
   241  				},
   242  			},
   243  			token:   "foo",
   244  			wantErr: "reserved for internal use",
   245  		},
   246  		{
   247  			name: "re-registering same sidecar with no port should pick same one",
   248  			// Allow multiple ports to be sure we get the right one
   249  			maxPort: 2500,
   250  			// Pre register the sidecar we want
   251  			preRegister: &structs.ServiceDefinition{
   252  				Kind: structs.ServiceKindConnectProxy,
   253  				ID:   "web1-sidecar-proxy",
   254  				Name: "web-sidecar-proxy",
   255  				Port: 2222,
   256  				Proxy: &structs.ConnectProxyConfig{
   257  					DestinationServiceName: "web",
   258  					DestinationServiceID:   "web1",
   259  					LocalServiceAddress:    "127.0.0.1",
   260  					LocalServicePort:       1111,
   261  				},
   262  			},
   263  			// Register same again but with different service port
   264  			sd: &structs.ServiceDefinition{
   265  				ID:   "web1",
   266  				Name: "web",
   267  				Port: 1112,
   268  				Connect: &structs.ServiceConnect{
   269  					SidecarService: &structs.ServiceDefinition{},
   270  				},
   271  			},
   272  			token: "foo",
   273  			wantNS: &structs.NodeService{
   274  				Kind:                       structs.ServiceKindConnectProxy,
   275  				ID:                         "web1-sidecar-proxy",
   276  				Service:                    "web-sidecar-proxy",
   277  				Port:                       2222, // Should claim the same port as before
   278  				LocallyRegisteredAsSidecar: true,
   279  				Proxy: structs.ConnectProxyConfig{
   280  					DestinationServiceName: "web",
   281  					DestinationServiceID:   "web1",
   282  					LocalServiceAddress:    "127.0.0.1",
   283  					LocalServicePort:       1112,
   284  				},
   285  			},
   286  			wantChecks: []*structs.CheckType{
   287  				&structs.CheckType{
   288  					Name:     "Connect Sidecar Listening",
   289  					TCP:      "127.0.0.1:2222",
   290  					Interval: 10 * time.Second,
   291  				},
   292  				&structs.CheckType{
   293  					Name:         "Connect Sidecar Aliasing web1",
   294  					AliasService: "web1",
   295  				},
   296  			},
   297  			wantToken: "foo",
   298  		},
   299  	}
   300  	for _, tt := range tests {
   301  		t.Run(tt.name, func(t *testing.T) {
   302  			// Set port range to be tiny (one availabl) to test consuming all of it.
   303  			// This allows a single assigned port at 2222 thanks to being inclusive at
   304  			// both ends.
   305  			if tt.maxPort == 0 {
   306  				tt.maxPort = 2222
   307  			}
   308  			hcl := fmt.Sprintf(`
   309  			ports {
   310  				sidecar_min_port = 2222
   311  				sidecar_max_port = %d
   312  			}
   313  			`, tt.maxPort)
   314  			if tt.autoPortsDisabled {
   315  				hcl = `
   316  				ports {
   317  					sidecar_min_port = 0
   318  					sidecar_max_port = 0
   319  				}
   320  				`
   321  			}
   322  
   323  			require := require.New(t)
   324  			a := NewTestAgent(t, "jones", hcl)
   325  
   326  			if tt.preRegister != nil {
   327  				err := a.AddService(tt.preRegister.NodeService(), nil, false, "", ConfigSourceLocal)
   328  				require.NoError(err)
   329  			}
   330  
   331  			ns := tt.sd.NodeService()
   332  			err := ns.Validate()
   333  			require.NoError(err, "Invalid test case - NodeService must validate")
   334  
   335  			gotNS, gotChecks, gotToken, err := a.sidecarServiceFromNodeService(ns, tt.token)
   336  			if tt.wantErr != "" {
   337  				require.Error(err)
   338  				require.Contains(err.Error(), tt.wantErr)
   339  				return
   340  			}
   341  
   342  			require.NoError(err)
   343  			require.Equal(tt.wantNS, gotNS)
   344  			require.Equal(tt.wantChecks, gotChecks)
   345  			require.Equal(tt.wantToken, gotToken)
   346  		})
   347  	}
   348  }