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 }