github.com/verrazzano/verrazzano@v1.7.1/pkg/vzchecks/precheck_test.go (about)

     1  // Copyright (c) 2022, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package vzchecks
     5  
     6  import (
     7  	"fmt"
     8  	"github.com/stretchr/testify/assert"
     9  	v1 "k8s.io/api/core/v1"
    10  	"k8s.io/apimachinery/pkg/api/resource"
    11  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  	k8scheme "k8s.io/client-go/kubernetes/scheme"
    13  	client2 "sigs.k8s.io/controller-runtime/pkg/client"
    14  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    15  	"testing"
    16  )
    17  
    18  var (
    19  	allNotMet              = []string{nodeCountReqMsg, cpuReqMsg, memoryReqMsg, storageReqMsg}
    20  	onlyStorageMet         = []string{nodeCountReqMsg, cpuReqMsg, memoryReqMsg}
    21  	onlyMemoryMet          = []string{nodeCountReqMsg, cpuReqMsg, storageReqMsg}
    22  	onlyCPUMet             = []string{nodeCountReqMsg, memoryReqMsg, storageReqMsg}
    23  	onlyNodeCountMet       = []string{cpuReqMsg, memoryReqMsg, storageReqMsg}
    24  	storageAndMemoryMet    = []string{nodeCountReqMsg, cpuReqMsg}
    25  	storageAndCPUMet       = []string{nodeCountReqMsg, memoryReqMsg}
    26  	storageAndNodeCountMet = []string{memoryReqMsg, cpuReqMsg}
    27  	memoryAndCPUMet        = []string{nodeCountReqMsg, storageReqMsg}
    28  	memoryAndNodeCountMet  = []string{cpuReqMsg, storageReqMsg}
    29  	nodeCountAndCPUMet     = []string{memoryReqMsg, storageReqMsg}
    30  	memoryNotMet           = []string{memoryReqMsg}
    31  	nodeNotMet             = []string{nodeCountReqMsg}
    32  	allMet                 = []string{}
    33  )
    34  
    35  type testData = struct {
    36  	profile   ProfileType
    37  	nodeCount int
    38  	cpu       string
    39  	memory    string
    40  	storage   string
    41  }
    42  
    43  // TestPrerequisiteCheck tests prerequisite checks for various profiles
    44  func TestPrerequisiteCheck(t *testing.T) {
    45  	var tests = []struct {
    46  		data     testData
    47  		errCount int
    48  		errTypes []string
    49  	}{
    50  		{testData{Prod, 2, "1", "423Ki", "50G"}, 7, allNotMet},
    51  		{testData{Prod, 3, "1", "12G", "50G"}, 9, onlyNodeCountMet},
    52  		{testData{Prod, 2, "1", "12G", "100G"}, 5, onlyStorageMet},
    53  		{testData{Prod, 2, "4", "12G", "50G"}, 5, onlyCPUMet},
    54  		{testData{Prod, 2, "1", "32G", "50G"}, 5, onlyMemoryMet},
    55  		{testData{Prod, 1, "6", "35G", "50G"}, 2, memoryAndCPUMet},
    56  		{testData{Prod, 2, "5", "12G", "100G"}, 3, storageAndCPUMet},
    57  		{testData{Prod, 2, "3", "32G", "100G"}, 3, storageAndMemoryMet},
    58  		{testData{Prod, 5, "6", "32G", "500G"}, 0, allMet},
    59  		{testData{Dev, 0, "1", "10G", "50G"}, 1, allNotMet},
    60  		{testData{Dev, 1, "1", "12G", "50G"}, 3, onlyNodeCountMet},
    61  		{testData{ManagedCluster, 2, "1", "12G", "100G"}, 4, storageAndNodeCountMet},
    62  		{testData{ManagedCluster, 2, "4", "12G", "50G"}, 4, nodeCountAndCPUMet},
    63  		{testData{Dev, 2, "2", "2G", "50G"}, 4, nodeCountAndCPUMet},
    64  		{testData{Dev, 1, "1", "5G", "100G"}, 2, storageAndNodeCountMet},
    65  		{testData{ManagedCluster, 2, "5", "12G", "100G"}, 2, memoryNotMet},
    66  		{testData{Dev, 1, "1", "32G", "10G"}, 2, memoryAndNodeCountMet},
    67  		{testData{Dev, 0, "", "", ""}, 1, nodeNotMet},
    68  		{testData{"unspecified", 1, "2", "24G", "50G"}, 0, allMet},
    69  	}
    70  
    71  	for _, tt := range tests {
    72  		t.Run(string(tt.data.profile), func(t *testing.T) {
    73  			client := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).WithObjects(
    74  				getNodes(tt.data)...).Build()
    75  			errs := PrerequisiteCheck(client, tt.data.profile)
    76  			assert.Equal(t, tt.errCount, len(errs))
    77  			vzReq := getVZRequirement(tt.data.profile)
    78  			for _, errMsg := range tt.errTypes {
    79  				for i := 1; i <= tt.data.nodeCount; i++ {
    80  					nodeName := fmt.Sprintf("node%d", i)
    81  					expectedMsg := getExpectedMessage(errMsg, nodeName, vzReq, tt.data)
    82  					assert.Equal(t, true, errorSliceContainsMessage(errs, expectedMsg),
    83  						"Expected error message \"%s\" not found", expectedMsg)
    84  				}
    85  			}
    86  		})
    87  	}
    88  }
    89  
    90  // errorSliceContainsMessage checks for a error message in a slice of errors
    91  // errs is the error slice to search. May be nil.
    92  // msg is the string to search for in the errs.
    93  // Returns true if the string is found in the slice and false otherwise.
    94  func errorSliceContainsMessage(errs []error, msg string) bool {
    95  	for _, err := range errs {
    96  		if err.Error() == msg {
    97  			return true
    98  		}
    99  	}
   100  	return false
   101  }
   102  
   103  func getNodes(data testData) []client2.Object {
   104  	var nodes []client2.Object
   105  	for i := 1; i <= data.nodeCount; i++ {
   106  		nodes = append(nodes, &v1.Node{
   107  			TypeMeta: metav1.TypeMeta{
   108  				Kind:       "Node",
   109  				APIVersion: "v1",
   110  			},
   111  			ObjectMeta: metav1.ObjectMeta{
   112  				Name: fmt.Sprintf("node%d", i),
   113  			},
   114  			Status: v1.NodeStatus{
   115  				Allocatable: v1.ResourceList{
   116  					"cpu":               resource.MustParse(data.cpu),
   117  					"memory":            resource.MustParse(data.memory),
   118  					"ephemeral-storage": resource.MustParse(data.storage),
   119  				},
   120  			},
   121  		})
   122  	}
   123  	return nodes
   124  }
   125  
   126  func getExpectedMessage(errMsg string, nodeName string, vzReq VZRequirement, data testData) string {
   127  	expectedMsg := ""
   128  	switch errMsg {
   129  	case nodeCountReqMsg:
   130  		expectedMsg = fmt.Sprintf(errMsg, vzReq.nodeCount, data.nodeCount)
   131  	case cpuReqMsg:
   132  		expectedMsg = fmt.Sprintf(errMsg, vzReq.cpu.allocatable.Value(),
   133  			nodeName, data.cpu)
   134  	case memoryReqMsg:
   135  		expectedMsg = fmt.Sprintf(errMsg, convertQuantityToString(vzReq.memory.allocatable),
   136  			nodeName, convertQuantityToString(resource.MustParse(data.memory)))
   137  	case storageReqMsg:
   138  		expectedMsg = fmt.Sprintf(errMsg, convertQuantityToString(vzReq.ephemeralStorage.allocatable),
   139  			nodeName, convertQuantityToString(resource.MustParse(data.storage)))
   140  	}
   141  	return expectedMsg
   142  }