github.com/kiali/kiali@v1.84.0/graph/telemetry/istio/appender/health_test.go (about)

     1  package appender
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	osproject_v1 "github.com/openshift/api/project/v1"
     9  	"github.com/prometheus/common/model"
    10  	"github.com/stretchr/testify/assert"
    11  	apps_v1 "k8s.io/api/apps/v1"
    12  	core_v1 "k8s.io/api/core/v1"
    13  	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  	"k8s.io/apimachinery/pkg/runtime"
    15  
    16  	"github.com/kiali/kiali/business"
    17  	"github.com/kiali/kiali/config"
    18  	"github.com/kiali/kiali/graph"
    19  	"github.com/kiali/kiali/kubernetes"
    20  	"github.com/kiali/kiali/kubernetes/cache"
    21  	"github.com/kiali/kiali/kubernetes/kubetest"
    22  	"github.com/kiali/kiali/models"
    23  	"github.com/kiali/kiali/prometheus/prometheustest"
    24  )
    25  
    26  const (
    27  	rateDefinition         = "400,10,20,http,inbound"
    28  	rateWorkloadDefinition = "4xx,20,30,http,inbound"
    29  )
    30  
    31  func TestServicesHealthConfigPasses(t *testing.T) {
    32  	conf := config.NewConfig()
    33  	conf.KubernetesConfig.ClusterName = config.DefaultClusterID
    34  	config.Set(conf)
    35  	trafficMap := buildServiceTrafficMap()
    36  	businessLayer := setupHealthConfig(t, buildFakeServicesHealth(rateDefinition), buildFakeWorkloadDeploymentsHealth(rateWorkloadDefinition), buildFakePodsHealth(rateWorkloadDefinition))
    37  
    38  	globalInfo := graph.NewAppenderGlobalInfo()
    39  	globalInfo.Business = businessLayer
    40  	namespaceInfo := graph.NewAppenderNamespaceInfo("testNamespace")
    41  
    42  	a := HealthAppender{}
    43  	a.AppendGraph(trafficMap, globalInfo, namespaceInfo)
    44  
    45  	for _, node := range trafficMap {
    46  		srv, ok := node.Metadata[graph.HasHealthConfig].(map[string]string)
    47  		assert.True(t, ok)
    48  		assert.Equal(t, rateDefinition, srv[string(models.RateHealthAnnotation)])
    49  	}
    50  }
    51  
    52  func TestServicesHealthNoConfigPasses(t *testing.T) {
    53  	trafficMap := buildServiceTrafficMap()
    54  	businessLayer := setupHealthConfig(t, buildFakeServicesHealth(""), buildFakeWorkloadDeploymentsHealth(""), buildFakePodsHealth(""))
    55  
    56  	globalInfo := graph.NewAppenderGlobalInfo()
    57  	globalInfo.Business = businessLayer
    58  	namespaceInfo := graph.NewAppenderNamespaceInfo("testNamespace")
    59  
    60  	a := HealthAppender{}
    61  	a.AppendGraph(trafficMap, globalInfo, namespaceInfo)
    62  
    63  	for _, node := range trafficMap {
    64  		srv, ok := node.Metadata[graph.HasHealthConfig].(map[string]string)
    65  		assert.True(t, ok)
    66  		assert.Equal(t, "", srv[string(models.RateHealthAnnotation)])
    67  	}
    68  }
    69  
    70  func TestWorkloadHealthConfigPasses(t *testing.T) {
    71  	trafficMap := buildWorkloadTrafficMap()
    72  	businessLayer := setupHealthConfig(t, buildFakeServicesHealth(rateDefinition), buildFakeWorkloadDeploymentsHealth(rateWorkloadDefinition), buildFakePodsHealth(rateWorkloadDefinition))
    73  
    74  	globalInfo := graph.NewAppenderGlobalInfo()
    75  	globalInfo.Business = businessLayer
    76  	namespaceInfo := graph.NewAppenderNamespaceInfo("testNamespace")
    77  
    78  	a := HealthAppender{}
    79  	a.AppendGraph(trafficMap, globalInfo, namespaceInfo)
    80  
    81  	for _, node := range trafficMap {
    82  		srv, ok := node.Metadata[graph.HasHealthConfig].(map[string]string)
    83  		assert.True(t, ok)
    84  		assert.Equal(t, rateWorkloadDefinition, srv[string(models.RateHealthAnnotation)])
    85  	}
    86  }
    87  
    88  func TestWorkloadHealthNoConfigPasses(t *testing.T) {
    89  	trafficMap := buildWorkloadTrafficMap()
    90  	businessLayer := setupHealthConfig(t, buildFakeServicesHealth(""), buildFakeWorkloadDeploymentsHealth(""), buildFakePodsHealth(""))
    91  
    92  	globalInfo := graph.NewAppenderGlobalInfo()
    93  	globalInfo.Business = businessLayer
    94  	namespaceInfo := graph.NewAppenderNamespaceInfo("testNamespace")
    95  
    96  	a := HealthAppender{}
    97  	a.AppendGraph(trafficMap, globalInfo, namespaceInfo)
    98  
    99  	for _, node := range trafficMap {
   100  		srv, ok := node.Metadata[graph.HasHealthConfig].(map[string]string)
   101  		assert.True(t, ok)
   102  		assert.Equal(t, "", srv[string(models.RateHealthAnnotation)])
   103  	}
   104  }
   105  
   106  func TestHealthDataPresent(t *testing.T) {
   107  	assert := assert.New(t)
   108  
   109  	svcNodes := buildServiceTrafficMap()
   110  	appNodes := buildAppTrafficMap()
   111  	wkNodes := buildWorkloadTrafficMap()
   112  	trafficMap := make(graph.TrafficMap)
   113  	for k, v := range svcNodes {
   114  		trafficMap[k] = v
   115  	}
   116  	for k, v := range appNodes {
   117  		trafficMap[k] = v
   118  	}
   119  	for k, v := range wkNodes {
   120  		trafficMap[k] = v
   121  	}
   122  	businessLayer := setupHealthConfig(t, buildFakeServicesHealth(rateDefinition), buildFakeWorkloadDeploymentsHealth(rateWorkloadDefinition), buildFakePodsHealth(rateWorkloadDefinition))
   123  
   124  	globalInfo := graph.NewAppenderGlobalInfo()
   125  	globalInfo.Business = businessLayer
   126  	namespaceInfo := graph.NewAppenderNamespaceInfo("testNamespace")
   127  
   128  	a := HealthAppender{}
   129  	a.AppendGraph(trafficMap, globalInfo, namespaceInfo)
   130  
   131  	for _, node := range trafficMap {
   132  		assert.Contains(node.Metadata, graph.HealthData)
   133  	}
   134  }
   135  
   136  func TestHealthDataPresent200SvcWk(t *testing.T) {
   137  	assert := assert.New(t)
   138  
   139  	svcNodes := buildServiceTrafficMap()
   140  	appNodes := buildAppTrafficMap()
   141  	wkNodes := buildWorkloadTrafficMap()
   142  	trafficMap := make(graph.TrafficMap)
   143  	var (
   144  		svc *graph.Node
   145  		wk  *graph.Node
   146  	)
   147  	for k, v := range svcNodes {
   148  		trafficMap[k] = v
   149  		svc = v
   150  	}
   151  	for k, v := range appNodes {
   152  		trafficMap[k] = v
   153  	}
   154  	for k, v := range wkNodes {
   155  		trafficMap[k] = v
   156  		wk = v
   157  	}
   158  	edge := svc.AddEdge(wk)
   159  	/* Example of edge data:
   160  	{
   161  	 	"traffic": {
   162  	 		"protocol": "http",
   163  	 		"rates": {
   164  	 			"http": "1.93",
   165  	 			"httpPercentReq": "100.0"
   166  	 		},
   167  	 		"responses": {
   168  	 			"200": {
   169  	 				"flags": {
   170  	 					"-": "100.0"
   171  	 				},
   172  	 				"hosts": {
   173  	 					"v-server.beta.svc.cluster.local": "100.0"
   174  	 				}
   175  	 			}
   176  	 		}
   177  	 	}
   178  	 }
   179  	*/
   180  	edge.Metadata[graph.ProtocolKey] = "http"
   181  	edge.Metadata[graph.MetadataKey(graph.HTTP.EdgeResponses)] = graph.Responses{
   182  		"200": &graph.ResponseDetail{
   183  			Flags: graph.ResponseFlags{"-": 100.0},
   184  			Hosts: map[string]float64{"v-server.beta.svc.cluster.local": 100.0},
   185  		},
   186  	}
   187  	businessLayer := setupHealthConfig(t, buildFakeServicesHealth(rateDefinition), buildFakeWorkloadDeploymentsHealth(rateWorkloadDefinition), buildFakePodsHealth(rateWorkloadDefinition))
   188  
   189  	globalInfo := graph.NewAppenderGlobalInfo()
   190  	globalInfo.Business = businessLayer
   191  	namespaceInfo := graph.NewAppenderNamespaceInfo("testNamespace")
   192  
   193  	a := HealthAppender{}
   194  	a.AppendGraph(trafficMap, globalInfo, namespaceInfo)
   195  
   196  	for _, node := range trafficMap {
   197  		assert.Contains(node.Metadata, graph.HealthData)
   198  	}
   199  	source := trafficMap[svc.ID]
   200  	sourceHealth := source.Metadata[graph.HealthData].(*models.ServiceHealth)
   201  	assert.Equal(sourceHealth.Requests.Outbound["http"]["200"], 100.0)
   202  
   203  	dest := trafficMap[wk.ID]
   204  	destHealth := dest.Metadata[graph.HealthData].(*models.WorkloadHealth)
   205  	assert.Equal(destHealth.Requests.Inbound["http"]["200"], 100.0)
   206  }
   207  
   208  func TestHealthDataPresent200500WkSvc(t *testing.T) {
   209  	assert := assert.New(t)
   210  
   211  	conf := config.NewConfig()
   212  	conf.KubernetesConfig.ClusterName = config.DefaultClusterID
   213  	config.Set(conf)
   214  	svcNodes := buildServiceTrafficMap()
   215  	appNodes := buildAppTrafficMap()
   216  	wkNodes := buildWorkloadTrafficMap()
   217  	trafficMap := make(graph.TrafficMap)
   218  	var (
   219  		svc *graph.Node
   220  		wk  *graph.Node
   221  	)
   222  	for k, v := range svcNodes {
   223  		trafficMap[k] = v
   224  		svc = v
   225  	}
   226  	for k, v := range appNodes {
   227  		trafficMap[k] = v
   228  	}
   229  	for k, v := range wkNodes {
   230  		trafficMap[k] = v
   231  		wk = v
   232  	}
   233  	edge := wk.AddEdge(svc)
   234  	edge.Metadata[graph.ProtocolKey] = "http"
   235  	edge.Metadata[graph.MetadataKey(graph.HTTP.EdgeResponses)] = graph.Responses{
   236  		"200": &graph.ResponseDetail{
   237  			Flags: graph.ResponseFlags{"-": 100.0},
   238  			Hosts: map[string]float64{"v-server.beta.svc.cluster.local": 100.0},
   239  		},
   240  		"500": &graph.ResponseDetail{
   241  			Flags: graph.ResponseFlags{"-": 10.0},
   242  			Hosts: map[string]float64{"v-server.beta.svc.cluster.local": 10.0},
   243  		},
   244  	}
   245  	businessLayer := setupHealthConfig(t, buildFakeServicesHealth(rateDefinition), buildFakeWorkloadDeploymentsHealth(rateWorkloadDefinition), buildFakePodsHealth(rateWorkloadDefinition))
   246  
   247  	globalInfo := graph.NewAppenderGlobalInfo()
   248  	globalInfo.Business = businessLayer
   249  	namespaceInfo := graph.NewAppenderNamespaceInfo("testNamespace")
   250  
   251  	a := HealthAppender{}
   252  	a.AppendGraph(trafficMap, globalInfo, namespaceInfo)
   253  
   254  	for _, node := range trafficMap {
   255  		assert.Contains(node.Metadata, graph.HealthData)
   256  	}
   257  	source := trafficMap[wk.ID]
   258  	sourceHealth := source.Metadata[graph.HealthData].(*models.WorkloadHealth)
   259  	assert.Equal(sourceHealth.Requests.Outbound["http"]["200"], 100.0)
   260  	assert.Equal(sourceHealth.Requests.Outbound["http"]["500"], 10.0)
   261  
   262  	dest := trafficMap[svc.ID]
   263  	destHealth := dest.Metadata[graph.HealthData].(*models.ServiceHealth)
   264  	assert.Equal(destHealth.Requests.Inbound["http"]["200"], 100.0)
   265  	assert.Equal(destHealth.Requests.Inbound["http"]["500"], 10.0)
   266  }
   267  
   268  func TestHealthDataPresentToApp(t *testing.T) {
   269  	assert := assert.New(t)
   270  
   271  	conf := config.NewConfig()
   272  	conf.KubernetesConfig.ClusterName = config.DefaultClusterID
   273  	config.Set(conf)
   274  	svcNodes := buildServiceTrafficMap()
   275  	appNodes := buildAppTrafficMap()
   276  	wkNodes := buildWorkloadTrafficMap()
   277  	trafficMap := make(graph.TrafficMap)
   278  	var (
   279  		svc *graph.Node
   280  		app *graph.Node
   281  	)
   282  	for k, v := range svcNodes {
   283  		trafficMap[k] = v
   284  		svc = v
   285  	}
   286  	for k, v := range appNodes {
   287  		trafficMap[k] = v
   288  		app = v
   289  	}
   290  	for k, v := range wkNodes {
   291  		trafficMap[k] = v
   292  	}
   293  	edge := svc.AddEdge(app)
   294  	edge.Metadata[graph.ProtocolKey] = "http"
   295  	edge.Metadata[graph.MetadataKey(graph.HTTP.EdgeResponses)] = graph.Responses{
   296  		"200": &graph.ResponseDetail{
   297  			Flags: graph.ResponseFlags{"-": 100.0},
   298  			Hosts: map[string]float64{"v-server.beta.svc.cluster.local": 100.0},
   299  		},
   300  	}
   301  	businessLayer := setupHealthConfig(t, buildFakeServicesHealth(rateDefinition), buildFakeWorkloadDeploymentsHealth(rateWorkloadDefinition), buildFakePodsHealth(rateWorkloadDefinition))
   302  
   303  	globalInfo := graph.NewAppenderGlobalInfo()
   304  	globalInfo.Business = businessLayer
   305  	namespaceInfo := graph.NewAppenderNamespaceInfo("testNamespace")
   306  
   307  	a := HealthAppender{}
   308  	a.AppendGraph(trafficMap, globalInfo, namespaceInfo)
   309  
   310  	for _, node := range trafficMap {
   311  		assert.Contains(node.Metadata, graph.HealthData)
   312  	}
   313  	source := trafficMap[svc.ID]
   314  	sourceHealth := source.Metadata[graph.HealthData].(*models.ServiceHealth)
   315  	assert.Equal(sourceHealth.Requests.Outbound["http"]["200"], 100.0)
   316  
   317  	dest := trafficMap[app.ID]
   318  	destHealth := dest.Metadata[graph.HealthData].(*models.AppHealth)
   319  	assert.Equal(destHealth.Requests.Inbound["http"]["200"], 100.0)
   320  }
   321  
   322  func TestHealthDataPresentFromApp(t *testing.T) {
   323  	assert := assert.New(t)
   324  
   325  	conf := config.NewConfig()
   326  	conf.KubernetesConfig.ClusterName = config.DefaultClusterID
   327  	config.Set(conf)
   328  	svcNodes := buildServiceTrafficMap()
   329  	appNodes := buildAppTrafficMap()
   330  	wkNodes := buildWorkloadTrafficMap()
   331  	trafficMap := make(graph.TrafficMap)
   332  	var (
   333  		svc *graph.Node
   334  		app *graph.Node
   335  	)
   336  	for k, v := range svcNodes {
   337  		trafficMap[k] = v
   338  		svc = v
   339  	}
   340  	for k, v := range appNodes {
   341  		trafficMap[k] = v
   342  		app = v
   343  	}
   344  	for k, v := range wkNodes {
   345  		trafficMap[k] = v
   346  		app.Workload = v.Workload
   347  	}
   348  	edge := app.AddEdge(svc)
   349  	edge.Metadata[graph.ProtocolKey] = "http"
   350  	edge.Metadata[graph.MetadataKey(graph.HTTP.EdgeResponses)] = graph.Responses{
   351  		"200": &graph.ResponseDetail{
   352  			Flags: graph.ResponseFlags{"-": 100.0},
   353  			Hosts: map[string]float64{"v-server.beta.svc.cluster.local": 100.0},
   354  		},
   355  	}
   356  	businessLayer := setupHealthConfig(t, buildFakeServicesHealth(rateDefinition), buildFakeWorkloadDeploymentsHealth(rateWorkloadDefinition), buildFakePodsHealth(rateWorkloadDefinition))
   357  
   358  	globalInfo := graph.NewAppenderGlobalInfo()
   359  	globalInfo.Business = businessLayer
   360  	namespaceInfo := graph.NewAppenderNamespaceInfo("testNamespace")
   361  
   362  	a := HealthAppender{}
   363  	a.AppendGraph(trafficMap, globalInfo, namespaceInfo)
   364  
   365  	for _, node := range trafficMap {
   366  		assert.Contains(node.Metadata, graph.HealthData)
   367  	}
   368  	source := trafficMap[app.ID]
   369  	sourceHealth := source.Metadata[graph.HealthData].(*models.AppHealth)
   370  	assert.Equal(sourceHealth.Requests.Outbound["http"]["200"], 100.0)
   371  	assert.Contains(source.Metadata, graph.HealthDataApp)
   372  	sourceAppHealth := source.Metadata[graph.HealthDataApp].(*models.AppHealth)
   373  	assert.Equal(sourceAppHealth.Requests.Outbound["http"]["200"], 100.0)
   374  
   375  	dest := trafficMap[svc.ID]
   376  	destHealth := dest.Metadata[graph.HealthData].(*models.ServiceHealth)
   377  	assert.Equal(destHealth.Requests.Inbound["http"]["200"], 100.0)
   378  }
   379  
   380  func TestHealthDataBadResponses(t *testing.T) {
   381  	assert := assert.New(t)
   382  
   383  	conf := config.NewConfig()
   384  	conf.KubernetesConfig.ClusterName = config.DefaultClusterID
   385  	config.Set(conf)
   386  	svcNodes := buildServiceTrafficMap()
   387  	appNodes := buildAppTrafficMap()
   388  	wkNodes := buildWorkloadTrafficMap()
   389  	trafficMap := make(graph.TrafficMap)
   390  	var (
   391  		svc *graph.Node
   392  		wk  *graph.Node
   393  		app *graph.Node
   394  	)
   395  	for k, v := range svcNodes {
   396  		trafficMap[k] = v
   397  		svc = v
   398  	}
   399  	for k, v := range appNodes {
   400  		trafficMap[k] = v
   401  		app = v
   402  	}
   403  	for k, v := range wkNodes {
   404  		trafficMap[k] = v
   405  		wk = v
   406  	}
   407  	edge1 := app.AddEdge(svc)
   408  	edge1.Metadata[graph.ProtocolKey] = "badprotocol"
   409  	edge1.Metadata[graph.MetadataKey("badprotocol")] = graph.Responses{
   410  		"200": &graph.ResponseDetail{
   411  			Flags: graph.ResponseFlags{"-": 100.0},
   412  			Hosts: map[string]float64{"v-server.beta.svc.cluster.local": 100.0},
   413  		},
   414  	}
   415  	edge2 := wk.AddEdge(svc)
   416  	edge2.Metadata[graph.ProtocolKey] = 20000
   417  	businessLayer := setupHealthConfig(t, buildFakeServicesHealth(rateDefinition), buildFakeWorkloadDeploymentsHealth(rateWorkloadDefinition), buildFakePodsHealth(rateWorkloadDefinition))
   418  
   419  	globalInfo := graph.NewAppenderGlobalInfo()
   420  	globalInfo.Business = businessLayer
   421  	namespaceInfo := graph.NewAppenderNamespaceInfo("testNamespace")
   422  
   423  	a := HealthAppender{}
   424  	a.AppendGraph(trafficMap, globalInfo, namespaceInfo)
   425  
   426  	for _, node := range trafficMap {
   427  		assert.Contains(node.Metadata, graph.HealthData)
   428  	}
   429  	source := trafficMap[app.ID]
   430  	sourceHealth := source.Metadata[graph.HealthData].(*models.AppHealth)
   431  	assert.Empty(sourceHealth.Requests.Outbound)
   432  
   433  	dest := trafficMap[svc.ID]
   434  	destHealth := dest.Metadata[graph.HealthData].(*models.ServiceHealth)
   435  	assert.Empty(destHealth.Requests.Inbound)
   436  }
   437  
   438  func TestIdleNodesHaveHealthData(t *testing.T) {
   439  	assert := assert.New(t)
   440  
   441  	conf := config.NewConfig()
   442  	conf.KubernetesConfig.ClusterName = config.DefaultClusterID
   443  	config.Set(conf)
   444  	trafficMap := make(graph.TrafficMap)
   445  	idleNode, _ := graph.NewNode("cluster-default", "testNamespace", "svc", "", "", "", "v1", graph.GraphTypeVersionedApp)
   446  	trafficMap[idleNode.ID] = idleNode
   447  	idleNode.Metadata[graph.IsIdle] = true
   448  	idleNode.Metadata[graph.IsInaccessible] = true
   449  	businessLayer := setupHealthConfig(t, buildFakeServicesHealth(rateDefinition), buildFakeWorkloadDeploymentsHealth(rateWorkloadDefinition), buildFakePodsHealth(rateWorkloadDefinition))
   450  
   451  	globalInfo := graph.NewAppenderGlobalInfo()
   452  	globalInfo.Business = businessLayer
   453  	namespaceInfo := graph.NewAppenderNamespaceInfo("testNamespace")
   454  
   455  	a := HealthAppender{}
   456  	a.AppendGraph(trafficMap, globalInfo, namespaceInfo)
   457  
   458  	assert.NotNil(trafficMap[idleNode.ID].Metadata[graph.HealthData])
   459  }
   460  
   461  type cacheWithServicesError struct {
   462  	cache.KialiCache
   463  	kubeCache cache.KubeCache
   464  }
   465  
   466  func (c *cacheWithServicesError) GetKubeCache(cluster string) (cache.KubeCache, error) {
   467  	return c.kubeCache, nil
   468  }
   469  
   470  type servicesError struct {
   471  	cache.KubeCache
   472  	errorMsg string
   473  }
   474  
   475  func (s *servicesError) GetServicesBySelectorLabels(namespace string, selectorLabels map[string]string) ([]core_v1.Service, error) {
   476  	return nil, fmt.Errorf(s.errorMsg)
   477  }
   478  
   479  func TestErrorCausesPanic(t *testing.T) {
   480  	assert := assert.New(t)
   481  
   482  	trafficMap := buildAppTrafficMap()
   483  	objects := []runtime.Object{
   484  		&osproject_v1.Project{ObjectMeta: meta_v1.ObjectMeta{Name: "testNamespace"}},
   485  		&core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "testNamespace"}},
   486  	}
   487  	for _, obj := range buildFakeWorkloadDeploymentsHealth(rateDefinition) {
   488  		o := obj
   489  		objects = append(objects, &o)
   490  	}
   491  	for _, obj := range buildFakePodsHealth(rateDefinition) {
   492  		o := obj
   493  		objects = append(objects, &o)
   494  	}
   495  	var k8s kubernetes.ClientInterface = kubetest.NewFakeK8sClient(objects...)
   496  
   497  	conf := config.NewConfig()
   498  	conf.KubernetesConfig.ClusterName = config.DefaultClusterID
   499  	config.Set(conf)
   500  	conf.ExternalServices.Istio.IstioAPIEnabled = false
   501  	config.Set(conf)
   502  	cache := cache.NewTestingCache(t, k8s, *conf)
   503  	const panicErrMsg = "test error! This should cause a panic"
   504  	cache = &cacheWithServicesError{KialiCache: cache, kubeCache: &servicesError{KubeCache: cache.GetKubeCaches()[conf.KubernetesConfig.ClusterName], errorMsg: panicErrMsg}}
   505  	business.WithKialiCache(cache)
   506  
   507  	prom := new(prometheustest.PromClientMock)
   508  	prom.MockNamespaceServicesRequestRates("testNamespace", "0s", time.Unix(0, 0), model.Vector{})
   509  	prom.MockAllRequestRates("testNamespace", conf.KubernetesConfig.ClusterName, "0s", time.Unix(0, 0), model.Vector{})
   510  	k8sclients := make(map[string]kubernetes.ClientInterface)
   511  	k8sclients[conf.KubernetesConfig.ClusterName] = k8s
   512  	businessLayer := business.NewWithBackends(k8sclients, k8sclients, prom, nil)
   513  
   514  	globalInfo := graph.NewAppenderGlobalInfo()
   515  	globalInfo.Business = businessLayer
   516  	namespaceInfo := graph.NewAppenderNamespaceInfo("testNamespace")
   517  
   518  	a := HealthAppender{}
   519  
   520  	assert.PanicsWithValue(panicErrMsg, func() { a.AppendGraph(trafficMap, globalInfo, namespaceInfo) })
   521  }
   522  
   523  func TestMultiClusterHealthConfig(t *testing.T) {
   524  	assert := assert.New(t)
   525  
   526  	trafficMap := graph.NewTrafficMap()
   527  	eastNode, _ := graph.NewNode("east", "testNamespace", "", "testNamespace", graph.Unknown, "myTest", graph.Unknown, graph.GraphTypeVersionedApp)
   528  	trafficMap[eastNode.ID] = eastNode
   529  	westNode, _ := graph.NewNode("west", "testNamespace", "", "testNamespace", graph.Unknown, "myTest", graph.Unknown, graph.GraphTypeVersionedApp)
   530  	trafficMap[westNode.ID] = westNode
   531  	objects := []runtime.Object{
   532  		&osproject_v1.Project{ObjectMeta: meta_v1.ObjectMeta{Name: "testNamespace"}},
   533  		&core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "testNamespace"}},
   534  	}
   535  	westClient := kubetest.NewFakeK8sClient(objects...)
   536  	for _, obj := range buildFakeWorkloadDeploymentsHealth(rateDefinition) {
   537  		o := obj
   538  		objects = append(objects, &o)
   539  	}
   540  	for _, obj := range buildFakePodsHealth(rateDefinition) {
   541  		o := obj
   542  		objects = append(objects, &o)
   543  	}
   544  	for _, obj := range buildFakeServicesHealth(rateDefinition) {
   545  		o := obj
   546  		objects = append(objects, &o)
   547  	}
   548  
   549  	factory := kubetest.NewK8SClientFactoryMock(nil)
   550  	factory.Clients = map[string]kubernetes.ClientInterface{
   551  		"east": kubetest.NewFakeK8sClient(objects...),
   552  		"west": westClient,
   553  	}
   554  
   555  	conf := config.NewConfig()
   556  	conf.KubernetesConfig.ClusterName = config.DefaultClusterID
   557  	config.Set(conf)
   558  	conf.ExternalServices.Istio.IstioAPIEnabled = false
   559  	conf.KubernetesConfig.ClusterName = "east"
   560  	config.Set(conf)
   561  	cache := cache.NewTestingCacheWithFactory(t, factory, *conf)
   562  	business.WithKialiCache(cache)
   563  
   564  	prom := new(prometheustest.PromClientMock)
   565  	prom.MockNamespaceServicesRequestRates("testNamespace", "0s", time.Unix(0, 0), model.Vector{})
   566  	prom.MockAllRequestRates("testNamespace", conf.KubernetesConfig.ClusterName, "0s", time.Unix(0, 0), model.Vector{})
   567  	businessLayer := business.NewWithBackends(factory.GetSAClients(), factory.GetSAClients(), prom, nil)
   568  
   569  	globalInfo := graph.NewAppenderGlobalInfo()
   570  	globalInfo.Business = businessLayer
   571  	namespaceInfo := graph.NewAppenderNamespaceInfo("testNamespace")
   572  
   573  	a := HealthAppender{}
   574  	a.AppendGraph(trafficMap, globalInfo, namespaceInfo)
   575  
   576  	assert.Contains(eastNode.Metadata, graph.HealthData)
   577  	assert.Contains(westNode.Metadata, graph.HealthData)
   578  	assert.NotEmpty(eastNode.Metadata[graph.HealthData].(*models.AppHealth).WorkloadStatuses)
   579  	assert.Empty(westNode.Metadata[graph.HealthData].(*models.AppHealth).WorkloadStatuses)
   580  }
   581  
   582  func buildFakeServicesHealth(rate string) []core_v1.Service {
   583  	annotationMap := map[string]string{}
   584  	if rate != "" {
   585  		annotationMap[string(models.RateHealthAnnotation)] = rate
   586  	}
   587  	return []core_v1.Service{
   588  		{
   589  			ObjectMeta: meta_v1.ObjectMeta{
   590  				Name:        "svc",
   591  				Namespace:   "testNamespace",
   592  				Annotations: annotationMap,
   593  			},
   594  		},
   595  	}
   596  }
   597  
   598  func buildFakeWorkloadDeploymentsHealth(rate string) []apps_v1.Deployment {
   599  	apps := buildFakeWorkloadDeployments()
   600  	if rate != "" {
   601  		apps[0].ObjectMeta.Annotations = map[string]string{string(models.RateHealthAnnotation): rate}
   602  	}
   603  	return apps
   604  }
   605  
   606  func buildFakePodsHealth(rate string) []core_v1.Pod {
   607  	pods := buildFakeWorkloadPods()
   608  	if rate != "" {
   609  		pods[0].ObjectMeta.Annotations[string(models.RateHealthAnnotation)] = rate
   610  	}
   611  	return pods
   612  }
   613  
   614  func setupHealthConfig(t *testing.T, services []core_v1.Service, deployments []apps_v1.Deployment, pods []core_v1.Pod) *business.Layer {
   615  	objects := []runtime.Object{
   616  		&osproject_v1.Project{ObjectMeta: meta_v1.ObjectMeta{Name: "testNamespace"}},
   617  		&core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "testNamespace"}},
   618  	}
   619  	for _, obj := range services {
   620  		o := obj
   621  		objects = append(objects, &o)
   622  	}
   623  	for _, obj := range deployments {
   624  		o := obj
   625  		objects = append(objects, &o)
   626  	}
   627  	for _, obj := range pods {
   628  		o := obj
   629  		objects = append(objects, &o)
   630  	}
   631  	k8s := kubetest.NewFakeK8sClient(objects...)
   632  
   633  	conf := config.NewConfig()
   634  	conf.KubernetesConfig.ClusterName = config.DefaultClusterID
   635  	config.Set(conf)
   636  	conf.ExternalServices.Istio.IstioAPIEnabled = false
   637  	config.Set(conf)
   638  	business.SetupBusinessLayer(t, k8s, *conf)
   639  
   640  	prom := new(prometheustest.PromClientMock)
   641  	prom.MockNamespaceServicesRequestRates("testNamespace", "0s", time.Unix(0, 0), model.Vector{})
   642  	prom.MockAllRequestRates("testNamespace", conf.KubernetesConfig.ClusterName, "0s", time.Unix(0, 0), model.Vector{})
   643  	k8sclients := make(map[string]kubernetes.ClientInterface)
   644  	k8sclients[conf.KubernetesConfig.ClusterName] = k8s
   645  	businessLayer := business.NewWithBackends(k8sclients, k8sclients, prom, nil)
   646  	return businessLayer
   647  }