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