Best Ginkgo code snippet using watch.Len
state_test.go
Source:state_test.go
...201 require.True(t, ok)202 require.Equal(t, expectedDatacenter, reqReal.Datacenter)203 require.NotNil(t, reqReal.Match)204 require.Equal(t, structs.IntentionMatchDestination, reqReal.Match.Type)205 require.Len(t, reqReal.Match.Entries, 1)206 require.Equal(t, structs.IntentionDefaultNamespace, reqReal.Match.Entries[0].Namespace)207 require.Equal(t, expectedService, reqReal.Match.Entries[0].Name)208 }209}210func genVerifyPreparedQueryWatch(expectedName string, expectedDatacenter string) verifyWatchRequest {211 return func(t testing.TB, cacheType string, request cache.Request) {212 require.Equal(t, cachetype.PreparedQueryName, cacheType)213 reqReal, ok := request.(*structs.PreparedQueryExecuteRequest)214 require.True(t, ok)215 require.Equal(t, expectedDatacenter, reqReal.Datacenter)216 require.Equal(t, expectedName, reqReal.QueryIDOrName)217 require.Equal(t, true, reqReal.Connect)218 }219}220func genVerifyDiscoveryChainWatch(expected *structs.DiscoveryChainRequest) verifyWatchRequest {221 return func(t testing.TB, cacheType string, request cache.Request) {222 require.Equal(t, cachetype.CompiledDiscoveryChainName, cacheType)223 reqReal, ok := request.(*structs.DiscoveryChainRequest)224 require.True(t, ok)225 require.Equal(t, expected, reqReal)226 }227}228func genVerifyGatewayWatch(expectedDatacenter string) verifyWatchRequest {229 return func(t testing.TB, cacheType string, request cache.Request) {230 require.Equal(t, cachetype.InternalServiceDumpName, cacheType)231 reqReal, ok := request.(*structs.ServiceDumpRequest)232 require.True(t, ok)233 require.Equal(t, expectedDatacenter, reqReal.Datacenter)234 require.True(t, reqReal.UseServiceKind)235 require.Equal(t, structs.ServiceKindMeshGateway, reqReal.ServiceKind)236 require.Equal(t, structs.DefaultEnterpriseMeta(), &reqReal.EnterpriseMeta)237 }238}239func genVerifyServiceSpecificRequest(expectedCacheType, expectedService, expectedFilter, expectedDatacenter string, connect bool) verifyWatchRequest {240 return func(t testing.TB, cacheType string, request cache.Request) {241 require.Equal(t, expectedCacheType, cacheType)242 reqReal, ok := request.(*structs.ServiceSpecificRequest)243 require.True(t, ok)244 require.Equal(t, expectedDatacenter, reqReal.Datacenter)245 require.Equal(t, expectedService, reqReal.ServiceName)246 require.Equal(t, expectedFilter, reqReal.QueryOptions.Filter)247 require.Equal(t, connect, reqReal.Connect)248 }249}250func genVerifyServiceWatch(expectedService, expectedFilter, expectedDatacenter string, connect bool) verifyWatchRequest {251 return genVerifyServiceSpecificRequest(cachetype.HealthServicesName, expectedService, expectedFilter, expectedDatacenter, connect)252}253// This test is meant to exercise the various parts of the cache watching done by the state as254// well as its management of the ConfigSnapshot255//256// This test is expressly not calling Watch which in turn would execute the run function in a go257// routine. This allows the test to be fully synchronous and deterministic while still being able258// to validate the logic of most of the watching and state updating.259//260// The general strategy here is to261//262// 1. Initialize a state with a call to newState + setting some of the extra stuff like the CacheNotifier263// We will not be using the CacheNotifier to send notifications but calling handleUpdate ourselves264// 2. Iterate through a list of verification stages performing validation and updates for each.265// a. Ensure that the required watches are in place and validate they are correct266// b. Process a bunch of UpdateEvents by calling handleUpdate267// c. Validate that the ConfigSnapshot has been updated appropriately268func TestState_WatchesAndUpdates(t *testing.T) {269 t.Parallel()270 indexedRoots, issuedCert := TestCerts(t)271 rootWatchEvent := func() cache.UpdateEvent {272 return cache.UpdateEvent{273 CorrelationID: rootsWatchID,274 Result: indexedRoots,275 Err: nil,276 }277 }278 type verificationStage struct {279 requiredWatches map[string]verifyWatchRequest280 events []cache.UpdateEvent281 verifySnapshot func(t testing.TB, snap *ConfigSnapshot)282 }283 type testCase struct {284 // the state to operate on. the logger, source, cache,285 // ctx and cancel fields will be filled in by the test286 ns structs.NodeService287 sourceDC string288 stages []verificationStage289 }290 newConnectProxyCase := func(meshGatewayProxyConfigValue structs.MeshGatewayMode) testCase {291 ns := structs.NodeService{292 Kind: structs.ServiceKindConnectProxy,293 ID: "web-sidecar-proxy",294 Service: "web-sidecar-proxy",295 Address: "10.0.1.1",296 Port: 443,297 Proxy: structs.ConnectProxyConfig{298 DestinationServiceName: "web",299 Upstreams: structs.Upstreams{300 structs.Upstream{301 DestinationType: structs.UpstreamDestTypePreparedQuery,302 DestinationName: "query",303 LocalBindPort: 10001,304 },305 structs.Upstream{306 DestinationType: structs.UpstreamDestTypeService,307 DestinationName: "api",308 LocalBindPort: 10002,309 },310 structs.Upstream{311 DestinationType: structs.UpstreamDestTypeService,312 DestinationName: "api-failover-remote",313 Datacenter: "dc2",314 LocalBindPort: 10003,315 MeshGateway: structs.MeshGatewayConfig{316 Mode: structs.MeshGatewayModeRemote,317 },318 },319 structs.Upstream{320 DestinationType: structs.UpstreamDestTypeService,321 DestinationName: "api-failover-local",322 Datacenter: "dc2",323 LocalBindPort: 10004,324 MeshGateway: structs.MeshGatewayConfig{325 Mode: structs.MeshGatewayModeLocal,326 },327 },328 structs.Upstream{329 DestinationType: structs.UpstreamDestTypeService,330 DestinationName: "api-failover-direct",331 Datacenter: "dc2",332 LocalBindPort: 10005,333 MeshGateway: structs.MeshGatewayConfig{334 Mode: structs.MeshGatewayModeNone,335 },336 },337 structs.Upstream{338 DestinationType: structs.UpstreamDestTypeService,339 DestinationName: "api-dc2",340 LocalBindPort: 10006,341 },342 },343 },344 }345 if meshGatewayProxyConfigValue != structs.MeshGatewayModeDefault {346 ns.Proxy.MeshGateway.Mode = meshGatewayProxyConfigValue347 }348 stage0 := verificationStage{349 requiredWatches: map[string]verifyWatchRequest{350 rootsWatchID: genVerifyRootsWatch("dc1"),351 leafWatchID: genVerifyLeafWatch("web", "dc1"),352 intentionsWatchID: genVerifyIntentionWatch("web", "dc1"),353 "upstream:prepared_query:query": genVerifyPreparedQueryWatch("query", "dc1"),354 "discovery-chain:api": genVerifyDiscoveryChainWatch(&structs.DiscoveryChainRequest{355 Name: "api",356 EvaluateInDatacenter: "dc1",357 EvaluateInNamespace: "default",358 Datacenter: "dc1",359 OverrideMeshGateway: structs.MeshGatewayConfig{360 Mode: meshGatewayProxyConfigValue,361 },362 }),363 "discovery-chain:api-failover-remote?dc=dc2": genVerifyDiscoveryChainWatch(&structs.DiscoveryChainRequest{364 Name: "api-failover-remote",365 EvaluateInDatacenter: "dc2",366 EvaluateInNamespace: "default",367 Datacenter: "dc1",368 OverrideMeshGateway: structs.MeshGatewayConfig{369 Mode: structs.MeshGatewayModeRemote,370 },371 }),372 "discovery-chain:api-failover-local?dc=dc2": genVerifyDiscoveryChainWatch(&structs.DiscoveryChainRequest{373 Name: "api-failover-local",374 EvaluateInDatacenter: "dc2",375 EvaluateInNamespace: "default",376 Datacenter: "dc1",377 OverrideMeshGateway: structs.MeshGatewayConfig{378 Mode: structs.MeshGatewayModeLocal,379 },380 }),381 "discovery-chain:api-failover-direct?dc=dc2": genVerifyDiscoveryChainWatch(&structs.DiscoveryChainRequest{382 Name: "api-failover-direct",383 EvaluateInDatacenter: "dc2",384 EvaluateInNamespace: "default",385 Datacenter: "dc1",386 OverrideMeshGateway: structs.MeshGatewayConfig{387 Mode: structs.MeshGatewayModeNone,388 },389 }),390 "discovery-chain:api-dc2": genVerifyDiscoveryChainWatch(&structs.DiscoveryChainRequest{391 Name: "api-dc2",392 EvaluateInDatacenter: "dc1",393 EvaluateInNamespace: "default",394 Datacenter: "dc1",395 OverrideMeshGateway: structs.MeshGatewayConfig{396 Mode: meshGatewayProxyConfigValue,397 },398 }),399 },400 events: []cache.UpdateEvent{401 rootWatchEvent(),402 cache.UpdateEvent{403 CorrelationID: leafWatchID,404 Result: issuedCert,405 Err: nil,406 },407 cache.UpdateEvent{408 CorrelationID: "discovery-chain:api",409 Result: &structs.DiscoveryChainResponse{410 Chain: discoverychain.TestCompileConfigEntries(t, "api", "default", "dc1", "trustdomain.consul", "dc1",411 func(req *discoverychain.CompileRequest) {412 req.OverrideMeshGateway.Mode = meshGatewayProxyConfigValue413 }),414 },415 Err: nil,416 },417 cache.UpdateEvent{418 CorrelationID: "discovery-chain:api-failover-remote?dc=dc2",419 Result: &structs.DiscoveryChainResponse{420 Chain: discoverychain.TestCompileConfigEntries(t, "api-failover-remote", "default", "dc2", "trustdomain.consul", "dc1",421 func(req *discoverychain.CompileRequest) {422 req.OverrideMeshGateway.Mode = structs.MeshGatewayModeRemote423 }),424 },425 Err: nil,426 },427 cache.UpdateEvent{428 CorrelationID: "discovery-chain:api-failover-local?dc=dc2",429 Result: &structs.DiscoveryChainResponse{430 Chain: discoverychain.TestCompileConfigEntries(t, "api-failover-local", "default", "dc2", "trustdomain.consul", "dc1",431 func(req *discoverychain.CompileRequest) {432 req.OverrideMeshGateway.Mode = structs.MeshGatewayModeLocal433 }),434 },435 Err: nil,436 },437 cache.UpdateEvent{438 CorrelationID: "discovery-chain:api-failover-direct?dc=dc2",439 Result: &structs.DiscoveryChainResponse{440 Chain: discoverychain.TestCompileConfigEntries(t, "api-failover-direct", "default", "dc2", "trustdomain.consul", "dc1",441 func(req *discoverychain.CompileRequest) {442 req.OverrideMeshGateway.Mode = structs.MeshGatewayModeNone443 }),444 },445 Err: nil,446 },447 cache.UpdateEvent{448 CorrelationID: "discovery-chain:api-dc2",449 Result: &structs.DiscoveryChainResponse{450 Chain: discoverychain.TestCompileConfigEntries(t, "api-dc2", "default", "dc1", "trustdomain.consul", "dc1",451 func(req *discoverychain.CompileRequest) {452 req.OverrideMeshGateway.Mode = meshGatewayProxyConfigValue453 },454 &structs.ServiceResolverConfigEntry{455 Kind: structs.ServiceResolver,456 Name: "api-dc2",457 Redirect: &structs.ServiceResolverRedirect{458 Service: "api",459 Datacenter: "dc2",460 },461 },462 ),463 },464 Err: nil,465 },466 },467 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {468 require.True(t, snap.Valid())469 require.True(t, snap.MeshGateway.IsEmpty())470 require.Equal(t, indexedRoots, snap.Roots)471 require.Equal(t, issuedCert, snap.ConnectProxy.Leaf)472 require.Len(t, snap.ConnectProxy.DiscoveryChain, 5, "%+v", snap.ConnectProxy.DiscoveryChain)473 require.Len(t, snap.ConnectProxy.WatchedUpstreams, 5, "%+v", snap.ConnectProxy.WatchedUpstreams)474 require.Len(t, snap.ConnectProxy.WatchedUpstreamEndpoints, 5, "%+v", snap.ConnectProxy.WatchedUpstreamEndpoints)475 require.Len(t, snap.ConnectProxy.WatchedGateways, 5, "%+v", snap.ConnectProxy.WatchedGateways)476 require.Len(t, snap.ConnectProxy.WatchedGatewayEndpoints, 5, "%+v", snap.ConnectProxy.WatchedGatewayEndpoints)477 require.Len(t, snap.ConnectProxy.WatchedServiceChecks, 0, "%+v", snap.ConnectProxy.WatchedServiceChecks)478 require.Len(t, snap.ConnectProxy.PreparedQueryEndpoints, 0, "%+v", snap.ConnectProxy.PreparedQueryEndpoints)479 },480 }481 stage1 := verificationStage{482 requiredWatches: map[string]verifyWatchRequest{483 "upstream-target:api.default.dc1:api": genVerifyServiceWatch("api", "", "dc1", true),484 "upstream-target:api-failover-remote.default.dc2:api-failover-remote?dc=dc2": genVerifyServiceWatch("api-failover-remote", "", "dc2", true),485 "upstream-target:api-failover-local.default.dc2:api-failover-local?dc=dc2": genVerifyServiceWatch("api-failover-local", "", "dc2", true),486 "upstream-target:api-failover-direct.default.dc2:api-failover-direct?dc=dc2": genVerifyServiceWatch("api-failover-direct", "", "dc2", true),487 "mesh-gateway:dc2:api-failover-remote?dc=dc2": genVerifyGatewayWatch("dc2"),488 "mesh-gateway:dc1:api-failover-local?dc=dc2": genVerifyGatewayWatch("dc1"),489 },490 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {491 require.True(t, snap.Valid())492 require.True(t, snap.MeshGateway.IsEmpty())493 require.Equal(t, indexedRoots, snap.Roots)494 require.Equal(t, issuedCert, snap.ConnectProxy.Leaf)495 require.Len(t, snap.ConnectProxy.DiscoveryChain, 5, "%+v", snap.ConnectProxy.DiscoveryChain)496 require.Len(t, snap.ConnectProxy.WatchedUpstreams, 5, "%+v", snap.ConnectProxy.WatchedUpstreams)497 require.Len(t, snap.ConnectProxy.WatchedUpstreamEndpoints, 5, "%+v", snap.ConnectProxy.WatchedUpstreamEndpoints)498 require.Len(t, snap.ConnectProxy.WatchedGateways, 5, "%+v", snap.ConnectProxy.WatchedGateways)499 require.Len(t, snap.ConnectProxy.WatchedGatewayEndpoints, 5, "%+v", snap.ConnectProxy.WatchedGatewayEndpoints)500 require.Len(t, snap.ConnectProxy.WatchedServiceChecks, 0, "%+v", snap.ConnectProxy.WatchedServiceChecks)501 require.Len(t, snap.ConnectProxy.PreparedQueryEndpoints, 0, "%+v", snap.ConnectProxy.PreparedQueryEndpoints)502 },503 }504 if meshGatewayProxyConfigValue == structs.MeshGatewayModeLocal {505 stage1.requiredWatches["mesh-gateway:dc1:api-dc2"] = genVerifyGatewayWatch("dc1")506 }507 return testCase{508 ns: ns,509 sourceDC: "dc1",510 stages: []verificationStage{stage0, stage1},511 }512 }513 // Used in terminating-gateway cases to account for differences in OSS/ent implementations of ServiceID.String()514 db := structs.NewServiceID("db", nil)515 dbStr := db.String()516 cases := map[string]testCase{517 "initial-gateway": testCase{518 ns: structs.NodeService{519 Kind: structs.ServiceKindMeshGateway,520 ID: "mesh-gateway",521 Service: "mesh-gateway",522 Address: "10.0.1.1",523 Port: 443,524 },525 sourceDC: "dc1",526 stages: []verificationStage{527 verificationStage{528 requiredWatches: map[string]verifyWatchRequest{529 rootsWatchID: genVerifyRootsWatch("dc1"),530 serviceListWatchID: genVerifyListServicesWatch("dc1"),531 datacentersWatchID: verifyDatacentersWatch,532 },533 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {534 require.False(t, snap.Valid(), "gateway without root is not valid")535 require.True(t, snap.ConnectProxy.IsEmpty())536 },537 },538 verificationStage{539 events: []cache.UpdateEvent{540 rootWatchEvent(),541 },542 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {543 require.False(t, snap.Valid(), "gateway without services is valid")544 require.True(t, snap.ConnectProxy.IsEmpty())545 require.Equal(t, indexedRoots, snap.Roots)546 require.Empty(t, snap.MeshGateway.WatchedServices)547 require.False(t, snap.MeshGateway.WatchedServicesSet)548 require.Empty(t, snap.MeshGateway.WatchedDatacenters)549 require.Empty(t, snap.MeshGateway.ServiceGroups)550 require.Empty(t, snap.MeshGateway.ServiceResolvers)551 require.Empty(t, snap.MeshGateway.GatewayGroups)552 },553 },554 verificationStage{555 events: []cache.UpdateEvent{556 cache.UpdateEvent{557 CorrelationID: serviceListWatchID,558 Result: &structs.IndexedServiceList{559 Services: make(structs.ServiceList, 0),560 },561 Err: nil,562 },563 },564 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {565 require.True(t, snap.Valid(), "gateway with empty service list is valid")566 require.True(t, snap.ConnectProxy.IsEmpty())567 require.Equal(t, indexedRoots, snap.Roots)568 require.Empty(t, snap.MeshGateway.WatchedServices)569 require.True(t, snap.MeshGateway.WatchedServicesSet)570 require.Empty(t, snap.MeshGateway.WatchedDatacenters)571 require.Empty(t, snap.MeshGateway.ServiceGroups)572 require.Empty(t, snap.MeshGateway.ServiceResolvers)573 require.Empty(t, snap.MeshGateway.GatewayGroups)574 },575 },576 },577 },578 "mesh-gateway-do-not-cancel-service-watches": testCase{579 ns: structs.NodeService{580 Kind: structs.ServiceKindMeshGateway,581 ID: "mesh-gateway",582 Service: "mesh-gateway",583 Address: "10.0.1.1",584 Port: 443,585 },586 sourceDC: "dc1",587 stages: []verificationStage{588 verificationStage{589 requiredWatches: map[string]verifyWatchRequest{590 rootsWatchID: genVerifyRootsWatch("dc1"),591 serviceListWatchID: genVerifyListServicesWatch("dc1"),592 datacentersWatchID: verifyDatacentersWatch,593 },594 events: []cache.UpdateEvent{595 rootWatchEvent(),596 cache.UpdateEvent{597 CorrelationID: serviceListWatchID,598 Result: &structs.IndexedServiceList{599 Services: structs.ServiceList{600 {Name: "web"},601 },602 },603 Err: nil,604 },605 },606 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {607 require.True(t, snap.Valid(), "gateway with service list is valid")608 require.Len(t, snap.MeshGateway.WatchedServices, 1)609 require.True(t, snap.MeshGateway.WatchedServicesSet)610 },611 },612 verificationStage{613 events: []cache.UpdateEvent{614 cache.UpdateEvent{615 CorrelationID: serviceListWatchID,616 Result: &structs.IndexedServiceList{617 Services: structs.ServiceList{618 {Name: "web"},619 {Name: "api"},620 },621 },622 Err: nil,623 },624 },625 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {626 require.True(t, snap.Valid(), "gateway with service list is valid")627 require.Len(t, snap.MeshGateway.WatchedServices, 2)628 require.True(t, snap.MeshGateway.WatchedServicesSet)629 },630 },631 },632 },633 "ingress-gateway": testCase{634 ns: structs.NodeService{635 Kind: structs.ServiceKindIngressGateway,636 ID: "ingress-gateway",637 Service: "ingress-gateway",638 Address: "10.0.1.1",639 },640 sourceDC: "dc1",641 stages: []verificationStage{642 verificationStage{643 requiredWatches: map[string]verifyWatchRequest{644 rootsWatchID: genVerifyRootsWatch("dc1"),645 leafWatchID: genVerifyLeafWatch("ingress-gateway", "dc1"),646 },647 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {648 require.False(t, snap.Valid(), "gateway without root is not valid")649 require.True(t, snap.IngressGateway.IsEmpty())650 },651 },652 verificationStage{653 events: []cache.UpdateEvent{654 rootWatchEvent(),655 },656 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {657 require.False(t, snap.Valid(), "gateway without leaf is not valid")658 require.Equal(t, indexedRoots, snap.Roots)659 },660 },661 verificationStage{662 events: []cache.UpdateEvent{663 cache.UpdateEvent{664 CorrelationID: leafWatchID,665 Result: issuedCert,666 Err: nil,667 },668 },669 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {670 require.True(t, snap.Valid(), "gateway with root and leaf certs is valid")671 require.Equal(t, issuedCert, snap.IngressGateway.Leaf)672 },673 },674 verificationStage{675 events: []cache.UpdateEvent{676 cache.UpdateEvent{677 CorrelationID: gatewayServicesWatchID,678 Result: &structs.IndexedGatewayServices{679 Services: structs.GatewayServices{680 {681 Gateway: structs.NewServiceID("ingress-gateway", nil),682 Service: structs.NewServiceID("api", nil),683 Port: 9999,684 Protocol: "http",685 },686 },687 },688 Err: nil,689 },690 },691 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {692 require.Len(t, snap.IngressGateway.Upstreams, 1)693 key := IngressListenerKey{Protocol: "http", Port: 9999}694 require.Equal(t, snap.IngressGateway.Upstreams[key], structs.Upstreams{695 {696 DestinationNamespace: "default",697 DestinationName: "api",698 LocalBindAddress: "10.0.1.1",699 LocalBindPort: 9999,700 Config: map[string]interface{}{701 "protocol": "http",702 },703 },704 })705 require.Len(t, snap.IngressGateway.WatchedDiscoveryChains, 1)706 require.Contains(t, snap.IngressGateway.WatchedDiscoveryChains, "api")707 },708 },709 verificationStage{710 requiredWatches: map[string]verifyWatchRequest{711 "discovery-chain:api": genVerifyDiscoveryChainWatch(&structs.DiscoveryChainRequest{712 Name: "api",713 EvaluateInDatacenter: "dc1",714 EvaluateInNamespace: "default",715 Datacenter: "dc1",716 }),717 },718 events: []cache.UpdateEvent{719 cache.UpdateEvent{720 CorrelationID: "discovery-chain:api",721 Result: &structs.DiscoveryChainResponse{722 Chain: discoverychain.TestCompileConfigEntries(t, "api", "default", "dc1", "trustdomain.consul", "dc1", nil),723 },724 Err: nil,725 },726 },727 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {728 require.Len(t, snap.IngressGateway.WatchedUpstreams, 1)729 require.Len(t, snap.IngressGateway.WatchedUpstreams["api"], 1)730 },731 },732 verificationStage{733 requiredWatches: map[string]verifyWatchRequest{734 "upstream-target:api.default.dc1:api": genVerifyServiceWatch("api", "", "dc1", true),735 },736 events: []cache.UpdateEvent{737 cache.UpdateEvent{738 CorrelationID: "upstream-target:api.default.dc1:api",739 Result: &structs.IndexedCheckServiceNodes{740 Nodes: structs.CheckServiceNodes{741 {742 Node: &structs.Node{743 Node: "node1",744 Address: "127.0.0.1",745 },746 Service: &structs.NodeService{747 ID: "api1",748 Service: "api",749 },750 },751 },752 },753 Err: nil,754 },755 },756 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {757 require.Len(t, snap.IngressGateway.WatchedUpstreamEndpoints, 1)758 require.Contains(t, snap.IngressGateway.WatchedUpstreamEndpoints, "api")759 require.Len(t, snap.IngressGateway.WatchedUpstreamEndpoints["api"], 1)760 require.Contains(t, snap.IngressGateway.WatchedUpstreamEndpoints["api"], "api.default.dc1")761 require.Equal(t, snap.IngressGateway.WatchedUpstreamEndpoints["api"]["api.default.dc1"],762 structs.CheckServiceNodes{763 {764 Node: &structs.Node{765 Node: "node1",766 Address: "127.0.0.1",767 },768 Service: &structs.NodeService{769 ID: "api1",770 Service: "api",771 },772 },773 },774 )775 },776 },777 },778 },779 "ingress-gateway-update-upstreams": testCase{780 ns: structs.NodeService{781 Kind: structs.ServiceKindIngressGateway,782 ID: "ingress-gateway",783 Service: "ingress-gateway",784 Address: "10.0.1.1",785 },786 sourceDC: "dc1",787 stages: []verificationStage{788 verificationStage{789 requiredWatches: map[string]verifyWatchRequest{790 rootsWatchID: genVerifyRootsWatch("dc1"),791 leafWatchID: genVerifyLeafWatch("ingress-gateway", "dc1"),792 },793 events: []cache.UpdateEvent{794 rootWatchEvent(),795 cache.UpdateEvent{796 CorrelationID: leafWatchID,797 Result: issuedCert,798 Err: nil,799 },800 cache.UpdateEvent{801 CorrelationID: gatewayServicesWatchID,802 Result: &structs.IndexedGatewayServices{803 Services: structs.GatewayServices{804 {805 Gateway: structs.NewServiceID("ingress-gateway", nil),806 Service: structs.NewServiceID("api", nil),807 Port: 9999,808 },809 },810 },811 Err: nil,812 },813 },814 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {815 require.True(t, snap.Valid())816 require.Len(t, snap.IngressGateway.Upstreams, 1)817 require.Len(t, snap.IngressGateway.WatchedDiscoveryChains, 1)818 require.Contains(t, snap.IngressGateway.WatchedDiscoveryChains, "api")819 },820 },821 verificationStage{822 requiredWatches: map[string]verifyWatchRequest{},823 events: []cache.UpdateEvent{824 cache.UpdateEvent{825 CorrelationID: gatewayServicesWatchID,826 Result: &structs.IndexedGatewayServices{},827 Err: nil,828 },829 },830 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {831 require.True(t, snap.Valid())832 require.Len(t, snap.IngressGateway.Upstreams, 0)833 require.Len(t, snap.IngressGateway.WatchedDiscoveryChains, 0)834 require.NotContains(t, snap.IngressGateway.WatchedDiscoveryChains, "api")835 },836 },837 },838 },839 "terminating-gateway-initial": testCase{840 ns: structs.NodeService{841 Kind: structs.ServiceKindTerminatingGateway,842 ID: "terminating-gateway",843 Service: "terminating-gateway",844 Address: "10.0.1.1",845 },846 sourceDC: "dc1",847 stages: []verificationStage{848 verificationStage{849 requiredWatches: map[string]verifyWatchRequest{850 rootsWatchID: genVerifyRootsWatch("dc1"),851 gatewayServicesWatchID: genVerifyServiceSpecificRequest(gatewayServicesWatchID,852 "terminating-gateway", "", "dc1", false),853 },854 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {855 require.False(t, snap.Valid(), "gateway without root is not valid")856 require.True(t, snap.ConnectProxy.IsEmpty())857 require.True(t, snap.MeshGateway.IsEmpty())858 require.True(t, snap.IngressGateway.IsEmpty())859 },860 },861 verificationStage{862 events: []cache.UpdateEvent{863 rootWatchEvent(),864 },865 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {866 require.True(t, snap.Valid(), "gateway without services is valid")867 require.True(t, snap.ConnectProxy.IsEmpty())868 require.True(t, snap.MeshGateway.IsEmpty())869 require.True(t, snap.IngressGateway.IsEmpty())870 require.True(t, snap.TerminatingGateway.IsEmpty())871 require.Equal(t, indexedRoots, snap.Roots)872 },873 },874 },875 },876 "terminating-gateway-handle-update": testCase{877 ns: structs.NodeService{878 Kind: structs.ServiceKindTerminatingGateway,879 ID: "terminating-gateway",880 Service: "terminating-gateway",881 Address: "10.0.1.1",882 },883 sourceDC: "dc1",884 stages: []verificationStage{885 verificationStage{886 requiredWatches: map[string]verifyWatchRequest{887 rootsWatchID: genVerifyRootsWatch("dc1"),888 gatewayServicesWatchID: genVerifyServiceSpecificRequest(gatewayServicesWatchID,889 "terminating-gateway", "", "dc1", false),890 },891 events: []cache.UpdateEvent{892 rootWatchEvent(),893 cache.UpdateEvent{894 CorrelationID: gatewayServicesWatchID,895 Result: &structs.IndexedGatewayServices{896 Services: structs.GatewayServices{897 {898 Service: structs.NewServiceID("db", nil),899 Gateway: structs.NewServiceID("terminating-gateway", nil),900 },901 },902 },903 Err: nil,904 },905 },906 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {907 require.True(t, snap.Valid(), "gateway with service list is valid")908 require.Len(t, snap.TerminatingGateway.WatchedServices, 1)909 },910 },911 verificationStage{912 events: []cache.UpdateEvent{913 cache.UpdateEvent{914 CorrelationID: gatewayServicesWatchID,915 Result: &structs.IndexedGatewayServices{916 Services: structs.GatewayServices{917 {918 Service: structs.NewServiceID("db", nil),919 Gateway: structs.NewServiceID("terminating-gateway", nil),920 },921 {922 Service: structs.NewServiceID("billing", nil),923 Gateway: structs.NewServiceID("terminating-gateway", nil),924 },925 },926 },927 Err: nil,928 },929 },930 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {931 db := structs.NewServiceID("db", nil)932 billing := structs.NewServiceID("billing", nil)933 require.True(t, snap.Valid(), "gateway with service list is valid")934 require.Len(t, snap.TerminatingGateway.WatchedServices, 2)935 require.Contains(t, snap.TerminatingGateway.WatchedServices, db)936 require.Contains(t, snap.TerminatingGateway.WatchedServices, billing)937 require.Len(t, snap.TerminatingGateway.WatchedIntentions, 2)938 require.Contains(t, snap.TerminatingGateway.WatchedIntentions, db)939 require.Contains(t, snap.TerminatingGateway.WatchedIntentions, billing)940 require.Len(t, snap.TerminatingGateway.WatchedLeaves, 2)941 require.Contains(t, snap.TerminatingGateway.WatchedLeaves, db)942 require.Contains(t, snap.TerminatingGateway.WatchedLeaves, billing)943 require.Len(t, snap.TerminatingGateway.WatchedResolvers, 2)944 require.Contains(t, snap.TerminatingGateway.WatchedResolvers, db)945 require.Contains(t, snap.TerminatingGateway.WatchedResolvers, billing)946 require.Len(t, snap.TerminatingGateway.GatewayServices, 2)947 require.Contains(t, snap.TerminatingGateway.GatewayServices, db)948 require.Contains(t, snap.TerminatingGateway.GatewayServices, billing)949 },950 },951 verificationStage{952 requiredWatches: map[string]verifyWatchRequest{953 "external-service:" + dbStr: genVerifyServiceWatch("db", "", "dc1", false),954 },955 events: []cache.UpdateEvent{956 cache.UpdateEvent{957 CorrelationID: "external-service:" + dbStr,958 Result: &structs.IndexedCheckServiceNodes{959 Nodes: structs.CheckServiceNodes{960 {961 Node: &structs.Node{962 Node: "node1",963 Address: "127.0.0.1",964 },965 Service: &structs.NodeService{966 ID: "db",967 Service: "db",968 },969 },970 },971 },972 Err: nil,973 },974 },975 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {976 require.Len(t, snap.TerminatingGateway.ServiceGroups, 1)977 require.Equal(t, snap.TerminatingGateway.ServiceGroups[structs.NewServiceID("db", nil)],978 structs.CheckServiceNodes{979 {980 Node: &structs.Node{981 Node: "node1",982 Address: "127.0.0.1",983 },984 Service: &structs.NodeService{985 ID: "db",986 Service: "db",987 },988 },989 },990 )991 },992 },993 verificationStage{994 requiredWatches: map[string]verifyWatchRequest{995 "service-leaf:" + dbStr: genVerifyLeafWatch("db", "dc1"),996 },997 events: []cache.UpdateEvent{998 cache.UpdateEvent{999 CorrelationID: "service-leaf:" + dbStr,1000 Result: issuedCert,1001 Err: nil,1002 },1003 },1004 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {1005 require.Equal(t, snap.TerminatingGateway.ServiceLeaves[structs.NewServiceID("db", nil)], issuedCert)1006 },1007 },1008 verificationStage{1009 requiredWatches: map[string]verifyWatchRequest{1010 "service-resolver:" + dbStr: genVerifyResolverWatch("db", "dc1", structs.ServiceResolver),1011 },1012 events: []cache.UpdateEvent{1013 cache.UpdateEvent{1014 CorrelationID: "service-resolver:" + dbStr,1015 Result: &structs.IndexedConfigEntries{1016 Kind: structs.ServiceResolver,1017 Entries: []structs.ConfigEntry{1018 &structs.ServiceResolverConfigEntry{1019 Name: "db",1020 Kind: structs.ServiceResolver,1021 Redirect: &structs.ServiceResolverRedirect{1022 Service: "db",1023 Datacenter: "dc2",1024 },1025 },1026 },1027 },1028 Err: nil,1029 },1030 },1031 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {1032 want := &structs.ServiceResolverConfigEntry{1033 Kind: structs.ServiceResolver,1034 Name: "db",1035 Redirect: &structs.ServiceResolverRedirect{1036 Service: "db",1037 Datacenter: "dc2",1038 },1039 }1040 require.Equal(t, want, snap.TerminatingGateway.ServiceResolvers[structs.NewServiceID("db", nil)])1041 },1042 },1043 verificationStage{1044 events: []cache.UpdateEvent{1045 cache.UpdateEvent{1046 CorrelationID: gatewayServicesWatchID,1047 Result: &structs.IndexedGatewayServices{1048 Services: structs.GatewayServices{1049 {1050 Service: structs.NewServiceID("billing", nil),1051 Gateway: structs.NewServiceID("terminating-gateway", nil),1052 },1053 },1054 },1055 Err: nil,1056 },1057 },1058 verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {1059 billing := structs.NewServiceID("billing", nil)1060 require.True(t, snap.Valid(), "gateway with service list is valid")1061 // All the watches should have been cancelled for db1062 require.Len(t, snap.TerminatingGateway.WatchedServices, 1)1063 require.Contains(t, snap.TerminatingGateway.WatchedServices, billing)1064 require.Len(t, snap.TerminatingGateway.WatchedIntentions, 1)1065 require.Contains(t, snap.TerminatingGateway.WatchedIntentions, billing)1066 require.Len(t, snap.TerminatingGateway.WatchedLeaves, 1)1067 require.Contains(t, snap.TerminatingGateway.WatchedLeaves, billing)1068 require.Len(t, snap.TerminatingGateway.WatchedResolvers, 1)1069 require.Contains(t, snap.TerminatingGateway.WatchedResolvers, billing)1070 require.Len(t, snap.TerminatingGateway.GatewayServices, 1)1071 require.Contains(t, snap.TerminatingGateway.GatewayServices, billing)1072 // There was no update event for billing's leaf/endpoints, so length is 01073 require.Len(t, snap.TerminatingGateway.ServiceGroups, 0)1074 require.Len(t, snap.TerminatingGateway.ServiceLeaves, 0)1075 require.Len(t, snap.TerminatingGateway.ServiceResolvers, 0)1076 },1077 },1078 },1079 },1080 "connect-proxy": newConnectProxyCase(structs.MeshGatewayModeDefault),1081 "connect-proxy-mesh-gateway-local": newConnectProxyCase(structs.MeshGatewayModeLocal),1082 }1083 for name, tc := range cases {1084 t.Run(name, func(t *testing.T) {1085 state, err := newState(&tc.ns, "")1086 // verify building the initial state worked1087 require.NoError(t, err)1088 require.NotNil(t, state)1089 // setup the test logger to use the t.Log...
logfetcher_test.go
Source:logfetcher_test.go
...161 )162 }163 // Now let us disconnect the second connection, we should receive any new164 // changes for watchResult2 (From watchFunc2) anymore165 currentWatchLen := len(watchResult)166 currentWatch2Len := len(watchResult2)167 remote2.Close()168 // wait so onDisconnect get recognized on Kite169 time.Sleep(time.Second)170 file.WriteString("Tail4\n")171 file.WriteString("Tail5\n")172 // wait so the watch function picked up the tail changes173 time.Sleep(time.Second)174 if currentWatch2Len != len(watchResult2) {175 t.Errorf("WatchFunc2 is still triggered, got %d should have %d", len(watchResult2), currentWatch2Len)176 }177 if currentWatchLen+2 != len(watchResult) {178 t.Errorf("WatchFunc2 is not triggered, got %d should have %d", len(watchResult), currentWatchLen+2)179 }180}181func TestTailOffset(t *testing.T) {182 t.Skip("this test is correct but prefetcher implementation is not: #10840")183 tmpDir, tmpFile, err := makeTempAndCopy("testdata/testfile1.txt")184 if err != nil {185 t.Fatal(err)186 }187 defer os.RemoveAll(tmpDir)188 var (189 offset = 3 // Read the last 3 lines of the file.190 linesC = make(chan string, 10)191 watchFunc = dnode.Callback(func(r *dnode.Partial) {192 linesC <- r.One().MustString()...
tree_recursive.go
Source:tree_recursive.go
1// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.2// Use of this source code is governed by the MIT license that can be3// found in the LICENSE file.4package notify5import "sync"6// watchAdd TODO(rjeczalik)7func watchAdd(nd node, c chan<- EventInfo, e Event) eventDiff {8 diff := nd.Watch.Add(c, e)9 if wp := nd.Child[""].Watch; len(wp) != 0 {10 e = wp.Total()11 diff[0] |= e12 diff[1] |= e13 if diff[0] == diff[1] {14 return none15 }16 }17 return diff18}19// watchAddInactive TODO(rjeczalik)20func watchAddInactive(nd node, c chan<- EventInfo, e Event) eventDiff {21 wp := nd.Child[""].Watch22 if wp == nil {23 wp = make(watchpoint)24 nd.Child[""] = node{Watch: wp}25 }26 diff := wp.Add(c, e)27 e = nd.Watch.Total()28 diff[0] |= e29 diff[1] |= e30 if diff[0] == diff[1] {31 return none32 }33 return diff34}35// watchCopy TODO(rjeczalik)36func watchCopy(src, dst node) {37 for c, e := range src.Watch {38 if c == nil {39 continue40 }41 watchAddInactive(dst, c, e)42 }43 if wpsrc := src.Child[""].Watch; len(wpsrc) != 0 {44 wpdst := dst.Child[""].Watch45 for c, e := range wpsrc {46 if c == nil {47 continue48 }49 wpdst.Add(c, e)50 }51 }52}53// watchDel TODO(rjeczalik)54func watchDel(nd node, c chan<- EventInfo, e Event) eventDiff {55 diff := nd.Watch.Del(c, e)56 if wp := nd.Child[""].Watch; len(wp) != 0 {57 diffInactive := wp.Del(c, e)58 e = wp.Total()59 // TODO(rjeczalik): add e if e != all?60 diff[0] |= diffInactive[0] | e61 diff[1] |= diffInactive[1] | e62 if diff[0] == diff[1] {63 return none64 }65 }66 return diff67}68// watchTotal TODO(rjeczalik)69func watchTotal(nd node) Event {70 e := nd.Watch.Total()71 if wp := nd.Child[""].Watch; len(wp) != 0 {72 e |= wp.Total()73 }74 return e75}76// watchIsRecursive TODO(rjeczalik)77func watchIsRecursive(nd node) bool {78 ok := nd.Watch.IsRecursive()79 // TODO(rjeczalik): add a test for len(wp) != 0 change the condition.80 if wp := nd.Child[""].Watch; len(wp) != 0 {81 // If a watchpoint holds inactive watchpoints, it means it's a parent82 // one, which is recursive by nature even though it may be not recursive83 // itself.84 ok = true85 }86 return ok87}88// recursiveTree TODO(rjeczalik)89type recursiveTree struct {90 rw sync.RWMutex // protects root91 root root92 // TODO(rjeczalik): merge watcher + recursiveWatcher after #5 and #693 w interface {94 watcher95 recursiveWatcher96 }97 c chan EventInfo98}99// newRecursiveTree TODO(rjeczalik)100func newRecursiveTree(w recursiveWatcher, c chan EventInfo) *recursiveTree {101 t := &recursiveTree{102 root: root{nd: newnode("")},103 w: struct {104 watcher105 recursiveWatcher106 }{w.(watcher), w},107 c: c,108 }109 go t.dispatch()110 return t111}112// dispatch TODO(rjeczalik)113func (t *recursiveTree) dispatch() {114 for ei := range t.c {115 dbgprintf("dispatching %v on %q", ei.Event(), ei.Path())116 go func(ei EventInfo) {117 nd, ok := node{}, false118 dir, base := split(ei.Path())119 fn := func(it node, isbase bool) error {120 if isbase {121 nd = it122 } else {123 it.Watch.Dispatch(ei, recursive)124 }125 return nil126 }127 t.rw.RLock()128 defer t.rw.RUnlock()129 // Notify recursive watchpoints found on the path.130 if err := t.root.WalkPath(dir, fn); err != nil {131 dbgprint("dispatch did not reach leaf:", err)132 return133 }134 // Notify parent watchpoint.135 nd.Watch.Dispatch(ei, 0)136 // If leaf watchpoint exists, notify it.137 if nd, ok = nd.Child[base]; ok {138 nd.Watch.Dispatch(ei, 0)139 }140 }(ei)141 }142}143// Watch TODO(rjeczalik)144func (t *recursiveTree) Watch(path string, c chan<- EventInfo, events ...Event) error {145 if c == nil {146 panic("notify: Watch using nil channel")147 }148 // Expanding with empty event set is a nop.149 if len(events) == 0 {150 return nil151 }152 path, isrec, err := cleanpath(path)153 if err != nil {154 return err155 }156 eventset := joinevents(events)157 if isrec {158 eventset |= recursive159 }160 t.rw.Lock()161 defer t.rw.Unlock()162 // case 1: cur is a child163 //164 // Look for parent watch which already covers the given path.165 parent := node{}166 self := false167 err = t.root.WalkPath(path, func(nd node, isbase bool) error {168 if watchTotal(nd) != 0 {169 parent = nd170 self = isbase171 return errSkip172 }173 return nil174 })175 cur := t.root.Add(path) // add after the walk, so it's less to traverse176 if err == nil && parent.Watch != nil {177 // Parent watch found. Register inactive watchpoint, so we have enough178 // information to shrink the eventset on eventual Stop.179 // return t.resetwatchpoint(parent, parent, c, eventset|inactive)180 var diff eventDiff181 if self {182 diff = watchAdd(cur, c, eventset)183 } else {184 diff = watchAddInactive(parent, c, eventset)185 }186 switch {187 case diff == none:188 // the parent watchpoint already covers requested subtree with its189 // eventset190 case diff[0] == 0:191 // TODO(rjeczalik): cleanup this panic after implementation is stable192 panic("dangling watchpoint: " + parent.Name)193 default:194 if isrec || watchIsRecursive(parent) {195 err = t.w.RecursiveRewatch(parent.Name, parent.Name, diff[0], diff[1])196 } else {197 err = t.w.Rewatch(parent.Name, diff[0], diff[1])198 }199 if err != nil {200 watchDel(parent, c, diff.Event())201 return err202 }203 watchAdd(cur, c, eventset)204 // TODO(rjeczalik): account top-most path for c205 return nil206 }207 if !self {208 watchAdd(cur, c, eventset)209 }210 return nil211 }212 // case 2: cur is new parent213 //214 // Look for children nodes, unwatch n-1 of them and rewatch the last one.215 var children []node216 fn := func(nd node) error {217 if len(nd.Watch) == 0 {218 return nil219 }220 children = append(children, nd)221 return errSkip222 }223 switch must(cur.Walk(fn)); len(children) {224 case 0:225 // no child watches, cur holds a new watch226 case 1:227 watchAdd(cur, c, eventset) // TODO(rjeczalik): update cache c subtree root?228 watchCopy(children[0], cur)229 err = t.w.RecursiveRewatch(children[0].Name, cur.Name, watchTotal(children[0]),230 watchTotal(cur))231 if err != nil {232 // Clean inactive watchpoint. The c chan did not exist before.233 cur.Child[""] = node{}234 delete(cur.Watch, c)235 return err236 }237 return nil238 default:239 watchAdd(cur, c, eventset)240 // Copy children inactive watchpoints to the new parent.241 for _, nd := range children {242 watchCopy(nd, cur)243 }244 // Watch parent subtree.245 if err = t.w.RecursiveWatch(cur.Name, watchTotal(cur)); err != nil {246 // Clean inactive watchpoint. The c chan did not exist before.247 cur.Child[""] = node{}248 delete(cur.Watch, c)249 return err250 }251 // Unwatch children subtrees.252 var e error253 for _, nd := range children {254 if watchIsRecursive(nd) {255 e = t.w.RecursiveUnwatch(nd.Name)256 } else {257 e = t.w.Unwatch(nd.Name)258 }259 if e != nil {260 err = nonil(err, e)261 // TODO(rjeczalik): child is still watched, warn all its watchpoints262 // about possible duplicate events via Error event263 }264 }265 return err266 }267 // case 3: cur is new, alone node268 switch diff := watchAdd(cur, c, eventset); {269 case diff == none:270 // TODO(rjeczalik): cleanup this panic after implementation is stable271 panic("watch requested but no parent watchpoint found: " + cur.Name)272 case diff[0] == 0:273 if isrec {274 err = t.w.RecursiveWatch(cur.Name, diff[1])275 } else {276 err = t.w.Watch(cur.Name, diff[1])277 }278 if err != nil {279 watchDel(cur, c, diff.Event())280 return err281 }282 default:283 // TODO(rjeczalik): cleanup this panic after implementation is stable284 panic("watch requested but no parent watchpoint found: " + cur.Name)285 }286 return nil287}288// Stop TODO(rjeczalik)289//290// TODO(rjeczalik): Split parent watchpoint - transfer watches to children291// if parent is no longer needed. This carries a risk that underlying292// watcher calls could fail - reconsider if it's worth the effort.293func (t *recursiveTree) Stop(c chan<- EventInfo) {294 var err error295 fn := func(nd node) (e error) {296 diff := watchDel(nd, c, all)297 switch {298 case diff == none && watchTotal(nd) == 0:299 // TODO(rjeczalik): There's no watchpoints deeper in the tree,300 // probably we should remove the nodes as well.301 return nil302 case diff == none:303 // Removing c from nd does not require shrinking its eventset.304 case diff[1] == 0:305 if watchIsRecursive(nd) {306 e = t.w.RecursiveUnwatch(nd.Name)307 } else {308 e = t.w.Unwatch(nd.Name)309 }310 default:311 if watchIsRecursive(nd) {312 e = t.w.RecursiveRewatch(nd.Name, nd.Name, diff[0], diff[1])313 } else {314 e = t.w.Rewatch(nd.Name, diff[0], diff[1])315 }316 }317 fn := func(nd node) error {318 watchDel(nd, c, all)319 return nil320 }321 err = nonil(err, e, nd.Walk(fn))322 // TODO(rjeczalik): if e != nil store dummy chan in nd.Watch just to323 // retry un/rewatching next time and/or let the user handle the failure324 // vie Error event?325 return errSkip326 }327 t.rw.Lock()328 e := t.root.Walk("", fn) // TODO(rjeczalik): use max root per c329 t.rw.Unlock()330 if e != nil {331 err = nonil(err, e)332 }333 dbgprintf("Stop(%p) error: %v\n", c, err)334}335// Close TODO(rjeczalik)336func (t *recursiveTree) Close() error {337 err := t.w.Close()338 close(t.c)339 return err340}...
Len
Using AI Code Generation
1import (2func main() {3 watch := NewWatch("Rolex", 100)4 fmt.Printf("Watch length: %dmm5", watch.Len())6}7type Watch struct {8}9func NewWatch(brand string, size int) *Watch {10 return &Watch{11 }12}13func (w *Watch) Len() int {14}15import (16func main() {17 watch := NewWatch("Rolex", 100)18 watch.SetSize(120)19 fmt.Printf("Watch length: %dmm20", watch.Len())21}22type Watch struct {23}24func NewWatch(brand string, size int) *Watch {25 return &Watch{26 }27}28func (w *Watch) Len() int {29}30func (w *Watch) SetSize(size int) {31}32import (33func main() {
Len
Using AI Code Generation
1func main() {2 watch := watch{make([]string, 0)}3 watch.Add("Titanic")4 watch.Add("Avatar")5 fmt.Println(watch.Len())6}7type watch struct {8}9func (w watch) Add(movie string) {10 w.movies = append(w.movies, movie)11}12func (w watch) Add(movie string, year int) {13 w.movies = append(w.movies, movie)14}15func main() {16 watch := watch{make([]string, 0)}17 watch.Add("Titanic")18 watch.Add("Avatar", 2009)19}20type watch struct {21}22func (w watch) Add(movie string) {23 w.movies = append(w.movies, movie)24}25func (w watch) Add(movie string) {26 w.movies = append(w.movies, movie)27}28func main() {29 watch := watch{make([]string, 0)}30 watch.Add("Titanic")31 watch.Add("Avatar")32}
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!