github.com/kiali/kiali@v1.84.0/business/namespaces_test.go (about) 1 package business 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 8 v1 "github.com/openshift/api/project/v1" 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11 core_v1 "k8s.io/api/core/v1" 12 meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 13 "k8s.io/apimachinery/pkg/runtime" 14 15 "github.com/kiali/kiali/config" 16 "github.com/kiali/kiali/kubernetes" 17 "github.com/kiali/kiali/kubernetes/cache" 18 "github.com/kiali/kiali/kubernetes/kubetest" 19 "github.com/kiali/kiali/models" 20 ) 21 22 // Namespace service setup 23 func setupNamespaceService(t *testing.T, k8s kubernetes.ClientInterface, conf *config.Config) NamespaceService { 24 cache := cache.NewTestingCache(t, k8s, *conf) 25 26 k8sclients := make(map[string]kubernetes.ClientInterface) 27 k8sclients[conf.KubernetesConfig.ClusterName] = k8s 28 return NewNamespaceService(k8sclients, k8sclients, cache, conf) 29 } 30 31 // Namespace service setup 32 func setupNamespaceServiceWithNs() *kubetest.FakeK8sClient { 33 // config needs to be set by other services since those rely on the global. 34 objects := []runtime.Object{ 35 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "bookinfo"}}, 36 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "alpha"}}, 37 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "beta"}}, 38 } 39 for _, obj := range fakeNamespaces() { 40 o := obj 41 objects = append(objects, &o) 42 } 43 k8s := kubetest.NewFakeK8sClient(objects...) 44 k8s.OpenShift = false 45 return k8s 46 } 47 48 // Namespace service setup 49 func setupAmbientNamespaceServiceWithNs() kubernetes.ClientInterface { 50 c := config.NewConfig() 51 labels := map[string]string{ 52 c.IstioLabels.AmbientNamespaceLabel: c.IstioLabels.AmbientNamespaceLabelValue, 53 } 54 // config needs to be set by other services since those rely on the global. 55 objects := []runtime.Object{ 56 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "bookinfo", Labels: labels}}, 57 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "alpha"}}, 58 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "beta"}}, 59 } 60 for _, obj := range fakeNamespaces() { 61 o := obj 62 objects = append(objects, &o) 63 } 64 k8s := kubetest.NewFakeK8sClient(objects...) 65 k8s.OpenShift = false 66 return k8s 67 } 68 69 // Project service setup 70 func setupAmbientProjectWithNs() kubernetes.ClientInterface { 71 c := config.NewConfig() 72 labels := map[string]string{ 73 c.IstioLabels.AmbientNamespaceLabel: c.IstioLabels.AmbientNamespaceLabelValue, 74 } 75 // config needs to be set by other services since those rely on the global. 76 objects := []runtime.Object{ 77 &v1.Project{ObjectMeta: meta_v1.ObjectMeta{Name: "bookinfo", Labels: labels}}, 78 &v1.Project{ObjectMeta: meta_v1.ObjectMeta{Name: "alpha"}}, 79 &v1.Project{ObjectMeta: meta_v1.ObjectMeta{Name: "beta"}}, 80 } 81 for _, obj := range fakeNamespaces() { 82 o := obj 83 objects = append(objects, &o) 84 } 85 k8s := kubetest.NewFakeK8sClient(objects...) 86 k8s.OpenShift = true 87 return k8s 88 } 89 90 // Get namespaces 91 func TestGetNamespaces(t *testing.T) { 92 conf := config.NewConfig() 93 config.Set(conf) 94 95 k8s := setupNamespaceServiceWithNs() 96 97 mockClientFactory := kubetest.NewK8SClientFactoryMock(k8s) 98 SetWithBackends(mockClientFactory, nil) 99 100 nsservice := setupNamespaceService(t, k8s, conf) 101 102 ns, _ := nsservice.GetNamespaces(context.TODO()) 103 104 assert.NotNil(t, ns) 105 assert.Equal(t, len(ns), 5) 106 assert.Equal(t, ns[0].Name, "alpha") 107 } 108 109 // Get namespace 110 func TestGetNamespace(t *testing.T) { 111 conf := config.NewConfig() 112 config.Set(conf) 113 114 k8s := setupNamespaceServiceWithNs() 115 116 mockClientFactory := kubetest.NewK8SClientFactoryMock(k8s) 117 SetWithBackends(mockClientFactory, nil) 118 119 nsservice := setupNamespaceService(t, k8s, conf) 120 121 ns, _ := nsservice.GetClusterNamespace(context.TODO(), "bookinfo", config.Get().KubernetesConfig.ClusterName) 122 123 assert.NotNil(t, ns) 124 assert.Equal(t, ns.Name, "bookinfo") 125 } 126 127 // Get namespace error 128 func TestGetNamespaceWithError(t *testing.T) { 129 conf := config.NewConfig() 130 config.Set(conf) 131 132 k8s := setupNamespaceServiceWithNs() 133 134 mockClientFactory := kubetest.NewK8SClientFactoryMock(k8s) 135 SetWithBackends(mockClientFactory, nil) 136 137 nsservice := setupNamespaceService(t, k8s, conf) 138 139 ns2, err := nsservice.GetClusterNamespace(context.TODO(), "fakeNS", config.Get().KubernetesConfig.ClusterName) 140 141 assert.NotNil(t, err) 142 assert.Nil(t, ns2) 143 } 144 145 // Get Ambient namespace 146 func TestAmbientNamespace(t *testing.T) { 147 conf := config.NewConfig() 148 config.Set(conf) 149 150 k8s := setupAmbientNamespaceServiceWithNs() 151 152 mockClientFactory := kubetest.NewK8SClientFactoryMock(k8s) 153 SetWithBackends(mockClientFactory, nil) 154 155 nsservice := setupNamespaceService(t, k8s, conf) 156 157 ns, _ := nsservice.GetClusterNamespace(context.TODO(), "bookinfo", config.Get().KubernetesConfig.ClusterName) 158 159 assert.NotNil(t, ns) 160 assert.Equal(t, ns.Name, "bookinfo") 161 assert.True(t, ns.IsAmbient) 162 163 ns2, _ := nsservice.GetClusterNamespace(context.TODO(), "alpha", config.Get().KubernetesConfig.ClusterName) 164 165 assert.NotNil(t, ns2) 166 assert.Equal(t, ns2.Name, "alpha") 167 assert.False(t, ns2.IsAmbient) 168 } 169 170 // Get Ambient namespace 171 func TestAmbientProject(t *testing.T) { 172 conf := config.NewConfig() 173 config.Set(conf) 174 175 k8s := setupAmbientProjectWithNs() 176 177 mockClientFactory := kubetest.NewK8SClientFactoryMock(k8s) 178 SetWithBackends(mockClientFactory, nil) 179 180 nsservice := setupNamespaceService(t, k8s, conf) 181 182 ns, _ := nsservice.GetClusterNamespace(context.TODO(), "bookinfo", config.Get().KubernetesConfig.ClusterName) 183 184 assert.NotNil(t, ns) 185 assert.Equal(t, ns.Name, "bookinfo") 186 assert.True(t, ns.IsAmbient) 187 188 ns2, _ := nsservice.GetClusterNamespace(context.TODO(), "alpha", config.Get().KubernetesConfig.ClusterName) 189 190 assert.NotNil(t, ns2) 191 assert.Equal(t, ns2.Name, "alpha") 192 assert.False(t, ns2.IsAmbient) 193 } 194 195 // Update namespaces 196 func TestUpdateNamespaces(t *testing.T) { 197 conf := config.NewConfig() 198 config.Set(conf) 199 200 k8s := setupNamespaceServiceWithNs() 201 202 mockClientFactory := kubetest.NewK8SClientFactoryMock(k8s) 203 SetWithBackends(mockClientFactory, nil) 204 205 nsservice := setupNamespaceService(t, k8s, conf) 206 207 ns, err := nsservice.UpdateNamespace(context.TODO(), "bookinfo", `{"metadata": {"labels": {"new": "label"}}}`, conf.KubernetesConfig.ClusterName) 208 209 assert.Nil(t, err) 210 assert.NotNil(t, ns) 211 assert.Equal(t, ns.Name, "bookinfo") 212 } 213 214 func TestMultiClusterGetNamespace(t *testing.T) { 215 require := require.New(t) 216 217 conf := config.NewConfig() 218 conf.KubernetesConfig.ClusterName = "east" 219 config.Set(conf) 220 221 k8s := setupNamespaceServiceWithNs() 222 223 clientFactory := kubetest.NewK8SClientFactoryMock(nil) 224 clients := map[string]kubernetes.ClientInterface{ 225 "east": kubetest.NewFakeK8sClient( 226 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "bookinfo"}}, 227 ), 228 "west": kubetest.NewFakeK8sClient( 229 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "bookinfo"}}, 230 ), 231 } 232 clientFactory.SetClients(clients) 233 mockClientFactory := kubetest.NewK8SClientFactoryMock(k8s) 234 SetWithBackends(mockClientFactory, nil) 235 cache := cache.NewTestingCacheWithFactory(t, clientFactory, *conf) 236 237 nsservice := NewNamespaceService(clients, clients, cache, conf) 238 239 ns, err := nsservice.GetClusterNamespace(context.TODO(), "bookinfo", conf.KubernetesConfig.ClusterName) 240 require.NoError(err) 241 242 assert.Equal(t, conf.KubernetesConfig.ClusterName, ns.Cluster) 243 } 244 245 func TestMultiClusterGetNamespaces(t *testing.T) { 246 require := require.New(t) 247 assert := assert.New(t) 248 249 conf := config.NewConfig() 250 conf.KubernetesConfig.ClusterName = "east" 251 config.Set(conf) 252 253 k8s := setupNamespaceServiceWithNs() 254 255 clientFactory := kubetest.NewK8SClientFactoryMock(nil) 256 clients := map[string]kubernetes.ClientInterface{ 257 "east": kubetest.NewFakeK8sClient( 258 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "bookinfo"}}, 259 ), 260 "west": kubetest.NewFakeK8sClient( 261 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "bookinfo"}}, 262 ), 263 } 264 clientFactory.SetClients(clients) 265 mockClientFactory := kubetest.NewK8SClientFactoryMock(k8s) 266 SetWithBackends(mockClientFactory, nil) 267 cache := cache.NewTestingCacheWithFactory(t, clientFactory, *conf) 268 269 nsservice := NewNamespaceService(clients, clients, cache, conf) 270 namespaces, err := nsservice.GetNamespaces(context.TODO()) 271 require.NoError(err) 272 273 require.Len(namespaces, 2) 274 var clusterNames []string 275 for _, ns := range namespaces { 276 clusterNames = append(clusterNames, ns.Cluster) 277 } 278 279 assert.Contains(clusterNames, "east") 280 assert.Contains(clusterNames, "west") 281 } 282 283 func TestGetNamespacesCached(t *testing.T) { 284 require := require.New(t) 285 assert := assert.New(t) 286 287 conf := config.NewConfig() 288 conf.KubernetesConfig.ClusterName = "east" 289 conf.KubernetesConfig.CacheTokenNamespaceDuration = 600000 290 config.Set(conf) 291 292 k8s := setupNamespaceServiceWithNs() 293 294 clientFactory := kubetest.NewK8SClientFactoryMock(nil) 295 clients := map[string]kubernetes.ClientInterface{ 296 "east": k8s, 297 "west": kubetest.NewFakeK8sClient(), 298 } 299 clientFactory.SetClients(clients) 300 mockClientFactory := kubetest.NewK8SClientFactoryMock(k8s) 301 SetWithBackends(mockClientFactory, nil) 302 cache := cache.NewTestingCacheWithFactory(t, clientFactory, *conf) 303 cache.SetNamespaces( 304 k8s.GetToken(), 305 // gamma only exists in the cache. 306 []models.Namespace{{Name: "bookinfo", Cluster: "east"}, {Name: "alpha", Cluster: "east"}, {Name: "beta", Cluster: "east"}, {Name: "gamma", Cluster: "west"}}, 307 ) 308 309 nsservice := NewNamespaceService(clients, clients, cache, conf) 310 namespaces, err := nsservice.GetNamespaces(context.TODO()) 311 require.NoError(err) 312 313 // There's actually 6 namespaces with 'test' and 'test1' but only 4 are cached. 314 require.Len(namespaces, 4) 315 316 namespace, err := nsservice.GetClusterNamespace(context.TODO(), "gamma", "west") 317 require.NoError(err) 318 319 assert.Equal("west", namespace.Cluster) 320 } 321 322 func TestGetNamespacesDifferentTokens(t *testing.T) { 323 require := require.New(t) 324 assert := assert.New(t) 325 326 conf := config.NewConfig() 327 conf.KubernetesConfig.ClusterName = "east" 328 conf.KubernetesConfig.CacheTokenNamespaceDuration = 600000 329 config.Set(conf) 330 331 east := setupNamespaceServiceWithNs() 332 east.Token = "east-token" 333 west := kubetest.NewFakeK8sClient() 334 west.Token = "west-token" 335 336 clientFactory := kubetest.NewK8SClientFactoryMock(nil) 337 clients := map[string]kubernetes.ClientInterface{ 338 "east": east, 339 "west": west, 340 } 341 clientFactory.SetClients(clients) 342 cache := cache.NewTestingCacheWithFactory(t, clientFactory, *conf) 343 cache.SetNamespaces( 344 east.GetToken(), 345 []models.Namespace{{Name: "bookinfo", Cluster: "east"}, {Name: "alpha", Cluster: "east"}, {Name: "beta", Cluster: "east"}}, 346 ) 347 cache.SetNamespaces( 348 west.GetToken(), 349 []models.Namespace{{Name: "gamma", Cluster: "west"}}, 350 ) 351 352 nsservice := NewNamespaceService(clients, clients, cache, conf) 353 namespaces, err := nsservice.GetNamespaces(context.TODO()) 354 require.NoError(err) 355 356 // There's actually 6 namespaces with 'test' and 'test1' but only 4 are cached. 357 require.Len(namespaces, 4) 358 359 namespace, err := nsservice.GetClusterNamespace(context.TODO(), "gamma", "west") 360 require.NoError(err) 361 362 assert.Equal("west", namespace.Cluster) 363 364 namespace, err = nsservice.GetClusterNamespace(context.TODO(), "bookinfo", "east") 365 require.NoError(err) 366 367 assert.Equal("east", namespace.Cluster) 368 } 369 370 type forbiddenFake struct{ kubernetes.ClientInterface } 371 372 func (f *forbiddenFake) GetNamespace(namespace string) (*core_v1.Namespace, error) { 373 return nil, fmt.Errorf("forbidden") 374 } 375 376 // Tests that GetNamespaces won't return a namespace with the same name from another cluster 377 // if the user doesn't have access to that cluster but the namespace is cached. 378 func TestGetNamespacesForbiddenCached(t *testing.T) { 379 require := require.New(t) 380 381 conf := config.NewConfig() 382 conf.KubernetesConfig.ClusterName = "east" 383 config.Set(conf) 384 385 k8s := setupNamespaceServiceWithNs() 386 387 clientFactory := kubetest.NewK8SClientFactoryMock(nil) 388 // Two clusters: one the user has access to, one they don't. 389 clients := map[string]kubernetes.ClientInterface{ 390 "east": &forbiddenFake{k8s}, 391 "west": k8s, 392 } 393 clientFactory.SetClients(clients) 394 mockClientFactory := kubetest.NewK8SClientFactoryMock(k8s) 395 SetWithBackends(mockClientFactory, nil) 396 cache := cache.NewTestingCacheWithFactory(t, clientFactory, *conf) 397 cache.SetNamespaces( 398 k8s.GetToken(), 399 // Bookinfo is cached for the west cluster that the user has access to 400 // but NOT for the east cluster that the user doesn't have access to. 401 []models.Namespace{{Name: "bookinfo", Cluster: "west"}}, 402 ) 403 404 nsservice := NewNamespaceService(clients, clients, cache, conf) 405 // Try to get the bookinfo namespace from the home cluster. 406 _, err := nsservice.GetClusterNamespace(context.TODO(), "bookinfo", "east") 407 require.Error(err) 408 } 409 410 // TODO: Add projects tests