github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/registry/grpc/source_test.go (about) 1 //go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -o ../../../fakes/fake_registry_store.go ../../../../vendor/github.com/operator-framework/operator-registry/pkg/registry/interface.go Query 2 package grpc 3 4 import ( 5 "context" 6 "net" 7 "net/url" 8 "os" 9 "sync" 10 "testing" 11 "time" 12 13 "github.com/sirupsen/logrus" 14 "github.com/stretchr/testify/require" 15 "google.golang.org/grpc" 16 "google.golang.org/grpc/connectivity" 17 18 "github.com/operator-framework/operator-lifecycle-manager/pkg/fakes" 19 "github.com/operator-framework/operator-registry/pkg/api" 20 opregistry "github.com/operator-framework/operator-registry/pkg/registry" 21 opserver "github.com/operator-framework/operator-registry/pkg/server" 22 23 "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" 24 ) 25 26 func server(store opregistry.Query) (func(), string, func()) { 27 lis, err := net.Listen("tcp", "localhost:") 28 if err != nil { 29 logrus.Fatalf("failed to listen: %v", err) 30 } 31 s := grpc.NewServer() 32 33 api.RegisterRegistryServer(s, opserver.NewRegistryServer(store)) 34 35 serve := func() { 36 if err := s.Serve(lis); err != nil { 37 logrus.Fatalf("failed to serve: %v", err) 38 } 39 } 40 41 stop := func() { 42 s.Stop() 43 } 44 45 return serve, lis.Addr().String(), stop 46 } 47 48 type FakeSourceSyncer struct { 49 // using a map[int] to preserve order 50 History map[registry.CatalogKey][]connectivity.State 51 52 sync.Mutex 53 expectedReadies int 54 done chan struct{} 55 } 56 57 func (f *FakeSourceSyncer) sync(state SourceState) { 58 f.Lock() 59 if f.History[state.Key] == nil { 60 f.History[state.Key] = []connectivity.State{} 61 } 62 f.History[state.Key] = append(f.History[state.Key], state.State) 63 if state.State == connectivity.Ready { 64 f.expectedReadies-- 65 } 66 if f.expectedReadies == 0 { 67 f.done <- struct{}{} 68 } 69 f.Unlock() 70 } 71 72 func NewFakeSourceSyncer(expectedReadies int) *FakeSourceSyncer { 73 return &FakeSourceSyncer{ 74 History: map[registry.CatalogKey][]connectivity.State{}, 75 expectedReadies: expectedReadies, 76 done: make(chan struct{}), 77 } 78 } 79 80 func TestConnectionEvents(t *testing.T) { 81 type testcase struct { 82 name string 83 expectedHistory map[registry.CatalogKey][]connectivity.State 84 } 85 86 test := func(tt testcase) func(t *testing.T) { 87 return func(t *testing.T) { 88 // start server for each catalog 89 addresses := map[registry.CatalogKey]string{} 90 91 for catalog := range tt.expectedHistory { 92 serve, address, stop := server(&fakes.FakeQuery{}) 93 addresses[catalog] = address 94 go serve() 95 defer stop() 96 } 97 98 // start source manager 99 syncer := NewFakeSourceSyncer(len(tt.expectedHistory)) 100 sources := NewSourceStore(logrus.New(), 1*time.Second, 5*time.Second, syncer.sync) 101 ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) 102 defer cancel() 103 sources.Start(ctx) 104 105 // add source for each catalog 106 for catalog, address := range addresses { 107 _, err := sources.Add(catalog, address) 108 require.NoError(t, err) 109 } 110 111 // wait for syncing to finish 112 <-syncer.done 113 114 // verify sync events 115 for catalog, events := range tt.expectedHistory { 116 recordedEvents := syncer.History[catalog] 117 for i := 0; i < len(recordedEvents); i++ { 118 found := false 119 for _, event := range events { 120 if event.String() == recordedEvents[i].String() { 121 found = true 122 } 123 } 124 require.True(t, found) 125 } 126 } 127 } 128 } 129 130 cases := []testcase{ 131 { 132 name: "Basic", 133 expectedHistory: map[registry.CatalogKey][]connectivity.State{ 134 {Name: "test", Namespace: "test"}: { 135 connectivity.Connecting, 136 connectivity.Ready, 137 }, 138 }, 139 }, 140 { 141 name: "Multiple", 142 expectedHistory: map[registry.CatalogKey][]connectivity.State{ 143 {Name: "test", Namespace: "test"}: { 144 connectivity.Connecting, 145 connectivity.Ready, 146 }, 147 {Name: "test2", Namespace: "test2"}: { 148 connectivity.Connecting, 149 connectivity.Ready, 150 }, 151 }, 152 }, 153 } 154 155 for _, tt := range cases { 156 t.Run(tt.name, test(tt)) 157 } 158 } 159 160 func TestGetEnvAny(t *testing.T) { 161 type envVar struct { 162 key string 163 value string 164 } 165 166 type testcase struct { 167 name string 168 envVars []envVar 169 expectedValue string 170 } 171 172 test := func(tt testcase) func(t *testing.T) { 173 return func(t *testing.T) { 174 for _, envVar := range tt.envVars { 175 os.Setenv(envVar.key, envVar.value) 176 } 177 178 defer func() { 179 for _, envVar := range tt.envVars { 180 os.Setenv(envVar.key, "") 181 } 182 }() 183 184 require.Equal(t, getEnvAny("NO_PROXY", "no_proxy"), tt.expectedValue) 185 } 186 } 187 188 cases := []testcase{ 189 { 190 name: "NotFound", 191 expectedValue: "", 192 }, 193 { 194 name: "LowerCaseFound", 195 envVars: []envVar{ 196 { 197 key: "no_proxy", 198 value: "foo", 199 }, 200 }, 201 expectedValue: "foo", 202 }, 203 { 204 name: "UpperCaseFound", 205 envVars: []envVar{ 206 { 207 key: "NO_PROXY", 208 value: "bar", 209 }, 210 }, 211 expectedValue: "bar", 212 }, 213 { 214 name: "OrderPreference", 215 envVars: []envVar{ 216 { 217 key: "no_proxy", 218 value: "foo", 219 }, 220 { 221 key: "NO_PROXY", 222 value: "bar", 223 }, 224 }, 225 expectedValue: "bar", 226 }, 227 } 228 229 for _, tt := range cases { 230 t.Run(tt.name, test(tt)) 231 } 232 } 233 234 func TestGetGRPCProxyEnv(t *testing.T) { 235 type envVar struct { 236 key string 237 value string 238 } 239 240 type testcase struct { 241 name string 242 envVars []envVar 243 expectedValue string 244 } 245 246 test := func(tt testcase) func(t *testing.T) { 247 return func(t *testing.T) { 248 for _, envVar := range tt.envVars { 249 os.Setenv(envVar.key, envVar.value) 250 } 251 252 defer func() { 253 for _, envVar := range tt.envVars { 254 os.Setenv(envVar.key, "") 255 } 256 }() 257 258 require.Equal(t, getGRPCProxyEnv(), tt.expectedValue) 259 } 260 } 261 262 cases := []testcase{ 263 { 264 name: "NotFound", 265 expectedValue: "", 266 }, 267 { 268 name: "LowerCaseFound", 269 envVars: []envVar{ 270 { 271 key: "grpc_proxy", 272 value: "foo", 273 }, 274 }, 275 expectedValue: "foo", 276 }, 277 { 278 name: "UpperCaseFound", 279 envVars: []envVar{ 280 { 281 key: "GRPC_PROXY", 282 value: "bar", 283 }, 284 }, 285 expectedValue: "bar", 286 }, 287 { 288 name: "UpperCasePreference", 289 envVars: []envVar{ 290 { 291 key: "grpc_proxy", 292 value: "foo", 293 }, 294 { 295 key: "GRPC_PROXY", 296 value: "bar", 297 }, 298 }, 299 expectedValue: "bar", 300 }, 301 } 302 303 for _, tt := range cases { 304 t.Run(tt.name, test(tt)) 305 } 306 } 307 308 func TestGRPCProxyURL(t *testing.T) { 309 type envVar struct { 310 key string 311 value string 312 } 313 314 type testcase struct { 315 name string 316 address string 317 envVars []envVar 318 expectedProxy string 319 expectedError error 320 } 321 322 test := func(tt testcase) func(t *testing.T) { 323 return func(t *testing.T) { 324 for _, envVar := range tt.envVars { 325 os.Setenv(envVar.key, envVar.value) 326 } 327 328 defer func() { 329 for _, envVar := range tt.envVars { 330 os.Setenv(envVar.key, "") 331 } 332 }() 333 334 var expectedProxyURL *url.URL 335 var err error 336 if tt.expectedProxy != "" { 337 expectedProxyURL, err = url.Parse(tt.expectedProxy) 338 require.NoError(t, err) 339 } 340 341 proxyURL, err := grpcProxyURL(tt.address) 342 require.Equal(t, expectedProxyURL, proxyURL) 343 require.Equal(t, tt.expectedError, err) 344 } 345 } 346 347 cases := []testcase{ 348 { 349 name: "NoGRPCProxySet", 350 address: "foo.com:8080", 351 expectedProxy: "", 352 expectedError: nil, 353 }, 354 { 355 name: "GRPCProxyFoundForAddress", 356 address: "foo.com:8080", 357 envVars: []envVar{ 358 { 359 key: "GRPC_PROXY", 360 value: "http://my-proxy:8080", 361 }, 362 }, 363 expectedProxy: "http://my-proxy:8080", 364 expectedError: nil, 365 }, 366 { 367 name: "GRPCNoProxyIncludesAddress", 368 address: "foo.com:8080", 369 envVars: []envVar{ 370 { 371 key: "GRPC_PROXY", 372 value: "http://my-proxy:8080", 373 }, 374 { 375 key: "NO_PROXY", 376 value: "foo.com:8080", 377 }, 378 }, 379 expectedProxy: "", 380 expectedError: nil, 381 }, 382 { 383 name: "MissingPort", 384 address: "foo.com", 385 expectedProxy: "", 386 expectedError: error(&net.AddrError{Err: "missing port in address", Addr: "foo.com"}), 387 }, 388 { 389 name: "TooManyColons", 390 address: "http://bar.com:8080", 391 expectedProxy: "", 392 expectedError: error(&net.AddrError{Err: "too many colons in address", Addr: "http://bar.com:8080"}), 393 }, 394 } 395 396 for _, tt := range cases { 397 t.Run(tt.name, test(tt)) 398 } 399 }