github.com/letsencrypt/boulder@v0.20251208.0/redis/lookup_test.go (about) 1 package redis 2 3 import ( 4 "context" 5 "testing" 6 "time" 7 8 "github.com/letsencrypt/boulder/cmd" 9 blog "github.com/letsencrypt/boulder/log" 10 "github.com/letsencrypt/boulder/metrics" 11 "github.com/letsencrypt/boulder/test" 12 13 "github.com/redis/go-redis/v9" 14 ) 15 16 func newTestRedisRing() *redis.Ring { 17 CACertFile := "../test/certs/ipki/minica.pem" 18 CertFile := "../test/certs/ipki/localhost/cert.pem" 19 KeyFile := "../test/certs/ipki/localhost/key.pem" 20 tlsConfig := cmd.TLSConfig{ 21 CACertFile: CACertFile, 22 CertFile: CertFile, 23 KeyFile: KeyFile, 24 } 25 tlsConfig2, err := tlsConfig.Load(metrics.NoopRegisterer) 26 if err != nil { 27 panic(err) 28 } 29 30 client := redis.NewRing(&redis.RingOptions{ 31 Username: "boulder", 32 Password: "824968fa490f4ecec1e52d5e34916bdb60d45f8d", 33 TLSConfig: tlsConfig2, 34 }) 35 return client 36 } 37 38 func TestNewLookup(t *testing.T) { 39 t.Parallel() 40 41 logger := blog.NewMock() 42 ring := newTestRedisRing() 43 44 _, err := newLookup([]cmd.ServiceDomain{ 45 { 46 Service: "redisratelimits", 47 Domain: "service.consul", 48 }, 49 }, 50 "consul.service.consul", 51 250*time.Millisecond, 52 ring, 53 logger, 54 metrics.NoopRegisterer, 55 ) 56 test.AssertNotError(t, err, "Expected newLookup construction to succeed") 57 } 58 59 func TestStart(t *testing.T) { 60 t.Parallel() 61 62 logger := blog.NewMock() 63 ring := newTestRedisRing() 64 65 lookup, err := newLookup([]cmd.ServiceDomain{ 66 { 67 Service: "redisratelimits", 68 Domain: "service.consul", 69 }, 70 }, 71 "consul.service.consul", 72 250*time.Millisecond, 73 ring, 74 logger, 75 metrics.NoopRegisterer, 76 ) 77 test.AssertNotError(t, err, "Expected newLookup construction to succeed") 78 79 lookup.start() 80 lookup.stop() 81 } 82 83 func TestNewLookupWithOneFailingSRV(t *testing.T) { 84 t.Parallel() 85 86 logger := blog.NewMock() 87 ring := newTestRedisRing() 88 89 _, err := newLookup([]cmd.ServiceDomain{ 90 { 91 Service: "doesnotexist", 92 Domain: "service.consuls", 93 }, 94 { 95 Service: "redisratelimits", 96 Domain: "service.consul", 97 }, 98 }, 99 "consul.service.consul", 100 250*time.Millisecond, 101 ring, 102 logger, 103 metrics.NoopRegisterer, 104 ) 105 test.AssertNotError(t, err, "Expected newLookup construction to succeed") 106 } 107 108 func TestNewLookupWithAllFailingSRV(t *testing.T) { 109 t.Parallel() 110 111 logger := blog.NewMock() 112 ring := newTestRedisRing() 113 114 _, err := newLookup([]cmd.ServiceDomain{ 115 { 116 Service: "doesnotexist", 117 Domain: "service.consuls", 118 }, 119 { 120 Service: "doesnotexist2", 121 Domain: "service.consuls", 122 }, 123 }, 124 "consul.service.consul", 125 250*time.Millisecond, 126 ring, 127 logger, 128 metrics.NoopRegisterer, 129 ) 130 test.AssertError(t, err, "Expected newLookup construction to fail") 131 } 132 133 func TestUpdateNowWithAllFailingSRV(t *testing.T) { 134 t.Parallel() 135 136 logger := blog.NewMock() 137 ring := newTestRedisRing() 138 139 lookup, err := newLookup([]cmd.ServiceDomain{ 140 { 141 Service: "redisratelimits", 142 Domain: "service.consul", 143 }, 144 }, 145 "consul.service.consul", 146 250*time.Millisecond, 147 ring, 148 logger, 149 metrics.NoopRegisterer, 150 ) 151 test.AssertNotError(t, err, "Expected newLookup construction to succeed") 152 153 lookup.srvLookups = []cmd.ServiceDomain{ 154 { 155 Service: "doesnotexist1", 156 Domain: "service.consul", 157 }, 158 { 159 Service: "doesnotexist2", 160 Domain: "service.consul", 161 }, 162 } 163 164 tempErr, nonTempErr := lookup.updateNow(t.Context()) 165 test.AssertNotError(t, tempErr, "Expected no temporary errors") 166 test.AssertError(t, nonTempErr, "Expected non-temporary errors to have occurred") 167 } 168 169 func TestUpdateNowWithAllFailingSRVs(t *testing.T) { 170 t.Parallel() 171 172 logger := blog.NewMock() 173 ring := newTestRedisRing() 174 175 lookup, err := newLookup([]cmd.ServiceDomain{ 176 { 177 Service: "redisratelimits", 178 Domain: "service.consul", 179 }, 180 }, 181 "consul.service.consul", 182 250*time.Millisecond, 183 ring, 184 logger, 185 metrics.NoopRegisterer, 186 ) 187 test.AssertNotError(t, err, "Expected newLookup construction to succeed") 188 189 // Replace the dnsAuthority with a non-existent DNS server, this will cause 190 // a timeout error, which is technically a temporary error, but will 191 // eventually result in a non-temporary error when no shards are resolved. 192 lookup.dnsAuthority = "consuls.services.consuls:53" 193 194 tempErr, nonTempErr := lookup.updateNow(t.Context()) 195 test.AssertError(t, tempErr, "Expected temporary errors") 196 test.AssertError(t, nonTempErr, "Expected a non-temporary error") 197 test.AssertErrorIs(t, nonTempErr, ErrNoShardsResolved) 198 } 199 200 func TestUpdateNowWithOneFailingSRV(t *testing.T) { 201 t.Parallel() 202 203 logger := blog.NewMock() 204 ring := newTestRedisRing() 205 206 lookup, err := newLookup([]cmd.ServiceDomain{ 207 { 208 Service: "doesnotexist", 209 Domain: "service.consuls", 210 }, 211 { 212 Service: "redisratelimits", 213 Domain: "service.consul", 214 }, 215 }, 216 "consul.service.consul", 217 250*time.Millisecond, 218 ring, 219 logger, 220 metrics.NoopRegisterer, 221 ) 222 test.AssertNotError(t, err, "Expected newLookup construction to succeed") 223 224 // The Consul service entry for 'redisratelimits' is configured to return 225 // two SRV targets. We should only have two shards in the ring. 226 test.Assert(t, ring.Len() == 2, "Expected 2 shards in the ring") 227 228 testCtx := t.Context() 229 230 // Ensure we can reach both shards using the PING command. 231 err = ring.ForEachShard(testCtx, func(ctx context.Context, shard *redis.Client) error { 232 return shard.Ping(ctx).Err() 233 }) 234 test.AssertNotError(t, err, "Expected PING to succeed for both shards") 235 236 // Drop both Shards from the ring. 237 ring.SetAddrs(map[string]string{}) 238 test.Assert(t, ring.Len() == 0, "Expected 0 shards in the ring") 239 240 // Force a lookup to occur. 241 tempErr, nonTempErr := lookup.updateNow(testCtx) 242 test.AssertNotError(t, tempErr, "Expected no temporary errors") 243 test.AssertNotError(t, nonTempErr, "Expected no non-temporary errors") 244 245 // The ring should now have two shards again. 246 test.Assert(t, ring.Len() == 2, "Expected 2 shards in the ring") 247 }