google.golang.org/grpc@v1.62.1/test/control_plane_status_test.go (about) 1 /* 2 * 3 * Copyright 2022 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package test 20 21 import ( 22 "context" 23 "strings" 24 "testing" 25 "time" 26 27 "google.golang.org/grpc" 28 "google.golang.org/grpc/balancer" 29 "google.golang.org/grpc/balancer/base" 30 "google.golang.org/grpc/codes" 31 "google.golang.org/grpc/connectivity" 32 "google.golang.org/grpc/internal/balancer/stub" 33 iresolver "google.golang.org/grpc/internal/resolver" 34 "google.golang.org/grpc/internal/stubserver" 35 testpb "google.golang.org/grpc/interop/grpc_testing" 36 "google.golang.org/grpc/resolver" 37 "google.golang.org/grpc/resolver/manual" 38 "google.golang.org/grpc/status" 39 ) 40 41 func (s) TestConfigSelectorStatusCodes(t *testing.T) { 42 testCases := []struct { 43 name string 44 csErr error 45 want error 46 }{{ 47 name: "legal status code", 48 csErr: status.Errorf(codes.Unavailable, "this error is fine"), 49 want: status.Errorf(codes.Unavailable, "this error is fine"), 50 }, { 51 name: "illegal status code", 52 csErr: status.Errorf(codes.NotFound, "this error is bad"), 53 want: status.Errorf(codes.Internal, "this error is bad"), 54 }} 55 56 for _, tc := range testCases { 57 t.Run(tc.name, func(t *testing.T) { 58 ss := &stubserver.StubServer{ 59 EmptyCallF: func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) { 60 return &testpb.Empty{}, nil 61 }, 62 } 63 ss.R = manual.NewBuilderWithScheme("confSel") 64 65 if err := ss.Start(nil); err != nil { 66 t.Fatalf("Error starting endpoint server: %v", err) 67 } 68 defer ss.Stop() 69 70 state := iresolver.SetConfigSelector(resolver.State{ 71 Addresses: []resolver.Address{{Addr: ss.Address}}, 72 ServiceConfig: parseServiceConfig(t, ss.R, "{}"), 73 }, funcConfigSelector{ 74 f: func(i iresolver.RPCInfo) (*iresolver.RPCConfig, error) { 75 return nil, tc.csErr 76 }, 77 }) 78 ss.R.UpdateState(state) // Blocks until config selector is applied 79 80 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 81 defer cancel() 82 if _, err := ss.Client.EmptyCall(ctx, &testpb.Empty{}); status.Code(err) != status.Code(tc.want) || !strings.Contains(err.Error(), status.Convert(tc.want).Message()) { 83 t.Fatalf("client.EmptyCall(_, _) = _, %v; want _, %v", err, tc.want) 84 } 85 }) 86 } 87 } 88 89 func (s) TestPickerStatusCodes(t *testing.T) { 90 testCases := []struct { 91 name string 92 pickerErr error 93 want error 94 }{{ 95 name: "legal status code", 96 pickerErr: status.Errorf(codes.Unavailable, "this error is fine"), 97 want: status.Errorf(codes.Unavailable, "this error is fine"), 98 }, { 99 name: "illegal status code", 100 pickerErr: status.Errorf(codes.NotFound, "this error is bad"), 101 want: status.Errorf(codes.Internal, "this error is bad"), 102 }} 103 104 for _, tc := range testCases { 105 t.Run(tc.name, func(t *testing.T) { 106 ss := &stubserver.StubServer{ 107 EmptyCallF: func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) { 108 return &testpb.Empty{}, nil 109 }, 110 } 111 112 if err := ss.Start(nil); err != nil { 113 t.Fatalf("Error starting endpoint server: %v", err) 114 } 115 defer ss.Stop() 116 117 // Create a stub balancer that creates a picker that always returns 118 // an error. 119 sbf := stub.BalancerFuncs{ 120 UpdateClientConnState: func(d *stub.BalancerData, _ balancer.ClientConnState) error { 121 d.ClientConn.UpdateState(balancer.State{ 122 ConnectivityState: connectivity.TransientFailure, 123 Picker: base.NewErrPicker(tc.pickerErr), 124 }) 125 return nil 126 }, 127 } 128 stub.Register("testPickerStatusCodesBalancer", sbf) 129 130 ss.NewServiceConfig(`{"loadBalancingConfig": [{"testPickerStatusCodesBalancer":{}}] }`) 131 132 // Make calls until pickerErr is received. 133 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 134 defer cancel() 135 136 var lastErr error 137 for ctx.Err() == nil { 138 if _, lastErr = ss.Client.EmptyCall(ctx, &testpb.Empty{}); status.Code(lastErr) == status.Code(tc.want) && strings.Contains(lastErr.Error(), status.Convert(tc.want).Message()) { 139 // Success! 140 return 141 } 142 time.Sleep(time.Millisecond) 143 } 144 145 t.Fatalf("client.EmptyCall(_, _) = _, %v; want _, %v", lastErr, tc.want) 146 }) 147 } 148 } 149 150 func (s) TestCallCredsFromDialOptionsStatusCodes(t *testing.T) { 151 testCases := []struct { 152 name string 153 credsErr error 154 want error 155 }{{ 156 name: "legal status code", 157 credsErr: status.Errorf(codes.Unavailable, "this error is fine"), 158 want: status.Errorf(codes.Unavailable, "this error is fine"), 159 }, { 160 name: "illegal status code", 161 credsErr: status.Errorf(codes.NotFound, "this error is bad"), 162 want: status.Errorf(codes.Internal, "this error is bad"), 163 }} 164 165 for _, tc := range testCases { 166 t.Run(tc.name, func(t *testing.T) { 167 ss := &stubserver.StubServer{ 168 EmptyCallF: func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) { 169 return &testpb.Empty{}, nil 170 }, 171 } 172 173 errChan := make(chan error, 1) 174 creds := &testPerRPCCredentials{errChan: errChan} 175 176 if err := ss.Start(nil, grpc.WithPerRPCCredentials(creds)); err != nil { 177 t.Fatalf("Error starting endpoint server: %v", err) 178 } 179 defer ss.Stop() 180 181 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 182 defer cancel() 183 184 errChan <- tc.credsErr 185 186 if _, err := ss.Client.EmptyCall(ctx, &testpb.Empty{}); status.Code(err) != status.Code(tc.want) || !strings.Contains(err.Error(), status.Convert(tc.want).Message()) { 187 t.Fatalf("client.EmptyCall(_, _) = _, %v; want _, %v", err, tc.want) 188 } 189 }) 190 } 191 } 192 193 func (s) TestCallCredsFromCallOptionsStatusCodes(t *testing.T) { 194 testCases := []struct { 195 name string 196 credsErr error 197 want error 198 }{{ 199 name: "legal status code", 200 credsErr: status.Errorf(codes.Unavailable, "this error is fine"), 201 want: status.Errorf(codes.Unavailable, "this error is fine"), 202 }, { 203 name: "illegal status code", 204 credsErr: status.Errorf(codes.NotFound, "this error is bad"), 205 want: status.Errorf(codes.Internal, "this error is bad"), 206 }} 207 208 for _, tc := range testCases { 209 t.Run(tc.name, func(t *testing.T) { 210 ss := &stubserver.StubServer{ 211 EmptyCallF: func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) { 212 return &testpb.Empty{}, nil 213 }, 214 } 215 216 errChan := make(chan error, 1) 217 creds := &testPerRPCCredentials{errChan: errChan} 218 219 if err := ss.Start(nil); err != nil { 220 t.Fatalf("Error starting endpoint server: %v", err) 221 } 222 defer ss.Stop() 223 224 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 225 defer cancel() 226 227 errChan <- tc.credsErr 228 229 if _, err := ss.Client.EmptyCall(ctx, &testpb.Empty{}, grpc.PerRPCCredentials(creds)); status.Code(err) != status.Code(tc.want) || !strings.Contains(err.Error(), status.Convert(tc.want).Message()) { 230 t.Fatalf("client.EmptyCall(_, _) = _, %v; want _, %v", err, tc.want) 231 } 232 }) 233 } 234 }