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 }