github.com/asynkron/protoactor-go@v0.0.0-20240308120642-ef91a6abee75/cluster/clusterproviders/consul/consul_provider_test.go (about) 1 package consul 2 3 import ( 4 "fmt" 5 "net" 6 "strconv" 7 "testing" 8 "time" 9 10 "github.com/asynkron/protoactor-go/cluster/identitylookup/disthash" 11 12 "github.com/asynkron/protoactor-go/actor" 13 "github.com/asynkron/protoactor-go/cluster" 14 "github.com/asynkron/protoactor-go/remote" 15 "github.com/stretchr/testify/assert" 16 ) 17 18 func newClusterForTest(name string, addr string, cp cluster.ClusterProvider) *cluster.Cluster { 19 host, _port, err := net.SplitHostPort(addr) 20 if err != nil { 21 panic(err) 22 } 23 port, _ := strconv.Atoi(_port) 24 remoteConfig := remote.Configure(host, port) 25 lookup := disthash.New() 26 config := cluster.Configure(name, cp, lookup, remoteConfig) 27 // return cluster.NewForTest(system, config) 28 29 system := actor.NewActorSystem() 30 c := cluster.New(system, config) 31 32 // use for test without start remote 33 c.ActorSystem.ProcessRegistry.Address = addr 34 c.MemberList = cluster.NewMemberList(c) 35 c.Remote = remote.NewRemote(c.ActorSystem, c.Config.RemoteConfig) 36 return c 37 } 38 39 func TestStartMember(t *testing.T) { 40 if testing.Short() { 41 return 42 } 43 a := assert.New(t) 44 45 p, _ := New() 46 defer p.Shutdown(true) 47 48 c := newClusterForTest("mycluster", "127.0.0.1:8000", p) 49 eventstream := c.ActorSystem.EventStream 50 ch := make(chan interface{}, 16) 51 eventstream.Subscribe(func(m interface{}) { 52 if _, ok := m.(*cluster.ClusterTopology); ok { 53 ch <- m 54 } 55 }) 56 57 err := p.StartMember(c) 58 a.NoError(err) 59 60 select { 61 case <-time.After(10 * time.Second): 62 a.FailNow("no member joined yet") 63 64 case m := <-ch: 65 msg := m.(*cluster.ClusterTopology) 66 // member joined 67 members := []*cluster.Member{ 68 { 69 // Id: "mycluster@127.0.0.1:8000", 70 Id: fmt.Sprintf("%s", c.ActorSystem.ID), 71 Host: "127.0.0.1", 72 Port: 8000, 73 Kinds: []string{}, 74 }, 75 } 76 77 expected := &cluster.ClusterTopology{ 78 Members: members, 79 Joined: members, 80 Left: []*cluster.Member{}, 81 TopologyHash: msg.TopologyHash, 82 } 83 a.Equal(expected, msg) 84 } 85 } 86 87 func TestRegisterMultipleMembers(t *testing.T) { 88 if testing.Short() { 89 return 90 } 91 a := assert.New(t) 92 93 members := []struct { 94 cluster string 95 host string 96 port int 97 }{ 98 {"mycluster2", "127.0.0.1", 8001}, 99 {"mycluster2", "127.0.0.1", 8002}, 100 {"mycluster2", "127.0.0.1", 8003}, 101 } 102 103 p, _ := New() 104 defer p.Shutdown(true) 105 for _, member := range members { 106 addr := fmt.Sprintf("%s:%d", member.host, member.port) 107 _p, _ := New() 108 c := newClusterForTest(member.cluster, addr, _p) 109 err := p.StartMember(c) 110 a.NoError(err) 111 t.Cleanup(func() { 112 _p.Shutdown(true) 113 }) 114 } 115 116 entries, _, err := p.client.Health().Service("mycluster2", "", true, nil) 117 a.NoError(err) 118 119 found := false 120 for _, entry := range entries { 121 found = false 122 for _, member := range members { 123 if entry.Service.Port == member.port { 124 found = true 125 } 126 } 127 a.Truef(found, "Member port not found - ExtensionID:%v Address: %v:%v", 128 entry.Service.ID, entry.Service.Address, entry.Service.Port) 129 } 130 } 131 132 func TestUpdateTTL_DoesNotReregisterAfterShutdown(t *testing.T) { 133 if testing.Short() { 134 return 135 } 136 a := assert.New(t) 137 138 p, _ := New() 139 c := newClusterForTest("mycluster5", "127.0.0.1:8001", p) 140 141 shutdownShouldHaveResolved := make(chan bool, 1) 142 143 err := p.StartMember(c) 144 a.NoError(err) 145 146 time.Sleep(time.Second) 147 found, _ := findService(t, p) 148 a.True(found, "service was not registered in consul") 149 150 go func() { 151 // if after 5 seconds `Shutdown` did not resolve, assume that it will not resolve until `blockingUpdateTTL` resolves 152 time.Sleep(5 * time.Second) 153 shutdownShouldHaveResolved <- true 154 }() 155 156 err = p.Shutdown(true) 157 a.NoError(err) 158 shutdownShouldHaveResolved <- true 159 160 // since `UpdateTTL` runs in a separate goroutine we need to wait until it is actually finished before checking the member's clusterstatus 161 p.updateTTLWaitGroup.Wait() 162 found, status := findService(t, p) 163 a.Falsef(found, "service was still registered in consul after shutdown (service status: %s)", status) 164 } 165 166 func findService(t *testing.T, p *Provider) (found bool, status string) { 167 service := p.cluster.Config.Name 168 port := p.cluster.Config.RemoteConfig.Port 169 entries, _, err := p.client.Health().Service(service, "", false, nil) 170 if err != nil { 171 t.Error(err) 172 } 173 174 for _, entry := range entries { 175 if entry.Service.Port == port { 176 return true, entry.Checks.AggregatedStatus() 177 } 178 } 179 return false, "" 180 }