github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/command/agent/consul/connect_test.go (about)

     1  package consul
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	"github.com/hashicorp/consul/api"
     8  	"github.com/hashicorp/nomad/helper"
     9  	"github.com/hashicorp/nomad/nomad/structs"
    10  	"github.com/stretchr/testify/require"
    11  )
    12  
    13  var (
    14  	testConnectNetwork = structs.Networks{{
    15  		Mode:   "bridge",
    16  		Device: "eth0",
    17  		IP:     "192.168.30.1",
    18  		DynamicPorts: []structs.Port{
    19  			{Label: "healthPort", Value: 23100, To: 23100},
    20  			{Label: "metricsPort", Value: 23200, To: 23200},
    21  			{Label: "connect-proxy-redis", Value: 3000, To: 3000},
    22  		},
    23  	}}
    24  	testConnectPorts = structs.AllocatedPorts{{
    25  		Label:  "connect-proxy-redis",
    26  		Value:  3000,
    27  		To:     3000,
    28  		HostIP: "192.168.30.1",
    29  	}}
    30  )
    31  
    32  func TestConnect_newConnect(t *testing.T) {
    33  	t.Parallel()
    34  
    35  	t.Run("nil", func(t *testing.T) {
    36  		asr, err := newConnect("", "", nil, nil, nil)
    37  		require.NoError(t, err)
    38  		require.Nil(t, asr)
    39  	})
    40  
    41  	t.Run("native", func(t *testing.T) {
    42  		asr, err := newConnect("", "", &structs.ConsulConnect{
    43  			Native: true,
    44  		}, nil, nil)
    45  		require.NoError(t, err)
    46  		require.True(t, asr.Native)
    47  		require.Nil(t, asr.SidecarService)
    48  	})
    49  
    50  	t.Run("with sidecar", func(t *testing.T) {
    51  		asr, err := newConnect("redis-service-id", "redis", &structs.ConsulConnect{
    52  			Native: false,
    53  			SidecarService: &structs.ConsulSidecarService{
    54  				Tags: []string{"foo", "bar"},
    55  				Port: "connect-proxy-redis",
    56  			},
    57  		}, testConnectNetwork, testConnectPorts)
    58  		require.NoError(t, err)
    59  		require.Equal(t, &api.AgentServiceRegistration{
    60  			Tags:    []string{"foo", "bar"},
    61  			Port:    3000,
    62  			Address: "192.168.30.1",
    63  			Proxy: &api.AgentServiceConnectProxyConfig{
    64  				Config: map[string]interface{}{
    65  					"bind_address": "0.0.0.0",
    66  					"bind_port":    3000,
    67  				},
    68  			},
    69  			Checks: api.AgentServiceChecks{
    70  				{
    71  					Name:     "Connect Sidecar Listening",
    72  					TCP:      "192.168.30.1:3000",
    73  					Interval: "10s",
    74  				},
    75  				{
    76  					Name:         "Connect Sidecar Aliasing redis-service-id",
    77  					AliasService: "redis-service-id",
    78  				},
    79  			},
    80  		}, asr.SidecarService)
    81  	})
    82  }
    83  
    84  func TestConnect_connectSidecarRegistration(t *testing.T) {
    85  	t.Parallel()
    86  
    87  	t.Run("nil", func(t *testing.T) {
    88  		sidecarReg, err := connectSidecarRegistration("", nil, testConnectNetwork, testConnectPorts)
    89  		require.NoError(t, err)
    90  		require.Nil(t, sidecarReg)
    91  	})
    92  
    93  	t.Run("no service port", func(t *testing.T) {
    94  		_, err := connectSidecarRegistration("unknown-id", &structs.ConsulSidecarService{
    95  			Port: "unknown-label",
    96  		}, testConnectNetwork, testConnectPorts)
    97  		require.EqualError(t, err, `No port of label "unknown-label" defined`)
    98  	})
    99  
   100  	t.Run("bad proxy", func(t *testing.T) {
   101  		_, err := connectSidecarRegistration("redis-service-id", &structs.ConsulSidecarService{
   102  			Port: "connect-proxy-redis",
   103  			Proxy: &structs.ConsulProxy{
   104  				Expose: &structs.ConsulExposeConfig{
   105  					Paths: []structs.ConsulExposePath{{
   106  						ListenerPort: "badPort",
   107  					}},
   108  				},
   109  			},
   110  		}, testConnectNetwork, testConnectPorts)
   111  		require.EqualError(t, err, `No port of label "badPort" defined`)
   112  	})
   113  
   114  	t.Run("normal", func(t *testing.T) {
   115  		proxy, err := connectSidecarRegistration("redis-service-id", &structs.ConsulSidecarService{
   116  			Tags: []string{"foo", "bar"},
   117  			Port: "connect-proxy-redis",
   118  		}, testConnectNetwork, testConnectPorts)
   119  		require.NoError(t, err)
   120  		require.Equal(t, &api.AgentServiceRegistration{
   121  			Tags:    []string{"foo", "bar"},
   122  			Port:    3000,
   123  			Address: "192.168.30.1",
   124  			Proxy: &api.AgentServiceConnectProxyConfig{
   125  				Config: map[string]interface{}{
   126  					"bind_address": "0.0.0.0",
   127  					"bind_port":    3000,
   128  				},
   129  			},
   130  			Checks: api.AgentServiceChecks{
   131  				{
   132  					Name:     "Connect Sidecar Listening",
   133  					TCP:      "192.168.30.1:3000",
   134  					Interval: "10s",
   135  				},
   136  				{
   137  					Name:         "Connect Sidecar Aliasing redis-service-id",
   138  					AliasService: "redis-service-id",
   139  				},
   140  			},
   141  		}, proxy)
   142  	})
   143  }
   144  
   145  func TestConnect_connectProxy(t *testing.T) {
   146  	t.Parallel()
   147  
   148  	// If the input proxy is nil, we expect the output to be a proxy with its
   149  	// config set to default values.
   150  	t.Run("nil proxy", func(t *testing.T) {
   151  		proxy, err := connectSidecarProxy(nil, 2000, testConnectNetwork)
   152  		require.NoError(t, err)
   153  		require.Equal(t, &api.AgentServiceConnectProxyConfig{
   154  			LocalServiceAddress: "",
   155  			LocalServicePort:    0,
   156  			Upstreams:           nil,
   157  			Expose:              api.ExposeConfig{},
   158  			Config: map[string]interface{}{
   159  				"bind_address": "0.0.0.0",
   160  				"bind_port":    2000,
   161  			},
   162  		}, proxy)
   163  	})
   164  
   165  	t.Run("bad proxy", func(t *testing.T) {
   166  		_, err := connectSidecarProxy(&structs.ConsulProxy{
   167  			LocalServiceAddress: "0.0.0.0",
   168  			LocalServicePort:    2000,
   169  			Upstreams:           nil,
   170  			Expose: &structs.ConsulExposeConfig{
   171  				Paths: []structs.ConsulExposePath{{
   172  					ListenerPort: "badPort",
   173  				}},
   174  			},
   175  			Config: nil,
   176  		}, 2000, testConnectNetwork)
   177  		require.EqualError(t, err, `No port of label "badPort" defined`)
   178  	})
   179  
   180  	t.Run("normal", func(t *testing.T) {
   181  		proxy, err := connectSidecarProxy(&structs.ConsulProxy{
   182  			LocalServiceAddress: "0.0.0.0",
   183  			LocalServicePort:    2000,
   184  			Upstreams:           nil,
   185  			Expose: &structs.ConsulExposeConfig{
   186  				Paths: []structs.ConsulExposePath{{
   187  					Path:          "/health",
   188  					Protocol:      "http",
   189  					LocalPathPort: 8000,
   190  					ListenerPort:  "healthPort",
   191  				}},
   192  			},
   193  			Config: nil,
   194  		}, 2000, testConnectNetwork)
   195  		require.NoError(t, err)
   196  		require.Equal(t, &api.AgentServiceConnectProxyConfig{
   197  			LocalServiceAddress: "0.0.0.0",
   198  			LocalServicePort:    2000,
   199  			Upstreams:           nil,
   200  			Expose: api.ExposeConfig{
   201  				Paths: []api.ExposePath{{
   202  					Path:          "/health",
   203  					Protocol:      "http",
   204  					LocalPathPort: 8000,
   205  					ListenerPort:  23100,
   206  				}},
   207  			},
   208  			Config: map[string]interface{}{
   209  				"bind_address": "0.0.0.0",
   210  				"bind_port":    2000,
   211  			},
   212  		}, proxy)
   213  	})
   214  }
   215  
   216  func TestConnect_connectProxyExpose(t *testing.T) {
   217  	t.Parallel()
   218  
   219  	t.Run("nil", func(t *testing.T) {
   220  		exposeConfig, err := connectProxyExpose(nil, nil)
   221  		require.NoError(t, err)
   222  		require.Equal(t, api.ExposeConfig{}, exposeConfig)
   223  	})
   224  
   225  	t.Run("bad port", func(t *testing.T) {
   226  		_, err := connectProxyExpose(&structs.ConsulExposeConfig{
   227  			Paths: []structs.ConsulExposePath{{
   228  				ListenerPort: "badPort",
   229  			}},
   230  		}, testConnectNetwork)
   231  		require.EqualError(t, err, `No port of label "badPort" defined`)
   232  	})
   233  
   234  	t.Run("normal", func(t *testing.T) {
   235  		expose, err := connectProxyExpose(&structs.ConsulExposeConfig{
   236  			Paths: []structs.ConsulExposePath{{
   237  				Path:          "/health",
   238  				Protocol:      "http",
   239  				LocalPathPort: 8000,
   240  				ListenerPort:  "healthPort",
   241  			}},
   242  		}, testConnectNetwork)
   243  		require.NoError(t, err)
   244  		require.Equal(t, api.ExposeConfig{
   245  			Checks: false,
   246  			Paths: []api.ExposePath{{
   247  				Path:            "/health",
   248  				ListenerPort:    23100,
   249  				LocalPathPort:   8000,
   250  				Protocol:        "http",
   251  				ParsedFromCheck: false,
   252  			}},
   253  		}, expose)
   254  	})
   255  }
   256  
   257  func TestConnect_connectProxyExposePaths(t *testing.T) {
   258  	t.Parallel()
   259  
   260  	t.Run("nil", func(t *testing.T) {
   261  		upstreams, err := connectProxyExposePaths(nil, nil)
   262  		require.NoError(t, err)
   263  		require.Empty(t, upstreams)
   264  	})
   265  
   266  	t.Run("no network", func(t *testing.T) {
   267  		original := []structs.ConsulExposePath{{Path: "/path"}}
   268  		_, err := connectProxyExposePaths(original, nil)
   269  		require.EqualError(t, err, `Connect only supported with exactly 1 network (found 0)`)
   270  	})
   271  
   272  	t.Run("normal", func(t *testing.T) {
   273  		original := []structs.ConsulExposePath{{
   274  			Path:          "/health",
   275  			Protocol:      "http",
   276  			LocalPathPort: 8000,
   277  			ListenerPort:  "healthPort",
   278  		}, {
   279  			Path:          "/metrics",
   280  			Protocol:      "grpc",
   281  			LocalPathPort: 9500,
   282  			ListenerPort:  "metricsPort",
   283  		}}
   284  		exposePaths, err := connectProxyExposePaths(original, testConnectNetwork)
   285  		require.NoError(t, err)
   286  		require.Equal(t, []api.ExposePath{
   287  			{
   288  				Path:            "/health",
   289  				Protocol:        "http",
   290  				LocalPathPort:   8000,
   291  				ListenerPort:    23100,
   292  				ParsedFromCheck: false,
   293  			},
   294  			{
   295  				Path:            "/metrics",
   296  				Protocol:        "grpc",
   297  				LocalPathPort:   9500,
   298  				ListenerPort:    23200,
   299  				ParsedFromCheck: false,
   300  			},
   301  		}, exposePaths)
   302  	})
   303  }
   304  
   305  func TestConnect_connectUpstreams(t *testing.T) {
   306  	t.Parallel()
   307  
   308  	t.Run("nil", func(t *testing.T) {
   309  		require.Nil(t, connectUpstreams(nil))
   310  	})
   311  
   312  	t.Run("not empty", func(t *testing.T) {
   313  		require.Equal(t,
   314  			[]api.Upstream{{
   315  				DestinationName: "foo",
   316  				LocalBindPort:   8000,
   317  			}, {
   318  				DestinationName:  "bar",
   319  				LocalBindPort:    9000,
   320  				Datacenter:       "dc2",
   321  				LocalBindAddress: "127.0.0.2",
   322  			}},
   323  			connectUpstreams([]structs.ConsulUpstream{{
   324  				DestinationName: "foo",
   325  				LocalBindPort:   8000,
   326  			}, {
   327  				DestinationName:  "bar",
   328  				LocalBindPort:    9000,
   329  				Datacenter:       "dc2",
   330  				LocalBindAddress: "127.0.0.2",
   331  			}}),
   332  		)
   333  	})
   334  }
   335  
   336  func TestConnect_connectProxyConfig(t *testing.T) {
   337  	t.Parallel()
   338  
   339  	t.Run("nil map", func(t *testing.T) {
   340  		require.Equal(t, map[string]interface{}{
   341  			"bind_address": "0.0.0.0",
   342  			"bind_port":    42,
   343  		}, connectProxyConfig(nil, 42))
   344  	})
   345  
   346  	t.Run("pre-existing map", func(t *testing.T) {
   347  		require.Equal(t, map[string]interface{}{
   348  			"bind_address": "0.0.0.0",
   349  			"bind_port":    42,
   350  			"foo":          "bar",
   351  		}, connectProxyConfig(map[string]interface{}{
   352  			"foo": "bar",
   353  		}, 42))
   354  	})
   355  }
   356  
   357  func TestConnect_getConnectPort(t *testing.T) {
   358  	t.Parallel()
   359  
   360  	networks := structs.Networks{{
   361  		IP: "192.168.30.1",
   362  		DynamicPorts: []structs.Port{{
   363  			Label: "connect-proxy-foo",
   364  			Value: 23456,
   365  			To:    23456,
   366  		}}}}
   367  
   368  	ports := structs.AllocatedPorts{{
   369  		Label:  "foo",
   370  		Value:  23456,
   371  		To:     23456,
   372  		HostIP: "192.168.30.1",
   373  	}}
   374  
   375  	t.Run("normal", func(t *testing.T) {
   376  		nr, err := connectPort("foo", networks, ports)
   377  		require.NoError(t, err)
   378  		require.Equal(t, structs.AllocatedPortMapping{
   379  			Label:  "foo",
   380  			Value:  23456,
   381  			To:     23456,
   382  			HostIP: "192.168.30.1",
   383  		}, nr)
   384  	})
   385  
   386  	t.Run("no such service", func(t *testing.T) {
   387  		_, err := connectPort("other", networks, ports)
   388  		require.EqualError(t, err, `No port of label "other" defined`)
   389  	})
   390  
   391  	t.Run("no network", func(t *testing.T) {
   392  		_, err := connectPort("foo", nil, nil)
   393  		require.EqualError(t, err, "Connect only supported with exactly 1 network (found 0)")
   394  	})
   395  
   396  	t.Run("multi network", func(t *testing.T) {
   397  		_, err := connectPort("foo", append(networks, &structs.NetworkResource{
   398  			Device: "eth1",
   399  			IP:     "10.0.10.0",
   400  		}), nil)
   401  		require.EqualError(t, err, "Connect only supported with exactly 1 network (found 2)")
   402  	})
   403  }
   404  
   405  func TestConnect_getExposePathPort(t *testing.T) {
   406  	t.Parallel()
   407  
   408  	networks := structs.Networks{{
   409  		Device: "eth0",
   410  		IP:     "192.168.30.1",
   411  		DynamicPorts: []structs.Port{{
   412  			Label: "myPort",
   413  			Value: 23456,
   414  			To:    23456,
   415  		}}}}
   416  
   417  	t.Run("normal", func(t *testing.T) {
   418  		ip, port, err := connectExposePathPort("myPort", networks)
   419  		require.NoError(t, err)
   420  		require.Equal(t, ip, "192.168.30.1")
   421  		require.Equal(t, 23456, port)
   422  	})
   423  
   424  	t.Run("no such port label", func(t *testing.T) {
   425  		_, _, err := connectExposePathPort("otherPort", networks)
   426  		require.EqualError(t, err, `No port of label "otherPort" defined`)
   427  	})
   428  
   429  	t.Run("no network", func(t *testing.T) {
   430  		_, _, err := connectExposePathPort("myPort", nil)
   431  		require.EqualError(t, err, "Connect only supported with exactly 1 network (found 0)")
   432  	})
   433  
   434  	t.Run("multi network", func(t *testing.T) {
   435  		_, _, err := connectExposePathPort("myPort", append(networks, &structs.NetworkResource{
   436  			Device: "eth1",
   437  			IP:     "10.0.10.0",
   438  		}))
   439  		require.EqualError(t, err, "Connect only supported with exactly 1 network (found 2)")
   440  	})
   441  }
   442  
   443  func TestConnect_newConnectGateway(t *testing.T) {
   444  	t.Parallel()
   445  
   446  	t.Run("not a gateway", func(t *testing.T) {
   447  		result := newConnectGateway("s1", &structs.ConsulConnect{Native: true})
   448  		require.Nil(t, result)
   449  	})
   450  
   451  	t.Run("canonical empty", func(t *testing.T) {
   452  		result := newConnectGateway("s1", &structs.ConsulConnect{
   453  			Gateway: &structs.ConsulGateway{
   454  				Proxy: &structs.ConsulGatewayProxy{
   455  					ConnectTimeout:                  helper.TimeToPtr(1 * time.Second),
   456  					EnvoyGatewayBindTaggedAddresses: false,
   457  					EnvoyGatewayBindAddresses:       nil,
   458  					EnvoyGatewayNoDefaultBind:       false,
   459  					Config:                          nil,
   460  				},
   461  			},
   462  		})
   463  		require.Equal(t, &api.AgentServiceConnectProxyConfig{
   464  			Config: map[string]interface{}{
   465  				"connect_timeout_ms": int64(1000),
   466  			},
   467  		}, result)
   468  	})
   469  
   470  	t.Run("proxy undefined", func(t *testing.T) {
   471  		result := newConnectGateway("s1", &structs.ConsulConnect{
   472  			Gateway: &structs.ConsulGateway{
   473  				Proxy: nil,
   474  			},
   475  		})
   476  		require.Equal(t, &api.AgentServiceConnectProxyConfig{
   477  			Config: nil,
   478  		}, result)
   479  	})
   480  
   481  	t.Run("full", func(t *testing.T) {
   482  		result := newConnectGateway("s1", &structs.ConsulConnect{
   483  			Gateway: &structs.ConsulGateway{
   484  				Proxy: &structs.ConsulGatewayProxy{
   485  					ConnectTimeout:                  helper.TimeToPtr(1 * time.Second),
   486  					EnvoyGatewayBindTaggedAddresses: true,
   487  					EnvoyGatewayBindAddresses: map[string]*structs.ConsulGatewayBindAddress{
   488  						"service1": &structs.ConsulGatewayBindAddress{
   489  							Address: "10.0.0.1",
   490  							Port:    2000,
   491  						},
   492  					},
   493  					EnvoyGatewayNoDefaultBind: true,
   494  					EnvoyDNSDiscoveryType:     "STRICT_DNS",
   495  					Config: map[string]interface{}{
   496  						"foo": 1,
   497  					},
   498  				},
   499  			},
   500  		})
   501  		require.Equal(t, &api.AgentServiceConnectProxyConfig{
   502  			Config: map[string]interface{}{
   503  				"connect_timeout_ms":                  int64(1000),
   504  				"envoy_gateway_bind_tagged_addresses": true,
   505  				"envoy_gateway_bind_addresses": map[string]*structs.ConsulGatewayBindAddress{
   506  					"service1": &structs.ConsulGatewayBindAddress{
   507  						Address: "10.0.0.1",
   508  						Port:    2000,
   509  					},
   510  				},
   511  				"envoy_gateway_no_default_bind": true,
   512  				"envoy_dns_discovery_type":      "STRICT_DNS",
   513  				"foo":                           1,
   514  			},
   515  		}, result)
   516  	})
   517  }