github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/multicluster/examples/example_utils.go (about)

     1  // Copyright (c) 2021, 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 examples
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  
    10  	"github.com/verrazzano/verrazzano/pkg/k8s/resource"
    11  
    12  	clustersv1alpha1 "github.com/verrazzano/verrazzano/application-operator/apis/clusters/v1alpha1"
    13  	oamv1alpha1 "github.com/verrazzano/verrazzano/application-operator/apis/oam/v1alpha1"
    14  	appopconst "github.com/verrazzano/verrazzano/application-operator/constants"
    15  	vzconst "github.com/verrazzano/verrazzano/pkg/constants"
    16  	"github.com/verrazzano/verrazzano/pkg/k8sutil"
    17  	"github.com/verrazzano/verrazzano/tests/e2e/pkg"
    18  	"k8s.io/apimachinery/pkg/api/errors"
    19  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    20  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    21  	"k8s.io/apimachinery/pkg/runtime/schema"
    22  	"k8s.io/client-go/dynamic"
    23  )
    24  
    25  const (
    26  	helloHelidon          = "hello-helidon"
    27  	TestNamespace         = "hello-helidon" // currently only used for placement tests
    28  	multiclusterNamespace = "verrazzano-mc"
    29  	projectName           = helloHelidon
    30  	appConfigName         = helloHelidon
    31  	componentName         = "hello-helidon-component"
    32  	workloadName          = "hello-helidon-workload"
    33  	oamGroup              = "core.oam.dev"
    34  	oamVersion            = "v1alpha2"
    35  )
    36  
    37  var expectedPodsHelloHelidon = []string{"hello-helidon-deployment"}
    38  var compGvr = schema.GroupVersionResource{
    39  	Group:    oamGroup,
    40  	Version:  oamVersion,
    41  	Resource: "components",
    42  }
    43  var appConfGvr = schema.GroupVersionResource{
    44  	Group:    oamGroup,
    45  	Version:  oamVersion,
    46  	Resource: "applicationconfigurations",
    47  }
    48  
    49  var projectGvr = schema.GroupVersionResource{
    50  	Group:    clustersv1alpha1.SchemeGroupVersion.Group,
    51  	Version:  clustersv1alpha1.SchemeGroupVersion.Version,
    52  	Resource: "verrazzanoprojects",
    53  }
    54  
    55  // DeployHelloHelidonProject deploys the hello-helidon example's VerrazzanoProject to the cluster with the given kubeConfigPath
    56  func DeployHelloHelidonProject(kubeconfigPath string, sourceDir string) error {
    57  	file, err := pkg.FindTestDataFile(fmt.Sprintf("examples/multicluster/%s/verrazzano-project.yaml", sourceDir))
    58  	if err != nil {
    59  		return err
    60  	}
    61  	if err := resource.CreateOrUpdateResourceFromFileInCluster(file, kubeconfigPath); err != nil {
    62  		return fmt.Errorf("failed to create %s project resource: %v", sourceDir, err)
    63  	}
    64  	return nil
    65  }
    66  
    67  // DeployHelloHelidonApp deploys the hello-helidon example application to the cluster with the given kubeConfigPath
    68  func DeployHelloHelidonApp(kubeConfigPath string, sourceDir string) error {
    69  	file, err := pkg.FindTestDataFile(fmt.Sprintf("examples/multicluster/%s/hello-helidon-comp.yaml", sourceDir))
    70  	if err != nil {
    71  		return err
    72  	}
    73  	if err := resource.CreateOrUpdateResourceFromFileInCluster(file, kubeConfigPath); err != nil {
    74  		return fmt.Errorf("failed to create multi-cluster %s component resources: %v", sourceDir, err)
    75  	}
    76  	file, err = pkg.FindTestDataFile(fmt.Sprintf("examples/multicluster/%s/mc-hello-helidon-app.yaml", sourceDir))
    77  	if err := resource.CreateOrUpdateResourceFromFileInCluster(file, kubeConfigPath); err != nil {
    78  		return fmt.Errorf("failed to create multi-cluster %s application resource: %v", sourceDir, err)
    79  	}
    80  	return nil
    81  }
    82  
    83  // ChangePlacementToAdminCluster patches the hello-helidon example to be placed in the admin cluster
    84  // and uses the given kubeConfigPath as the cluster in which to do the patch
    85  func ChangePlacementToAdminCluster(kubeconfigPath string) error {
    86  	return changePlacement(kubeconfigPath, "examples/multicluster/hello-helidon/patch-change-placement-to-admin.yaml")
    87  }
    88  
    89  // ChangePlacementToManagedCluster patches the hello-helidon example to be placed in the managed cluster
    90  // and uses the given kubeConfigPath as the cluster in which to do the patch
    91  func ChangePlacementToManagedCluster(kubeconfigPath string) error {
    92  	return changePlacement(kubeconfigPath, "examples/multicluster/hello-helidon/patch-return-placement-to-managed1.yaml")
    93  }
    94  
    95  // changePlacement patches the hello-helidon example with the given patch file
    96  // and uses the given kubeConfigPath as the cluster in which to do the patch
    97  func changePlacement(kubeConfigPath string, patchFile string) error {
    98  	mcAppGvr := clustersv1alpha1.SchemeGroupVersion.WithResource(clustersv1alpha1.MultiClusterAppConfigResource)
    99  	vpGvr := clustersv1alpha1.SchemeGroupVersion.WithResource(clustersv1alpha1.VerrazzanoProjectResource)
   100  
   101  	file, err := pkg.FindTestDataFile(patchFile)
   102  	if err != nil {
   103  		return err
   104  	}
   105  	if err := resource.PatchResourceFromFileInCluster(mcAppGvr, TestNamespace, appConfigName, file, kubeConfigPath); err != nil {
   106  		return fmt.Errorf("failed to change placement of multicluster hello-helidon application resource: %v", err)
   107  	}
   108  	if err := resource.PatchResourceFromFileInCluster(vpGvr, multiclusterNamespace, projectName, file, kubeConfigPath); err != nil {
   109  		return fmt.Errorf("failed to create VerrazzanoProject resource: %v", err)
   110  	}
   111  	return nil
   112  }
   113  
   114  // ChangePlacementV100 patches the hello-helidon example with the given patch file
   115  // and uses the given kubeConfigPath as the cluster in which to do the patch
   116  // v1.0.0 variant of this function - requires edit to placement in mcComp resources
   117  func ChangePlacementV100(kubeConfigPath string, patchFile string, namespace string, projName string) error {
   118  	mcCompGvr := clustersv1alpha1.SchemeGroupVersion.WithResource(clustersv1alpha1.MultiClusterComponentResource)
   119  	mcAppGvr := clustersv1alpha1.SchemeGroupVersion.WithResource(clustersv1alpha1.MultiClusterAppConfigResource)
   120  	vpGvr := clustersv1alpha1.SchemeGroupVersion.WithResource(clustersv1alpha1.VerrazzanoProjectResource)
   121  
   122  	file, err := pkg.FindTestDataFile(patchFile)
   123  	if err != nil {
   124  		return err
   125  	}
   126  	if err := resource.PatchResourceFromFileInCluster(mcCompGvr, namespace, componentName, file, kubeConfigPath); err != nil {
   127  		return fmt.Errorf("failed to change placement of multicluster hello-helidon component resource: %v", err)
   128  	}
   129  	if err := resource.PatchResourceFromFileInCluster(mcAppGvr, namespace, appConfigName, file, kubeConfigPath); err != nil {
   130  		return fmt.Errorf("failed to change placement of multicluster hello-helidon application resource: %v", err)
   131  	}
   132  	if err := resource.PatchResourceFromFileInCluster(vpGvr, multiclusterNamespace, projName, file, kubeConfigPath); err != nil {
   133  		return fmt.Errorf("failed to create VerrazzanoProject resource: %v", err)
   134  	}
   135  	return nil
   136  }
   137  
   138  // VerifyMCResources verifies that the MC resources are present or absent depending on whether this is an admin
   139  // cluster and whether the resources are placed in the given cluster
   140  func VerifyMCResources(kubeconfigPath string, isAdminCluster bool, placedInThisCluster bool, namespace string) bool {
   141  	mcAppConfExists := mcAppConfExists(kubeconfigPath, namespace)
   142  	vzManagedLabelExists := verrazzanoManagedLabelExists(kubeconfigPath, namespace)
   143  	if isAdminCluster || placedInThisCluster {
   144  		// always expect MC resources on admin cluster - otherwise expect them only if placed here
   145  		if placedInThisCluster {
   146  			// the verrazzano-managed label will exist on unwrapped resources in the cluster where
   147  			// app is placed
   148  			return mcAppConfExists && vzManagedLabelExists
   149  		} else {
   150  			return mcAppConfExists && !vzManagedLabelExists
   151  		}
   152  
   153  		return mcAppConfExists
   154  	} else {
   155  		// don't expect
   156  		return !mcAppConfExists
   157  	}
   158  }
   159  
   160  // VerifyMCResourcesV100 verifies that the MC resources are present or absent depending on whether this is an admin
   161  // cluster and whether the resources are placed in the given cluster
   162  // v1.0.0 variant of this function - both mcApp and mcComp are required
   163  func VerifyMCResourcesV100(kubeconfigPath string, isAdminCluster bool, placedInThisCluster bool, namespace string) bool {
   164  	// call both mcAppConfExists and mcComponentExists and store the results, to avoid short-circuiting
   165  	// since we should check both in all cases
   166  	mcAppConfExists := mcAppConfExists(kubeconfigPath, namespace)
   167  	mcCompExists := mcComponentExists(kubeconfigPath, namespace)
   168  
   169  	if isAdminCluster || placedInThisCluster {
   170  		// always expect MC resources on admin cluster - otherwise expect them only if placed here
   171  		return mcAppConfExists && mcCompExists
   172  	} else {
   173  		// don't expect either
   174  		return !mcAppConfExists && !mcCompExists
   175  	}
   176  }
   177  
   178  // VerifyHelloHelidonInCluster verifies that the hello helidon app resources are either present or absent
   179  // depending on whether the app is placed in this cluster
   180  func VerifyHelloHelidonInCluster(kubeConfigPath string, isAdminCluster bool, placedInThisCluster bool, projectName string, namespace string) (bool, error) {
   181  	projectExists := projectExists(kubeConfigPath, projectName)
   182  	workloadExists := componentWorkloadExists(kubeConfigPath, namespace)
   183  	podsRunning, err := helloHelidonPodsRunning(kubeConfigPath, namespace)
   184  	if err != nil {
   185  		return false, err
   186  	}
   187  
   188  	if placedInThisCluster {
   189  		return projectExists && workloadExists && podsRunning, nil
   190  	} else {
   191  		if isAdminCluster {
   192  			return projectExists && !workloadExists && !podsRunning, nil
   193  		} else {
   194  			return !workloadExists && !podsRunning && !projectExists, nil
   195  		}
   196  	}
   197  }
   198  
   199  func VerifyHelloHelidonDeletedAdminCluster(kubeconfigPath string, placedInCluster bool, namespace string, projectName string) bool {
   200  	mcResDeleted := verifyMCResourcesDeleted(kubeconfigPath, namespace, projectName)
   201  	if !placedInCluster {
   202  		return mcResDeleted
   203  	}
   204  
   205  	appDeleted := VerifyAppDeleted(kubeconfigPath, namespace)
   206  
   207  	return mcResDeleted && appDeleted
   208  }
   209  
   210  func VerifyHelloHelidonDeletedInManagedCluster(kubeconfigPath string, namespace string, projectName string) bool {
   211  	mcResDeleted := verifyMCResourcesDeleted(kubeconfigPath, namespace, projectName)
   212  	appDeleted := VerifyAppDeleted(kubeconfigPath, namespace)
   213  
   214  	return mcResDeleted && appDeleted
   215  
   216  }
   217  
   218  // VerifyAppDeleted - verifies that the workload and pods are deleted on the the specified cluster
   219  func VerifyAppDeleted(kubeconfigPath string, namespace string) bool {
   220  	workloadExists := componentWorkloadExists(kubeconfigPath, namespace)
   221  	podsRunning, _ := helloHelidonPodsRunning(kubeconfigPath, namespace)
   222  	return !workloadExists && !podsRunning
   223  }
   224  
   225  func verifyMCResourcesDeleted(kubeconfigPath string, namespace string, projectName string) bool {
   226  	appConfExists := mcAppConfExists(kubeconfigPath, namespace)
   227  	mcCompExists := mcComponentExists(kubeconfigPath, namespace)
   228  	projExists := projectExists(kubeconfigPath, projectName)
   229  	compExists := componentExists(kubeconfigPath, namespace)
   230  	return !appConfExists && !compExists && !projExists && !mcCompExists
   231  }
   232  
   233  // HelidonNamespaceExists - returns true if the hello-helidon namespace exists in the given cluster
   234  func HelidonNamespaceExists(kubeconfigPath string, namespace string) bool {
   235  	_, err := pkg.GetNamespaceInCluster(namespace, kubeconfigPath)
   236  	return err == nil
   237  }
   238  
   239  func projectExists(kubeconfigPath string, projectName string) bool {
   240  	return resourceExists(projectGvr, multiclusterNamespace, projectName, kubeconfigPath)
   241  }
   242  
   243  func mcAppConfExists(kubeconfigPath string, namespace string) bool {
   244  	gvr := schema.GroupVersionResource{
   245  		Group:    clustersv1alpha1.SchemeGroupVersion.Group,
   246  		Version:  clustersv1alpha1.SchemeGroupVersion.Version,
   247  		Resource: "multiclusterapplicationconfigurations",
   248  	}
   249  	return resourceExists(gvr, namespace, appConfigName, kubeconfigPath)
   250  }
   251  
   252  func mcComponentExists(kubeconfigPath string, namespace string) bool {
   253  	gvr := schema.GroupVersionResource{
   254  		Group:    clustersv1alpha1.SchemeGroupVersion.Group,
   255  		Version:  clustersv1alpha1.SchemeGroupVersion.Version,
   256  		Resource: "multiclustercomponents",
   257  	}
   258  	return resourceExists(gvr, namespace, componentName, kubeconfigPath)
   259  }
   260  
   261  func verrazzanoManagedLabelExists(kubeconfigPath string, namespace string) bool {
   262  	appconf, err := getResource(appConfGvr, namespace, appConfigName, kubeconfigPath)
   263  	if err != nil {
   264  		return false
   265  	}
   266  	comp, err := getResource(compGvr, namespace, componentName, kubeconfigPath)
   267  	if err != nil {
   268  		return false
   269  	}
   270  	appLabels := appconf.GetLabels()
   271  	compLabels := comp.GetLabels()
   272  	return appLabels[vzconst.VerrazzanoManagedLabelKey] == appopconst.LabelVerrazzanoManagedDefault &&
   273  		compLabels[vzconst.VerrazzanoManagedLabelKey] == appopconst.LabelVerrazzanoManagedDefault
   274  }
   275  
   276  func componentExists(kubeconfigPath string, namespace string) bool {
   277  	return resourceExists(compGvr, namespace, componentName, kubeconfigPath)
   278  }
   279  
   280  func componentWorkloadExists(kubeconfigPath string, namespace string) bool {
   281  	gvr := schema.GroupVersionResource{
   282  		Group:    oamv1alpha1.SchemeGroupVersion.Group,
   283  		Version:  oamv1alpha1.SchemeGroupVersion.Version,
   284  		Resource: "verrazzanohelidonworkloads",
   285  	}
   286  	return resourceExists(gvr, namespace, workloadName, kubeconfigPath)
   287  }
   288  
   289  func resourceExists(gvr schema.GroupVersionResource, ns string, name string, kubeconfigPath string) bool {
   290  	u, err := getResource(gvr, ns, name, kubeconfigPath)
   291  	if err != nil {
   292  		if errors.IsNotFound(err) {
   293  			return false
   294  		}
   295  		pkg.Log(pkg.Error, fmt.Sprintf("Could not retrieve resource %s: %v\n", gvr.String(), err))
   296  		return false
   297  	}
   298  	return u != nil
   299  }
   300  
   301  func getResource(gvr schema.GroupVersionResource, ns string, name string, kubeconfigPath string) (*unstructured.Unstructured, error) {
   302  	config, err := k8sutil.GetKubeConfigGivenPath(kubeconfigPath)
   303  	if err != nil {
   304  		pkg.Log(pkg.Error, fmt.Sprintf("Could not get kube config: %v\n", err))
   305  		return nil, err
   306  	}
   307  	client, err := dynamic.NewForConfig(config)
   308  	if err != nil {
   309  		pkg.Log(pkg.Error, fmt.Sprintf("Could not create dynamic client: %v\n", err))
   310  		return nil, err
   311  	}
   312  
   313  	return client.Resource(gvr).Namespace(ns).Get(context.TODO(), name, v1.GetOptions{})
   314  
   315  }
   316  func helloHelidonPodsRunning(kubeconfigPath string, namespace string) (bool, error) {
   317  	result, err := pkg.PodsRunningInCluster(namespace, expectedPodsHelloHelidon, kubeconfigPath)
   318  	if err != nil {
   319  		pkg.Log(pkg.Error, fmt.Sprintf("One or more pods are not running in the namespace: %v, error: %v", namespace, err))
   320  		return false, err
   321  	}
   322  	return result, nil
   323  }