github.com/mongey/nomad@v0.5.2/command/agent/consul/syncer_test.go (about) 1 package consul 2 3 import ( 4 "io/ioutil" 5 "log" 6 "net" 7 "os" 8 "reflect" 9 "testing" 10 "time" 11 12 "github.com/hashicorp/consul/api" 13 "github.com/hashicorp/consul/testutil" 14 "github.com/hashicorp/nomad/nomad/structs" 15 "github.com/hashicorp/nomad/nomad/structs/config" 16 ) 17 18 const ( 19 allocID = "12" 20 serviceRegPrefix = "test" 21 serviceGroupName = "executor" 22 ) 23 24 var logger = log.New(os.Stdout, "", log.LstdFlags) 25 26 func TestSyncNow(t *testing.T) { 27 cs, testconsul := testConsul(t) 28 defer cs.Shutdown() 29 defer testconsul.Stop() 30 31 cs.SetAddrFinder(func(h string) (string, int) { 32 a, pstr, _ := net.SplitHostPort(h) 33 p, _ := net.LookupPort("tcp", pstr) 34 return a, p 35 }) 36 cs.syncInterval = 9000 * time.Hour 37 38 service := &structs.Service{Name: "foo1", Tags: []string{"a", "b"}} 39 services := map[ServiceKey]*structs.Service{ 40 GenerateServiceKey(service): service, 41 } 42 43 // Run syncs once on startup and then blocks forever 44 go cs.Run() 45 46 if err := cs.SetServices(serviceGroupName, services); err != nil { 47 t.Fatalf("error setting services: %v", err) 48 } 49 50 synced := false 51 for i := 0; !synced && i < 10; i++ { 52 time.Sleep(250 * time.Millisecond) 53 agentServices, err := cs.queryAgentServices() 54 if err != nil { 55 t.Fatalf("error querying consul services: %v", err) 56 } 57 synced = len(agentServices) == 1 58 } 59 if !synced { 60 t.Fatalf("initial sync never occurred") 61 } 62 63 // SetServices again should cause another sync 64 service1 := &structs.Service{Name: "foo1", Tags: []string{"Y", "Z"}} 65 service2 := &structs.Service{Name: "bar"} 66 services = map[ServiceKey]*structs.Service{ 67 GenerateServiceKey(service1): service1, 68 GenerateServiceKey(service2): service2, 69 } 70 71 if err := cs.SetServices(serviceGroupName, services); err != nil { 72 t.Fatalf("error setting services: %v", err) 73 } 74 75 synced = false 76 for i := 0; !synced && i < 10; i++ { 77 time.Sleep(250 * time.Millisecond) 78 agentServices, err := cs.queryAgentServices() 79 if err != nil { 80 t.Fatalf("error querying consul services: %v", err) 81 } 82 synced = len(agentServices) == 2 83 } 84 if !synced { 85 t.Fatalf("SetServices didn't sync immediately") 86 } 87 } 88 89 func TestCheckRegistration(t *testing.T) { 90 cs, err := NewSyncer(config.DefaultConsulConfig(), make(chan struct{}), logger) 91 if err != nil { 92 t.Fatalf("Err: %v", err) 93 } 94 95 check1 := structs.ServiceCheck{ 96 Name: "check-foo-1", 97 Type: structs.ServiceCheckTCP, 98 Interval: 30 * time.Second, 99 Timeout: 5 * time.Second, 100 InitialStatus: api.HealthPassing, 101 } 102 check2 := structs.ServiceCheck{ 103 Name: "check1", 104 Type: "tcp", 105 PortLabel: "port2", 106 Interval: 3 * time.Second, 107 Timeout: 1 * time.Second, 108 } 109 check3 := structs.ServiceCheck{ 110 Name: "check3", 111 Type: "http", 112 PortLabel: "port3", 113 Path: "/health?p1=1&p2=2", 114 Interval: 3 * time.Second, 115 Timeout: 1 * time.Second, 116 } 117 service1 := structs.Service{ 118 Name: "foo-1", 119 Tags: []string{"tag1", "tag2"}, 120 PortLabel: "port1", 121 Checks: []*structs.ServiceCheck{ 122 &check1, &check2, 123 }, 124 } 125 task := structs.Task{ 126 Name: "foo", 127 Services: []*structs.Service{&service1}, 128 Resources: &structs.Resources{ 129 Networks: []*structs.NetworkResource{ 130 &structs.NetworkResource{ 131 IP: "10.10.11.5", 132 DynamicPorts: []structs.Port{ 133 structs.Port{ 134 Label: "port1", 135 Value: 20002, 136 }, 137 structs.Port{ 138 Label: "port2", 139 Value: 20003, 140 }, 141 structs.Port{ 142 Label: "port3", 143 Value: 20004, 144 }, 145 }, 146 }, 147 }, 148 }, 149 } 150 cs.SetAddrFinder(task.FindHostAndPortFor) 151 srvReg, _ := cs.createService(&service1, "domain", "key") 152 check1Reg, _ := cs.createCheckReg(&check1, srvReg) 153 check2Reg, _ := cs.createCheckReg(&check2, srvReg) 154 check3Reg, _ := cs.createCheckReg(&check3, srvReg) 155 156 expected := "10.10.11.5:20002" 157 if check1Reg.TCP != expected { 158 t.Fatalf("expected: %v, actual: %v", expected, check1Reg.TCP) 159 } 160 161 expected = "10.10.11.5:20003" 162 if check2Reg.TCP != expected { 163 t.Fatalf("expected: %v, actual: %v", expected, check2Reg.TCP) 164 } 165 166 expected = "http://10.10.11.5:20004/health?p1=1&p2=2" 167 if check3Reg.HTTP != expected { 168 t.Fatalf("expected: %v, actual: %v", expected, check3Reg.HTTP) 169 } 170 171 expected = api.HealthPassing 172 if check1Reg.Status != expected { 173 t.Fatalf("expected: %v, actual: %v", expected, check1Reg.Status) 174 } 175 } 176 177 // testConsul returns a Syncer configured with an embedded Consul server. 178 // 179 // Callers must defer Syncer.Shutdown() and TestServer.Stop() 180 // 181 func testConsul(t *testing.T) (*Syncer, *testutil.TestServer) { 182 // Create an embedded Consul server 183 testconsul := testutil.NewTestServerConfig(t, func(c *testutil.TestServerConfig) { 184 // If -v wasn't specified squelch consul logging 185 if !testing.Verbose() { 186 c.Stdout = ioutil.Discard 187 c.Stderr = ioutil.Discard 188 } 189 }) 190 191 // Configure Syncer to talk to the test server 192 cconf := config.DefaultConsulConfig() 193 cconf.Addr = testconsul.HTTPAddr 194 195 cs, err := NewSyncer(cconf, nil, logger) 196 if err != nil { 197 t.Fatalf("Error creating Syncer: %v", err) 198 } 199 return cs, testconsul 200 } 201 202 func TestConsulServiceRegisterServices(t *testing.T) { 203 cs, testconsul := testConsul(t) 204 defer cs.Shutdown() 205 defer testconsul.Stop() 206 207 service1 := &structs.Service{Name: "foo", Tags: []string{"a", "b"}} 208 service2 := &structs.Service{Name: "foo"} 209 services := map[ServiceKey]*structs.Service{ 210 GenerateServiceKey(service1): service1, 211 GenerateServiceKey(service2): service2, 212 } 213 214 // Call SetServices to update services in consul 215 if err := cs.SetServices(serviceGroupName, services); err != nil { 216 t.Fatalf("error setting services: %v", err) 217 } 218 219 // Manually call SyncServers to cause a synchronous consul update 220 if err := cs.SyncServices(); err != nil { 221 t.Fatalf("error syncing services: %v", err) 222 } 223 224 numservices := len(cs.flattenedServices()) 225 if numservices != 2 { 226 t.Fatalf("expected 2 services but found %d", numservices) 227 } 228 229 numchecks := len(cs.flattenedChecks()) 230 if numchecks != 0 { 231 t.Fatalf("expected 0 checks but found %d", numchecks) 232 } 233 234 // Assert services are in consul 235 agentServices, err := cs.client.Agent().Services() 236 if err != nil { 237 t.Fatalf("error querying consul services: %v", err) 238 } 239 found := 0 240 for id, as := range agentServices { 241 if id == "consul" { 242 found++ 243 continue 244 } 245 if _, ok := services[ServiceKey(as.Service)]; ok { 246 found++ 247 continue 248 } 249 t.Errorf("unexpected service in consul: %s", id) 250 } 251 if found != 3 { 252 t.Fatalf("expected 3 services in consul but found %d:\nconsul: %#v", len(agentServices), agentServices) 253 } 254 255 agentChecks, err := cs.queryChecks() 256 if err != nil { 257 t.Fatalf("error querying consul checks: %v", err) 258 } 259 if len(agentChecks) != numchecks { 260 t.Fatalf("expected %d checks in consul but found %d:\n%#v", numservices, len(agentChecks), agentChecks) 261 } 262 } 263 264 func TestConsulServiceUpdateService(t *testing.T) { 265 cs, testconsul := testConsul(t) 266 defer cs.Shutdown() 267 defer testconsul.Stop() 268 269 cs.SetAddrFinder(func(h string) (string, int) { 270 a, pstr, _ := net.SplitHostPort(h) 271 p, _ := net.LookupPort("tcp", pstr) 272 return a, p 273 }) 274 275 service1 := &structs.Service{Name: "foo1", Tags: []string{"a", "b"}} 276 service2 := &structs.Service{Name: "foo2"} 277 services := map[ServiceKey]*structs.Service{ 278 GenerateServiceKey(service1): service1, 279 GenerateServiceKey(service2): service2, 280 } 281 if err := cs.SetServices(serviceGroupName, services); err != nil { 282 t.Fatalf("error setting services: %v", err) 283 } 284 if err := cs.SyncServices(); err != nil { 285 t.Fatalf("error syncing services: %v", err) 286 } 287 288 // Now update both services 289 service1 = &structs.Service{Name: "foo1", Tags: []string{"a", "z"}} 290 service2 = &structs.Service{Name: "foo2", PortLabel: ":8899"} 291 service3 := &structs.Service{Name: "foo3"} 292 services = map[ServiceKey]*structs.Service{ 293 GenerateServiceKey(service1): service1, 294 GenerateServiceKey(service2): service2, 295 GenerateServiceKey(service3): service3, 296 } 297 if err := cs.SetServices(serviceGroupName, services); err != nil { 298 t.Fatalf("error setting services: %v", err) 299 } 300 if err := cs.SyncServices(); err != nil { 301 t.Fatalf("error syncing services: %v", err) 302 } 303 304 agentServices, err := cs.queryAgentServices() 305 if err != nil { 306 t.Fatalf("error querying consul services: %v", err) 307 } 308 if len(agentServices) != 3 { 309 t.Fatalf("expected 3 services in consul but found %d:\n%#v", len(agentServices), agentServices) 310 } 311 consulServices := make(map[string]*api.AgentService, 3) 312 for _, as := range agentServices { 313 consulServices[as.ID] = as 314 } 315 316 found := 0 317 for _, s := range cs.flattenedServices() { 318 // Assert sure changes were applied to internal state 319 switch s.Name { 320 case "foo1": 321 found++ 322 if !reflect.DeepEqual(service1.Tags, s.Tags) { 323 t.Errorf("incorrect tags on foo1:\n expected: %v\n found: %v", service1.Tags, s.Tags) 324 } 325 case "foo2": 326 found++ 327 if s.Address != "" { 328 t.Errorf("expected empty host on foo2 but found %q", s.Address) 329 } 330 if s.Port != 8899 { 331 t.Errorf("expected port 8899 on foo2 but found %d", s.Port) 332 } 333 case "foo3": 334 found++ 335 default: 336 t.Errorf("unexpected service: %s", s.Name) 337 } 338 339 // Assert internal state equals consul's state 340 cs, ok := consulServices[s.ID] 341 if !ok { 342 t.Errorf("service not in consul: %s id: %s", s.Name, s.ID) 343 continue 344 } 345 if !reflect.DeepEqual(s.Tags, cs.Tags) { 346 t.Errorf("mismatched tags in syncer state and consul for %s:\nsyncer: %v\nconsul: %v", s.Name, s.Tags, cs.Tags) 347 } 348 if cs.Port != s.Port { 349 t.Errorf("mismatched port in syncer state and consul for %s\nsyncer: %v\nconsul: %v", s.Name, s.Port, cs.Port) 350 } 351 if cs.Address != s.Address { 352 t.Errorf("mismatched address in syncer state and consul for %s\nsyncer: %v\nconsul: %v", s.Name, s.Address, cs.Address) 353 } 354 } 355 if found != 3 { 356 t.Fatalf("expected 3 services locally but found %d", found) 357 } 358 }