github.com/thanos-io/thanos@v0.32.5/pkg/rules/proxy_test.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package rules 5 6 import ( 7 "context" 8 "io" 9 "os" 10 "reflect" 11 "testing" 12 13 "github.com/go-kit/log" 14 "github.com/pkg/errors" 15 "google.golang.org/grpc" 16 17 "github.com/thanos-io/thanos/pkg/rules/rulespb" 18 "github.com/thanos-io/thanos/pkg/store/storepb" 19 ) 20 21 type testRulesClient struct { 22 grpc.ClientStream 23 rulesErr, recvErr error 24 response *rulespb.RulesResponse 25 sentResponse bool 26 } 27 28 func (t *testRulesClient) String() string { 29 return "test" 30 } 31 32 func (t *testRulesClient) Recv() (*rulespb.RulesResponse, error) { 33 // A simulation of underlying grpc Recv behavior as per https://github.com/grpc/grpc-go/blob/7f2581f910fc21497091c4109b56d310276fc943/stream.go#L117-L125. 34 if t.recvErr != nil { 35 return nil, t.recvErr 36 } 37 38 if t.sentResponse { 39 return nil, io.EOF 40 } 41 t.sentResponse = true 42 43 return t.response, nil 44 } 45 46 func (t *testRulesClient) Rules(ctx context.Context, in *rulespb.RulesRequest, opts ...grpc.CallOption) (rulespb.Rules_RulesClient, error) { 47 return t, t.rulesErr 48 } 49 50 var _ rulespb.RulesClient = &testRulesClient{} 51 52 type testRulesServer struct { 53 grpc.ServerStream 54 sendErr error 55 response *rulespb.RulesResponse 56 } 57 58 func (t *testRulesServer) String() string { 59 return "test" 60 } 61 62 func (t *testRulesServer) Send(response *rulespb.RulesResponse) error { 63 if t.sendErr != nil { 64 return t.sendErr 65 } 66 t.response = response 67 return nil 68 } 69 70 func (t *testRulesServer) Context() context.Context { 71 return context.Background() 72 } 73 74 func TestProxy(t *testing.T) { 75 logger := log.NewLogfmtLogger(os.Stderr) 76 77 for _, tc := range []struct { 78 name string 79 request *rulespb.RulesRequest 80 client rulespb.RulesClient 81 server *testRulesServer 82 wantResponse *rulespb.RulesResponse 83 wantError error 84 }{ 85 { 86 name: "rule group proxy success", 87 request: &rulespb.RulesRequest{ 88 Type: rulespb.RulesRequest_ALL, 89 PartialResponseStrategy: storepb.PartialResponseStrategy_WARN, 90 }, 91 client: &testRulesClient{ 92 response: rulespb.NewRuleGroupRulesResponse(&rulespb.RuleGroup{ 93 Name: "foo", 94 }), 95 recvErr: nil, 96 }, 97 server: &testRulesServer{}, 98 wantResponse: rulespb.NewRuleGroupRulesResponse(&rulespb.RuleGroup{ 99 Name: "foo", 100 }), 101 }, 102 { 103 name: "warning proxy success", 104 request: &rulespb.RulesRequest{ 105 Type: rulespb.RulesRequest_ALL, 106 PartialResponseStrategy: storepb.PartialResponseStrategy_WARN, 107 }, 108 client: &testRulesClient{ 109 response: rulespb.NewWarningRulesResponse(errors.New("warning from client")), 110 recvErr: nil, 111 }, 112 server: &testRulesServer{}, 113 wantResponse: rulespb.NewWarningRulesResponse(errors.New("warning from client")), 114 }, 115 { 116 name: "warn: retreiving rules client failed", 117 request: &rulespb.RulesRequest{ 118 Type: rulespb.RulesRequest_ALL, 119 PartialResponseStrategy: storepb.PartialResponseStrategy_WARN, 120 }, 121 client: &testRulesClient{ 122 response: nil, 123 rulesErr: errors.New("retreiving rules failed"), 124 }, 125 server: &testRulesServer{}, 126 wantResponse: rulespb.NewWarningRulesResponse(errors.New("fetching rules from rules client test: retreiving rules failed")), 127 }, 128 { 129 name: "warn: retreiving rules client failed, forward warning failed", 130 request: &rulespb.RulesRequest{ 131 Type: rulespb.RulesRequest_ALL, 132 PartialResponseStrategy: storepb.PartialResponseStrategy_WARN, 133 }, 134 client: &testRulesClient{ 135 response: nil, 136 rulesErr: errors.New("retreiving rules failed"), 137 }, 138 server: &testRulesServer{ 139 sendErr: errors.New("forwarding warning response failed"), 140 }, 141 wantError: errors.New("forwarding warning response failed"), 142 }, 143 { 144 name: "abort: retreiving rules client failed", 145 request: &rulespb.RulesRequest{ 146 Type: rulespb.RulesRequest_ALL, 147 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 148 }, 149 client: &testRulesClient{ 150 response: nil, 151 rulesErr: errors.New("retreiving rules failed"), 152 }, 153 server: &testRulesServer{}, 154 wantError: errors.New("fetching rules from rules client test: retreiving rules failed"), 155 }, 156 { 157 name: "warn: receive failed", 158 request: &rulespb.RulesRequest{ 159 Type: rulespb.RulesRequest_ALL, 160 PartialResponseStrategy: storepb.PartialResponseStrategy_WARN, 161 }, 162 client: &testRulesClient{ 163 response: nil, 164 recvErr: errors.New("503 from Prometheus"), 165 }, 166 server: &testRulesServer{}, 167 wantResponse: rulespb.NewWarningRulesResponse(errors.New("receiving rules from rules client test: 503 from Prometheus")), 168 }, 169 { 170 name: "warn: receive failed, forward warning failed", 171 request: &rulespb.RulesRequest{ 172 Type: rulespb.RulesRequest_ALL, 173 PartialResponseStrategy: storepb.PartialResponseStrategy_WARN, 174 }, 175 client: &testRulesClient{ 176 response: nil, 177 recvErr: errors.New("503 from Prometheus"), 178 }, 179 server: &testRulesServer{ 180 sendErr: errors.New("forwarding warning response failed"), 181 }, 182 wantError: errors.New("sending rules error to server test: forwarding warning response failed"), 183 }, 184 { 185 name: "abort: receive failed", 186 request: &rulespb.RulesRequest{ 187 Type: rulespb.RulesRequest_ALL, 188 PartialResponseStrategy: storepb.PartialResponseStrategy_ABORT, 189 }, 190 client: &testRulesClient{ 191 response: nil, 192 recvErr: errors.New("503 from Prometheus"), 193 }, 194 server: &testRulesServer{}, 195 wantError: errors.New("receiving rules from rules client test: 503 from Prometheus"), 196 }, 197 { 198 name: "send failed", 199 request: &rulespb.RulesRequest{ 200 Type: rulespb.RulesRequest_ALL, 201 PartialResponseStrategy: storepb.PartialResponseStrategy_WARN, 202 }, 203 client: &testRulesClient{ 204 response: rulespb.NewRuleGroupRulesResponse(&rulespb.RuleGroup{ 205 Name: "foo", 206 }), 207 recvErr: nil, 208 }, 209 server: &testRulesServer{ 210 sendErr: errors.New("sending message failed"), 211 }, 212 wantError: errors.New("rpc error: code = Unknown desc = send rules response: sending message failed"), 213 }, 214 { 215 name: "sending warning response failed", 216 request: &rulespb.RulesRequest{ 217 Type: rulespb.RulesRequest_ALL, 218 PartialResponseStrategy: storepb.PartialResponseStrategy_WARN, 219 }, 220 client: &testRulesClient{ 221 response: rulespb.NewWarningRulesResponse(errors.New("warning from client")), 222 recvErr: nil, 223 }, 224 server: &testRulesServer{ 225 sendErr: errors.New("sending message failed"), 226 }, 227 wantError: errors.New("sending rules warning to server test: sending message failed"), 228 }, 229 } { 230 t.Run(tc.name, func(t *testing.T) { 231 p := NewProxy(logger, func() []rulespb.RulesClient { 232 return []rulespb.RulesClient{tc.client} 233 }) 234 235 err := p.Rules(tc.request, tc.server) 236 gotErr := "<nil>" 237 if err != nil { 238 gotErr = err.Error() 239 } 240 wantErr := "<nil>" 241 if tc.wantError != nil { 242 wantErr = tc.wantError.Error() 243 } 244 245 if gotErr != wantErr { 246 t.Errorf("want error %q, got %q", wantErr, gotErr) 247 } 248 249 if !reflect.DeepEqual(tc.wantResponse, tc.server.response) { 250 t.Errorf("want response %v, got %v", tc.wantResponse, tc.server.response) 251 } 252 }) 253 } 254 } 255 256 // TestProxyDataRace find the concurrent data race bug ( go test -race -run TestProxyDataRace -v ). 257 func TestProxyDataRace(t *testing.T) { 258 logger := log.NewLogfmtLogger(os.Stderr) 259 p := NewProxy(logger, func() []rulespb.RulesClient { 260 es := &testRulesClient{ 261 recvErr: errors.New("err"), 262 } 263 size := 100 264 endpoints := make([]rulespb.RulesClient, 0, size) 265 for i := 0; i < size; i++ { 266 endpoints = append(endpoints, es) 267 } 268 return endpoints 269 }) 270 req := &rulespb.RulesRequest{ 271 Type: rulespb.RulesRequest_ALL, 272 PartialResponseStrategy: storepb.PartialResponseStrategy_WARN, 273 } 274 s := &rulesServer{ 275 ctx: context.Background(), 276 } 277 _ = p.Rules(req, s) 278 }