github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/pkg/kubernetes.go (about) 1 // Copyright (c) 2020, 2023, 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 pkg 5 6 import ( 7 "bytes" 8 "context" 9 "errors" 10 "fmt" 11 "net/http" 12 "os" 13 "strings" 14 "time" 15 16 certmanagerv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" 17 "github.com/google/uuid" 18 "github.com/onsi/gomega" 19 vaoClient "github.com/verrazzano/verrazzano/application-operator/clientset/versioned" 20 vcoClient "github.com/verrazzano/verrazzano/cluster-operator/clientset/versioned" 21 "github.com/verrazzano/verrazzano/pkg/k8s/verrazzano" 22 "github.com/verrazzano/verrazzano/pkg/k8sutil" 23 "github.com/verrazzano/verrazzano/pkg/semver" 24 "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1alpha1" 25 "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1beta1" 26 vpoClient "github.com/verrazzano/verrazzano/platform-operator/clientset/versioned" 27 istionetv1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1" 28 appsv1 "k8s.io/api/apps/v1" 29 v1 "k8s.io/api/authorization/v1" 30 corev1 "k8s.io/api/core/v1" 31 netv1 "k8s.io/api/networking/v1" 32 rbacv1 "k8s.io/api/rbac/v1" 33 apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 34 apiextv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1" 35 k8serrors "k8s.io/apimachinery/pkg/api/errors" 36 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 37 "k8s.io/apimachinery/pkg/labels" 38 "k8s.io/apimachinery/pkg/runtime" 39 "k8s.io/apimachinery/pkg/types" 40 "k8s.io/apimachinery/pkg/util/wait" 41 "k8s.io/client-go/dynamic" 42 "k8s.io/client-go/kubernetes" 43 "k8s.io/client-go/kubernetes/scheme" 44 restclient "k8s.io/client-go/rest" 45 "k8s.io/client-go/tools/clientcmd" 46 clientcmdapi "k8s.io/client-go/tools/clientcmd/api" 47 "k8s.io/client-go/tools/remotecommand" 48 "sigs.k8s.io/controller-runtime/pkg/client" 49 ) 50 51 const ( 52 dockerconfigjsonTemplate string = "{\"auths\":{\"%v\":{\"username\":\"%v\",\"password\":\"%v\",\"auth\":\"%v\"}}}" 53 verrazzanoErrorTemplate = "Error Verrazzano Resource: %v" 54 ) 55 56 // DoesCRDExist returns whether a CRD with the given name exists for the cluster 57 func DoesCRDExist(crdName string) (bool, error) { 58 crds, err := ListCRDs() 59 if err != nil { 60 return false, err 61 } 62 63 for i := range crds.Items { 64 if strings.Compare(crds.Items[i].ObjectMeta.Name, crdName) == 0 { 65 return true, nil 66 } 67 } 68 69 return false, nil 70 } 71 72 // DoesNamespaceExist returns whether a namespace with the given name exists for the cluster set in the environment 73 func DoesNamespaceExist(name string) (bool, error) { 74 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 75 if err != nil { 76 Log(Error, fmt.Sprintf("Failed getting kubeconfig: %v", err)) 77 return false, err 78 } 79 80 return DoesNamespaceExistInCluster(name, kubeconfigPath) 81 } 82 83 // DoesNamespaceExistInCluster returns whether a namespace with the given name exists in the specified cluster 84 func DoesNamespaceExistInCluster(name string, kubeconfigPath string) (bool, error) { 85 // Get the Kubernetes clientset 86 clientset, err := GetKubernetesClientsetForCluster(kubeconfigPath) 87 if err != nil { 88 return false, err 89 } 90 91 namespace, err := clientset.CoreV1().Namespaces().Get(context.TODO(), name, metav1.GetOptions{}) 92 if err != nil && !k8serrors.IsNotFound(err) { 93 Log(Error, fmt.Sprintf("Failed to get namespace %s: %v", name, err)) 94 return false, err 95 } 96 97 return namespace != nil && len(namespace.Name) > 0, nil 98 } 99 100 // ListCRDs returns the list of CRDs in a cluster 101 func ListCRDs() (*apiext.CustomResourceDefinitionList, error) { 102 // use the current context in the kubeconfig 103 config, err := k8sutil.GetKubeConfig() 104 if err != nil { 105 return nil, err 106 } 107 apixClient, err := apiextv1.NewForConfig(config) 108 if err != nil { 109 Log(Error, "Could not get apix client") 110 return nil, err 111 } 112 113 crds, err := apixClient.CustomResourceDefinitions().List(context.TODO(), metav1.ListOptions{}) 114 if err != nil { 115 Log(Error, fmt.Sprintf("Failed to list CRDS: %v", err)) 116 return nil, err 117 } 118 119 return crds, nil 120 } 121 122 // ListNamespaces returns a namespace list for the given list options 123 func ListNamespaces(opts metav1.ListOptions) (*corev1.NamespaceList, error) { 124 client, err := k8sutil.GetKubernetesClientset() 125 if err != nil { 126 return nil, err 127 } 128 return client.CoreV1().Namespaces().List(context.TODO(), opts) 129 } 130 131 // ListPods returns a pod list for the given namespace and list options 132 func ListPods(namespace string, opts metav1.ListOptions) (*corev1.PodList, error) { 133 client, err := k8sutil.GetKubernetesClientset() 134 if err != nil { 135 return nil, err 136 } 137 return client.CoreV1().Pods(namespace).List(context.TODO(), opts) 138 } 139 140 // ListDeployments returns the list of deployments in a given namespace for the cluster 141 func ListDeployments(namespace string) (*appsv1.DeploymentList, error) { 142 // Get the Kubernetes clientset 143 clientset, err := k8sutil.GetKubernetesClientset() 144 if err != nil { 145 return nil, err 146 } 147 148 deployments, err := clientset.AppsV1().Deployments(namespace).List(context.TODO(), metav1.ListOptions{}) 149 if err != nil { 150 Log(Error, fmt.Sprintf("Failed to list deployments in namespace %s: %v", namespace, err)) 151 return nil, err 152 } 153 return deployments, nil 154 } 155 156 // ListStatefulSets returns the list of StatefulSets in a given namespace for the cluster 157 func ListStatefulSets(namespace string) (*appsv1.StatefulSetList, error) { 158 // Get the Kubernetes clientset 159 clientset, err := k8sutil.GetKubernetesClientset() 160 if err != nil { 161 return nil, err 162 } 163 164 statefulsets, err := clientset.AppsV1().StatefulSets(namespace).List(context.TODO(), metav1.ListOptions{}) 165 if err != nil { 166 Log(Error, fmt.Sprintf("Failed to list StatefulSets in namespace %s: %v", namespace, err)) 167 return nil, err 168 } 169 return statefulsets, nil 170 } 171 172 // GetReplicaCounts Builds a map of pod counts for a list of deployments 173 // expectedDeployments - a list of namespaced names for deployments to look for 174 // optsBuilder - a callback func to build the right set of options to select pods for the deployment 175 func GetReplicaCounts(expectedDeployments []types.NamespacedName, optsBuilder func(name types.NamespacedName) (metav1.ListOptions, error)) (map[string]uint32, error) { 176 podCountsMap := map[string]uint32{} 177 for _, deployment := range expectedDeployments { 178 listOpts, err := optsBuilder(deployment) 179 if err != nil { 180 return map[string]uint32{}, err 181 } 182 podList, err := ListPods(deployment.Namespace, listOpts) 183 if err != nil { 184 return map[string]uint32{}, err 185 } 186 podCountsMap[deployment.String()] = uint32(len(podList.Items)) 187 } 188 return podCountsMap, nil 189 } 190 191 // DoesDeploymentExist returns whether a deployment with the given name and namespace exists for the cluster 192 func DoesDeploymentExist(namespace string, name string) (bool, error) { 193 deployments, err := ListDeployments(namespace) 194 if err != nil { 195 Log(Error, fmt.Sprintf("Failed listing deployments in cluster for namespace %s: %v", namespace, err)) 196 return false, err 197 } 198 for i := range deployments.Items { 199 if strings.HasPrefix(deployments.Items[i].Name, name) { 200 return true, nil 201 } 202 } 203 return false, nil 204 } 205 206 // GetDeployment returns a deployment with the given name and namespace 207 func GetDeployment(namespace string, deploymentName string) (*appsv1.Deployment, error) { 208 // Get the Kubernetes clientset 209 clientSet, err := k8sutil.GetKubernetesClientset() 210 if err != nil { 211 return nil, err 212 } 213 deployment, err := clientSet.AppsV1().Deployments(namespace).Get(context.TODO(), deploymentName, metav1.GetOptions{}) 214 if err != nil { 215 if !k8serrors.IsNotFound(err) { 216 Log(Error, fmt.Sprintf("Failed to get Deployment %s from namespace %s: %v ", deploymentName, namespace, err)) 217 } 218 return nil, err 219 } 220 return deployment, nil 221 } 222 223 // DoesStatefulSetExist returns whether a StatefulSet with the given name and namespace exists for the cluster 224 func DoesStatefulSetExist(namespace string, name string) (bool, error) { 225 statefulsets, err := ListStatefulSets(namespace) 226 if err != nil { 227 Log(Error, fmt.Sprintf("Failed to list StatefulSets from namespace %s: %v", namespace, err)) 228 return false, err 229 } 230 for i := range statefulsets.Items { 231 if strings.HasPrefix(statefulsets.Items[i].Name, name) { 232 return true, nil 233 } 234 } 235 return false, nil 236 } 237 238 // GetStatefulSet returns a StatefulSet with the given name and namespace 239 func GetStatefulSet(namespace string, stsName string) (*appsv1.StatefulSet, error) { 240 // Get the Kubernetes clientset 241 clientSet, err := k8sutil.GetKubernetesClientset() 242 if err != nil { 243 return nil, err 244 } 245 sts, err := clientSet.AppsV1().StatefulSets(namespace).Get(context.TODO(), stsName, metav1.GetOptions{}) 246 if err != nil { 247 if !k8serrors.IsNotFound(err) { 248 Log(Error, fmt.Sprintf("Failed to get StatefulSet %s from namespace %s: %v ", stsName, namespace, err)) 249 } 250 return nil, err 251 } 252 return sts, nil 253 } 254 255 // GetDaemonSet returns a GetDaemonSet with the given name and namespace 256 func GetDaemonSet(namespace string, daemonSetName string) (*appsv1.DaemonSet, error) { 257 // Get the Kubernetes clientset 258 clientSet, err := k8sutil.GetKubernetesClientset() 259 if err != nil { 260 return nil, err 261 } 262 daemonset, err := clientSet.AppsV1().DaemonSets(namespace).Get(context.TODO(), daemonSetName, metav1.GetOptions{}) 263 if err != nil { 264 if !k8serrors.IsNotFound(err) { 265 Log(Error, fmt.Sprintf("Failed to get DaemonSet %s from namespace %s: %v ", daemonSetName, namespace, err)) 266 } 267 return nil, err 268 } 269 return daemonset, nil 270 } 271 272 // GetService returns a Service with the given name and namespace 273 func GetService(namespace string, serviceName string) (*corev1.Service, error) { 274 // Get the Kubernetes clientset 275 clientSet, err := k8sutil.GetKubernetesClientset() 276 if err != nil { 277 return nil, err 278 } 279 svc, err := clientSet.CoreV1().Services(namespace).Get(context.TODO(), serviceName, metav1.GetOptions{}) 280 if err != nil { 281 if !k8serrors.IsNotFound(err) { 282 Log(Error, fmt.Sprintf("Failed to get Service %s from namespace %s: %v ", serviceName, namespace, err)) 283 } 284 return nil, err 285 } 286 return svc, nil 287 } 288 289 // DoesServiceExist returns whether a Service with the given name and namespace exists for the cluster 290 func DoesServiceExist(namespace string, name string) (bool, error) { 291 _, err := GetService(namespace, name) 292 if err != nil { 293 return false, err 294 } 295 return true, nil 296 } 297 298 // GetIngressList returns a list of ingresses in the given namespace 299 func GetIngressList(namespace string) (*netv1.IngressList, error) { 300 // Get the Kubernetes clientset 301 clientSet, err := k8sutil.GetKubernetesClientset() 302 if err != nil { 303 return nil, err 304 } 305 ingressList, err := clientSet.NetworkingV1().Ingresses(namespace).List(context.TODO(), metav1.ListOptions{}) 306 if err != nil { 307 if !k8serrors.IsNotFound(err) { 308 Log(Error, fmt.Sprintf("Failed to get Ingresses in namespace %s: %v ", namespace, err)) 309 } 310 return nil, err 311 } 312 return ingressList, nil 313 } 314 315 // GetIngress returns the ingress given a namespace and ingress name 316 func GetIngress(namespace string, ingressName string) (*netv1.Ingress, error) { 317 // Get the Kubernetes clientset 318 clientSet, err := k8sutil.GetKubernetesClientset() 319 if err != nil { 320 return nil, err 321 } 322 ingress, err := clientSet.NetworkingV1().Ingresses(namespace).Get(context.TODO(), ingressName, metav1.GetOptions{}) 323 if err != nil { 324 if !k8serrors.IsNotFound(err) { 325 Log(Error, fmt.Sprintf("Failed to get Ingress %s in namespace %s: %v ", ingressName, namespace, err)) 326 } 327 return nil, err 328 } 329 return ingress, nil 330 } 331 332 // DoesVirtualServiceExist returns whether a VirtualService with the given name and namespace exists for the cluster 333 func DoesVirtualServiceExist(namespace string, name string) (bool, error) { 334 services, err := GetVirtualServiceList(namespace) 335 if err != nil { 336 Log(Error, fmt.Sprintf("Failed to list VirtualServices from namespace %s: %v", namespace, err)) 337 return false, err 338 } 339 340 // Verify that the virtual services contain the expected environment name 341 for _, virtualService := range services.Items { 342 service := virtualService.Name 343 if strings.Contains(service, name) { 344 return true, nil 345 } 346 } 347 return false, nil 348 } 349 350 // GetVirtualServiceList returns a list of virtual services in the given namespace 351 func GetVirtualServiceList(namespace string) (*istionetv1beta1.VirtualServiceList, error) { 352 // Get the Istio clientset 353 clientSet, err := k8sutil.GetIstioClientset() 354 if err != nil { 355 return nil, err 356 } 357 VirtualServiceList, err := clientSet.NetworkingV1beta1().VirtualServices(namespace).List(context.TODO(), metav1.ListOptions{}) 358 if err != nil { 359 if !k8serrors.IsNotFound(err) { 360 Log(Error, fmt.Sprintf("Failed to get Gateways in namespace %s: %v ", namespace, err)) 361 } 362 return nil, err 363 } 364 return VirtualServiceList, nil 365 } 366 367 // DoesSecretExist returns whether a Secret with the given name and namespace exists for the cluster 368 func DoesSecretExist(namespace string, name string) (bool, error) { 369 secrets, err := ListSecrets(namespace) 370 if err != nil { 371 Log(Error, fmt.Sprintf("Failed to list Secrets from namespace %s: %v", namespace, err)) 372 return false, err 373 } 374 for i := range secrets.Items { 375 if strings.HasPrefix(secrets.Items[i].Name, name) { 376 return true, nil 377 } 378 } 379 return false, nil 380 } 381 382 // GetCertificateList returns a list of certificates in the given namespace 383 func GetCertificateList(namespace string) (*certmanagerv1.CertificateList, error) { 384 // Get the Cert-manager clientset 385 clientSet, err := k8sutil.GetCertManagerClienset() 386 if err != nil { 387 return nil, err 388 } 389 certificateList, err := clientSet.Certificates(namespace).List(context.TODO(), metav1.ListOptions{}) 390 if err != nil { 391 if !k8serrors.IsNotFound(err) { 392 Log(Error, fmt.Sprintf("Failed to get Certificates in namespace %s: %v ", namespace, err)) 393 } 394 return nil, err 395 } 396 return certificateList, nil 397 } 398 399 // GetIssuerList returns a list of cluster issuers 400 func GetIssuerList(namespace string) (*certmanagerv1.IssuerList, error) { 401 // Get the Cert-manager clientset 402 clientSet, err := k8sutil.GetCertManagerClienset() 403 if err != nil { 404 return nil, err 405 } 406 issuerList, err := clientSet.Issuers(namespace).List(context.TODO(), metav1.ListOptions{}) 407 if err != nil { 408 if !k8serrors.IsNotFound(err) { 409 Log(Error, fmt.Sprintf("Failed to get Issuers in namespace %s: %v ", namespace, err)) 410 } 411 return nil, err 412 } 413 return issuerList, nil 414 } 415 416 // ListNodes returns the list of nodes for the cluster 417 func ListNodes() (*corev1.NodeList, error) { 418 // Get the Kubernetes clientset 419 clientset, err := k8sutil.GetKubernetesClientset() 420 if err != nil { 421 return nil, err 422 } 423 424 nodes, err := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) 425 if err != nil { 426 Log(Error, fmt.Sprintf("Failed to list nodes: %v", err)) 427 return nil, err 428 } 429 return nodes, nil 430 } 431 432 // GetNodeCount returns the number of nodes for the cluster 433 func GetNodeCount() (uint32, error) { 434 nodes, err := ListNodes() 435 if err != nil { 436 return 0, err 437 } 438 if len(nodes.Items) < 1 { 439 return 0, fmt.Errorf("can not find node in the cluster") 440 } 441 return uint32(len(nodes.Items)), nil 442 } 443 444 // GetSchedulableNodeCount returns the number of schedulabe nodes in the cluster 445 func GetSchedulableNodeCount() (uint32, error) { 446 nodes, err := ListNodes() 447 if err != nil { 448 return 0, err 449 } 450 nodeCount := 0 451 for _, node := range nodes.Items { 452 if NodeIsSchedulable(node) { 453 nodeCount++ 454 } 455 } 456 if nodeCount < 1 { 457 return 0, fmt.Errorf("can not find node in the cluster") 458 } 459 return uint32(nodeCount), nil 460 } 461 462 // NodeIsSchedulable returns false if a node has the control-plane/master taint and is unschedulable, true otherwise 463 func NodeIsSchedulable(node corev1.Node) bool { 464 for _, taint := range node.Spec.Taints { 465 switch taint.Key { 466 case "node-role.kubernetes.io/control-plane": 467 case "node-role.kubernetes.io/master": 468 if taint.Effect == corev1.TaintEffectNoSchedule { 469 return false 470 } 471 } 472 } 473 return true 474 } 475 476 // GetPodsFromSelector returns a collection of pods for the given namespace and selector 477 func GetPodsFromSelector(selector *metav1.LabelSelector, namespace string) ([]corev1.Pod, error) { 478 var pods *corev1.PodList 479 var err error 480 if selector == nil { 481 pods, err = ListPods(namespace, metav1.ListOptions{}) 482 } else { 483 var labelMap map[string]string 484 labelMap, err = metav1.LabelSelectorAsMap(selector) 485 if err == nil { 486 pods, err = ListPods(namespace, metav1.ListOptions{LabelSelector: labels.SelectorFromSet(labelMap).String()}) 487 } 488 } 489 if err != nil { 490 return nil, err 491 } 492 return pods.Items, nil 493 } 494 495 // ListPodsInCluster returns the list of pods in a given namespace for the cluster 496 func ListPodsInCluster(namespace string, clientset *kubernetes.Clientset) (*corev1.PodList, error) { 497 return clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{}) 498 } 499 500 // ListPodsWithLabelsInCluster returns the list of pods in a given namespace that matches a specific label for the cluster 501 func ListPodsWithLabelsInCluster(namespace, labels string, clientset *kubernetes.Clientset) (*corev1.PodList, error) { 502 return clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: labels}) 503 } 504 505 // DoesPodExist returns whether a pod with the given name and namespace exists for the cluster 506 func DoesPodExist(namespace string, name string) (bool, error) { 507 clientset, err := k8sutil.GetKubernetesClientset() 508 if err != nil { 509 return false, err 510 } 511 pods, err := ListPodsInCluster(namespace, clientset) 512 if err != nil { 513 Log(Error, fmt.Sprintf("Failed listing pods in cluster for namespace: %s: %v", namespace, err)) 514 return false, err 515 } 516 for i := range pods.Items { 517 if strings.HasPrefix(pods.Items[i].Name, name) { 518 return true, nil 519 } 520 } 521 return false, nil 522 } 523 524 // GetKubernetesClientsetForCluster returns the Kubernetes clientset for the cluster whose 525 // kubeconfig path is specified 526 func GetKubernetesClientsetForCluster(kubeconfigPath string) (*kubernetes.Clientset, error) { 527 // use the current context in the kubeconfig 528 config, err := k8sutil.GetKubeConfigGivenPath(kubeconfigPath) 529 if err != nil { 530 return nil, err 531 } 532 return createClientset(config) 533 } 534 535 // createClientset Creates Kubernetes Clientset for a given kubeconfig 536 func createClientset(config *restclient.Config) (*kubernetes.Clientset, error) { 537 return kubernetes.NewForConfig(config) 538 } 539 540 // GetClusterOperatorClientset returns the Kubernetes clientset for the Verrazzano Cluster Operator 541 // for a given kubeconfig 542 func GetClusterOperatorClientset(kubeconfigPath string) (*vcoClient.Clientset, error) { 543 config, err := k8sutil.GetKubeConfigGivenPath(kubeconfigPath) 544 if err != nil { 545 return nil, err 546 } 547 return vcoClient.NewForConfig(config) 548 } 549 550 // GetVerrazzanoClientset returns the Kubernetes clientset for the Verrazzano CRD 551 func GetVerrazzanoClientset() (*vpoClient.Clientset, error) { 552 config, err := k8sutil.GetKubeConfig() 553 if err != nil { 554 return nil, err 555 } 556 return vpoClient.NewForConfig(config) 557 } 558 559 // GetVerrazzanoProjectClientsetInCluster returns the Kubernetes clientset for the VerrazzanoProject 560 func GetVerrazzanoProjectClientsetInCluster(kubeconfigPath string) (*vaoClient.Clientset, error) { 561 config, err := k8sutil.GetKubeConfigGivenPath(kubeconfigPath) 562 if err != nil { 563 return nil, err 564 } 565 return vaoClient.NewForConfig(config) 566 } 567 568 // GetVerrazzanoApplicationOperatorClientSet returns the Kubernetes clientset for the Verrazzano Application Operator 569 func GetVerrazzanoApplicationOperatorClientSet() (*vaoClient.Clientset, error) { 570 config, err := k8sutil.GetKubeConfig() 571 if err != nil { 572 return nil, err 573 } 574 return vaoClient.NewForConfig(config) 575 } 576 577 // GetDynamicClient returns a dynamic client needed to access Unstructured data 578 func GetDynamicClient() (dynamic.Interface, error) { 579 config, err := k8sutil.GetKubeConfig() 580 if err != nil { 581 return nil, err 582 } 583 return dynamic.NewForConfig(config) 584 } 585 586 // GetDynamicClientInCluster returns a dynamic client needed to access Unstructured data 587 func GetDynamicClientInCluster(kubeconfigPath string) (dynamic.Interface, error) { 588 return k8sutil.GetDynamicClientInCluster(kubeconfigPath) 589 } 590 591 // GetV1Beta1ControllerRuntimeClient, given a kubeconfig, 592 // returns a controller runtime client with verrazzano v1beta1 as part of its scheme. 593 func GetV1Beta1ControllerRuntimeClient(config *restclient.Config) (client.Client, error) { 594 scheme := runtime.NewScheme() 595 if err := v1beta1.AddToScheme(scheme); err != nil { 596 return nil, err 597 } 598 options := client.Options{ 599 Scheme: scheme, 600 // The default SuppressWarnings=false will override whatever WarningHandler was used by the config. 601 // Set this to true to use whatever WarningHandler was passed into this function. 602 Opts: client.WarningHandlerOptions{SuppressWarnings: true}, 603 } 604 vzClient, err := client.New(config, options) 605 if err != nil { 606 return nil, err 607 } 608 return vzClient, nil 609 } 610 611 // GetVerrazzanoInstallResourceInCluster returns the installed Verrazzano CR in the given cluster 612 // (there should only be 1 per cluster) 613 func GetVerrazzanoInstallResourceInCluster(kubeconfigPath string) (*v1alpha1.Verrazzano, error) { 614 config, err := k8sutil.GetKubeConfigGivenPath(kubeconfigPath) 615 if err != nil { 616 return nil, err 617 } 618 vzClient, err := GetV1Beta1ControllerRuntimeClient(config) 619 if err != nil { 620 return nil, err 621 } 622 vzList, err := verrazzano.ListV1Alpha1(context.TODO(), vzClient) 623 624 if err != nil { 625 return nil, fmt.Errorf("error listing out Verrazzano instances: %v", err) 626 } 627 numVzs := len(vzList.Items) 628 if numVzs == 0 { 629 return nil, fmt.Errorf("did not find installed Verrazzano instance") 630 } 631 vz := vzList.Items[0] 632 return &vz, nil 633 } 634 635 // GetVerrazzanoInstallResourceInClusterV1beta1 returns the installed Verrazzano CR in the given cluster 636 // (there should only be 1 per cluster) 637 func GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath string) (*v1beta1.Verrazzano, error) { 638 config, err := k8sutil.GetKubeConfigGivenPath(kubeconfigPath) 639 if err != nil { 640 return nil, err 641 } 642 client, err := vpoClient.NewForConfig(config) 643 if err != nil { 644 return nil, err 645 } 646 vzClient := client.VerrazzanoV1beta1().Verrazzanos("") 647 vzList, err := vzClient.List(context.TODO(), metav1.ListOptions{}) 648 649 if err != nil { 650 return nil, fmt.Errorf("error listing out v1beta1 Verrazzano instances: %v", err) 651 } 652 numVzs := len(vzList.Items) 653 if numVzs == 0 { 654 return nil, fmt.Errorf("did not find installed Verrazzano instance") 655 } 656 vz := vzList.Items[0] 657 return &vz, nil 658 } 659 660 // IsDevProfile returns true if the deployed resource is a 'dev' profile 661 func IsDevProfile() bool { 662 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 663 if err != nil { 664 Log(Error, fmt.Sprintf("Error getting kubeconfig: %v", err)) 665 return false 666 } 667 668 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 669 if err != nil { 670 return false 671 } 672 if vz.Spec.Profile == v1beta1.Dev { 673 return true 674 } 675 return false 676 } 677 678 // GetVerrazzano returns the installed Verrazzano 679 func GetVerrazzano() (*v1alpha1.Verrazzano, error) { 680 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 681 if err != nil { 682 Log(Error, fmt.Sprintf("Error getting kubeconfig: %v", err)) 683 return nil, err 684 } 685 cr, err := GetVerrazzanoInstallResourceInCluster(kubeconfigPath) 686 if err != nil { 687 return nil, err 688 } 689 return cr, nil 690 } 691 692 // GetVerrazzanoV1beta1 returns the installed Verrazzano using v1beta1 API client 693 func GetVerrazzanoV1beta1() (*v1beta1.Verrazzano, error) { 694 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 695 if err != nil { 696 Log(Error, fmt.Sprintf("Error getting kubeconfig: %v", err)) 697 return nil, err 698 } 699 cr, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 700 if err != nil { 701 return nil, err 702 } 703 return cr, nil 704 } 705 706 // GetVerrazzanoVersion returns the Verrazzano Version 707 func GetVerrazzanoVersion(kubeconfigPath string) (string, error) { 708 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 709 if err != nil { 710 return "", err 711 } 712 vzVer := vz.Spec.Version 713 if vzVer == "" { 714 vzVer = vz.Status.Version 715 } 716 return vzVer, nil 717 } 718 719 // IsVerrazzanoMinVersion returns true if the installed Verrazzano version >= minVersion 720 func IsVerrazzanoMinVersion(minVersion string, kubeconfigPath string) (bool, error) { 721 vzVersion, err := GetVerrazzanoVersion(kubeconfigPath) 722 if err != nil { 723 return false, err 724 } 725 if len(vzVersion) == 0 { 726 return false, nil 727 } 728 return IsMinVersion(vzVersion, minVersion) 729 } 730 731 // IsMinVersion returns true if the given version >= minVersion 732 func IsMinVersion(vzVersion, minVersion string) (bool, error) { 733 vzSemver, err := semver.NewSemVersion(vzVersion) 734 if err != nil { 735 return false, err 736 } 737 minSemver, err := semver.NewSemVersion(minVersion) 738 if err != nil { 739 return false, err 740 } 741 return !vzSemver.IsLessThan(minSemver), nil 742 } 743 744 // IsProdProfile returns true if the deployed resource is a 'prod' profile 745 func IsProdProfile() bool { 746 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 747 if err != nil { 748 Log(Error, fmt.Sprintf("Error getting kubeconfig: %v", err)) 749 return false 750 } 751 752 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 753 if err != nil { 754 return false 755 } 756 if vz.Spec.Profile == v1beta1.Prod || vz.Spec.Profile == "" { 757 return true 758 } 759 return false 760 } 761 762 // IsManagedClusterProfile returns true if the deployed resource is a 'managed-cluster' profile 763 func IsManagedClusterProfile() bool { 764 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 765 if err != nil { 766 Log(Error, fmt.Sprintf("Error getting kubeconfig: %v", err)) 767 return false 768 } 769 770 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 771 if err != nil { 772 Log(Error, fmt.Sprintf("Error getting vz install resource: %v", err)) 773 return false 774 } 775 if vz.Spec.Profile == v1beta1.ManagedCluster { 776 return true 777 } 778 return false 779 } 780 781 // GetACMEEnvironment returns true if 782 func GetACMEEnvironment(kubeconfigPath string) (string, error) { 783 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 784 if err != nil { 785 return "", err 786 } 787 788 // first check if letsEncrypt configured in clusterIssuer component 789 clusterIssuer := vz.Spec.Components.ClusterIssuer 790 if clusterIssuer != nil && clusterIssuer.LetsEncrypt != nil { 791 Log(Info, fmt.Sprintf("Let's Encrypt env is %s", clusterIssuer.LetsEncrypt.Environment)) 792 return clusterIssuer.LetsEncrypt.Environment, nil 793 } 794 795 // then check if letsEncrypt configured in cert-manager certificate field 796 certManager := vz.Spec.Components.CertManager 797 if certManager != nil && strings.ToLower(string(certManager.Certificate.Acme.Provider)) == strings.ToLower(string(v1beta1.LetsEncrypt)) { 798 return certManager.Certificate.Acme.Environment, nil 799 } 800 return "", nil 801 } 802 803 // IsCoherenceOperatorEnabled returns true if the COH operator component is not set, or the value of its Enabled field otherwise 804 func IsCoherenceOperatorEnabled(kubeconfigPath string) bool { 805 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 806 if err != nil { 807 Log(Error, fmt.Sprintf("Error getting kubeconfig: %v", err)) 808 return true 809 } 810 if vz.Spec.Components.CoherenceOperator == nil || vz.Spec.Components.CoherenceOperator.Enabled == nil { 811 return true 812 } 813 return *vz.Spec.Components.CoherenceOperator.Enabled 814 } 815 816 // IsCertManagerEnabled returns true if the Cert Manager component is not set, or the value of its Enabled field otherwise 817 func IsCertManagerEnabled(kubeconfigPath string) bool { 818 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 819 if err != nil { 820 Log(Error, fmt.Sprintf("Error getting kubeconfig: %v", err)) 821 return true 822 } 823 if vz.Spec.Components.CertManager == nil || vz.Spec.Components.CertManager.Enabled == nil { 824 return true 825 } 826 return *vz.Spec.Components.CertManager.Enabled 827 } 828 829 // IsOCIDNSEnabled returns true if OCI DNS is enabled in the configuration 830 func IsOCIDNSEnabled(kubeconfigPath string) bool { 831 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 832 if err != nil { 833 Log(Error, fmt.Sprintf("Error getting kubeconfig: %v", err)) 834 return true 835 } 836 if vz.Spec.Components.DNS == nil || vz.Spec.Components.DNS.OCI == nil { 837 return false 838 } 839 return true 840 } 841 842 func IsCAIssuerConfig(certConfig v1beta1.Certificate) (isCAConfig bool, err error) { 843 // Check if Ca or Acme is empty 844 caNotEmpty := certConfig.CA != v1beta1.CA{} 845 acmeNotEmpty := certConfig.Acme != v1beta1.Acme{} 846 if caNotEmpty && acmeNotEmpty { 847 return false, errors.New("certificate object Acme and CA cannot be simultaneously populated") 848 } else if !caNotEmpty && !acmeNotEmpty { 849 return false, errors.New("Either Acme or CA certificate authorities must be configured") 850 } 851 return caNotEmpty, nil 852 } 853 854 // IsOCIDNSWebhookEnabled returns true if the Cert Manager component is not set, or the value of its Enabled field otherwise 855 func IsOCIDNSWebhookEnabled(kubeconfigPath string) bool { 856 if !IsCertManagerEnabled(kubeconfigPath) || !IsOCIDNSEnabled(kubeconfigPath) { 857 return false 858 } 859 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 860 if err != nil { 861 Log(Error, fmt.Sprintf("Error getting kubeconfig: %v", err)) 862 return true 863 } 864 certManager := vz.Spec.Components.CertManager 865 if certManager != nil { 866 isCAConfig, _ := IsCAIssuerConfig(certManager.Certificate) 867 return !isCAConfig 868 } 869 // CM is not defined, so the OCI DNS webhook won't be deployed 870 return false 871 } 872 873 // IsWebLogicOperatorEnabled returns true if the WKO operator component is not set, or the value of its Enabled field otherwise 874 func IsWebLogicOperatorEnabled(kubeconfigPath string) bool { 875 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 876 if err != nil { 877 Log(Error, fmt.Sprintf(verrazzanoErrorTemplate, err)) 878 return true 879 } 880 if vz.Spec.Components.WebLogicOperator == nil || vz.Spec.Components.WebLogicOperator.Enabled == nil { 881 return true 882 } 883 return *vz.Spec.Components.WebLogicOperator.Enabled 884 } 885 886 // IsOpenSearchEnabled returns true if the OpenSearch component is not set, or the value of its Enabled field otherwise 887 func IsOpenSearchEnabled(kubeconfigPath string) (bool, error) { 888 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 889 if err != nil { 890 Log(Error, fmt.Sprintf("Error Verrazzano Resource: %v", err)) 891 return false, err 892 } 893 if vz != nil && vz.Spec.Components.OpenSearch != nil && vz.Spec.Components.OpenSearch.Enabled != nil { 894 return *vz.Spec.Components.OpenSearch.Enabled, nil 895 } 896 return false, nil 897 } 898 899 // IsPrometheusAdapterEnabled returns false if the Prometheus Adapter component is not set, or the value of its Enabled field otherwise 900 func IsPrometheusAdapterEnabled(kubeconfigPath string) bool { 901 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 902 if err != nil { 903 Log(Error, fmt.Sprintf(verrazzanoErrorTemplate, err)) 904 return false 905 } 906 if vz.Spec.Components.PrometheusAdapter == nil || vz.Spec.Components.PrometheusAdapter.Enabled == nil { 907 return false 908 } 909 return *vz.Spec.Components.PrometheusAdapter.Enabled 910 } 911 912 // IsPrometheusOperatorEnabled returns false if the Prometheus Operator component is not set, or the value of its Enabled field otherwise 913 func IsPrometheusOperatorEnabled(kubeconfigPath string) bool { 914 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 915 if err != nil { 916 Log(Error, fmt.Sprintf(verrazzanoErrorTemplate, err)) 917 return true 918 } 919 if vz.Spec.Components.PrometheusOperator == nil || vz.Spec.Components.PrometheusOperator.Enabled == nil { 920 return vz.Spec.Profile != v1beta1.None 921 } 922 return *vz.Spec.Components.PrometheusOperator.Enabled 923 } 924 925 // IsPrometheusEnabled returns true if the Prometheus component is not set and the Prometheus Operator is enabled, or the value of its Enabled field otherwise 926 func IsPrometheusEnabled(kubeconfigPath string) bool { 927 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 928 if err != nil { 929 Log(Error, fmt.Sprintf(verrazzanoErrorTemplate, err)) 930 return false 931 } 932 if vz.Spec.Components.Prometheus == nil || vz.Spec.Components.Prometheus.Enabled == nil { 933 return vz.Spec.Profile != v1beta1.None 934 } 935 return *vz.Spec.Components.Prometheus.Enabled 936 } 937 938 // IsIngressEnabled returns false if the IngressNGINX component is not set and the IngressNGINX is enabled, or the value of its Enabled field otherwise 939 func IsIngressEnabled(kubeconfigPath string) bool { 940 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 941 if err != nil { 942 Log(Error, fmt.Sprintf(verrazzanoErrorTemplate, err)) 943 return false 944 } 945 if vz.Spec.Components.IngressNGINX == nil || vz.Spec.Components.IngressNGINX.Enabled == nil { 946 return false 947 } 948 return *vz.Spec.Components.IngressNGINX.Enabled 949 } 950 951 // IsKubeStateMetricsEnabled returns false if the Kube State Metrics component is not set, or the value of its Enabled field otherwise 952 func IsKubeStateMetricsEnabled(kubeconfigPath string) bool { 953 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 954 if err != nil { 955 Log(Error, fmt.Sprintf(verrazzanoErrorTemplate, err)) 956 return false 957 } 958 if vz.Spec.Components.KubeStateMetrics == nil || vz.Spec.Components.KubeStateMetrics.Enabled == nil { 959 return vz.Spec.Profile != v1beta1.None 960 } 961 return *vz.Spec.Components.KubeStateMetrics.Enabled 962 } 963 964 // IsPrometheusPushgatewayEnabled returns false if the Prometheus Pushgateway component is not set, or the value of its Enabled field otherwise 965 func IsPrometheusPushgatewayEnabled(kubeconfigPath string) bool { 966 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 967 if err != nil { 968 Log(Error, fmt.Sprintf(verrazzanoErrorTemplate, err)) 969 return false 970 } 971 if vz.Spec.Components.PrometheusPushgateway == nil || vz.Spec.Components.PrometheusPushgateway.Enabled == nil { 972 return false 973 } 974 return *vz.Spec.Components.PrometheusPushgateway.Enabled 975 } 976 977 // IsPrometheusNodeExporterEnabled returns false if the Prometheus Node Exporter component is not set, or the value of its Enabled field otherwise 978 func IsPrometheusNodeExporterEnabled(kubeconfigPath string) bool { 979 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 980 if err != nil { 981 Log(Error, fmt.Sprintf("Error Verrazzano Resource: %v", err)) 982 return false 983 } 984 if vz.Spec.Components.PrometheusNodeExporter == nil || vz.Spec.Components.PrometheusNodeExporter.Enabled == nil { 985 return IsPrometheusEnabled(kubeconfigPath) 986 } 987 return *vz.Spec.Components.PrometheusNodeExporter.Enabled 988 } 989 990 // IsOpenSearchDashboardsEnabled returns true if the OpenSearchDashboards component is not set, or the value of its Enabled field otherwise 991 func IsOpenSearchDashboardsEnabled(kubeconfigPath string) bool { 992 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 993 if err != nil { 994 Log(Error, fmt.Sprintf(verrazzanoErrorTemplate, err)) 995 return true 996 } 997 if vz != nil && vz.Spec.Components.OpenSearchDashboards != nil && vz.Spec.Components.OpenSearchDashboards.Enabled != nil { 998 return *vz.Spec.Components.OpenSearchDashboards.Enabled 999 } 1000 return true 1001 } 1002 1003 // IsJaegerOperatorEnabled returns false if the Jaeger Operator component is not set, or the value of its Enabled field otherwise 1004 func IsJaegerOperatorEnabled(kubeconfigPath string) bool { 1005 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 1006 if err != nil { 1007 Log(Error, fmt.Sprintf("Error Verrazzano Resource: %v", err)) 1008 return false 1009 } 1010 if vz == nil || vz.Spec.Components.JaegerOperator == nil || vz.Spec.Components.JaegerOperator.Enabled == nil { 1011 return false 1012 } 1013 return *vz.Spec.Components.JaegerOperator.Enabled 1014 } 1015 1016 // IsGrafanaEnabled returns false if the Grafana component is not set, or the value of its Enabled field otherwise 1017 func IsGrafanaEnabled(kubeconfigPath string) bool { 1018 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 1019 if err != nil { 1020 Log(Error, fmt.Sprintf("Error Verrazzano Resource: %v", err)) 1021 return false 1022 } 1023 if vz.Spec.Components.Grafana == nil || vz.Spec.Components.Grafana.Enabled == nil { 1024 // Grafana component is enabled by default 1025 return true 1026 } 1027 return *vz.Spec.Components.Grafana.Enabled 1028 } 1029 1030 // IsKeycloakEnabled returns false if the Keycloak component is not set, or the value of its Enabled field otherwise 1031 func IsKeycloakEnabled(kubeconfigPath string) bool { 1032 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 1033 if err != nil { 1034 Log(Error, fmt.Sprintf("Error Verrazzano Resource: %v", err)) 1035 return false 1036 } 1037 if vz.Spec.Components.Keycloak == nil || vz.Spec.Components.Keycloak.Enabled == nil { 1038 // Keycloak component is enabled by default 1039 return true 1040 } 1041 return *vz.Spec.Components.Keycloak.Enabled 1042 } 1043 1044 // IsMySQLOperatorEnabled returns false if the MySQLOperator component is not set, or the value of its Enabled field otherwise 1045 func IsMySQLOperatorEnabled(kubeconfigPath string) bool { 1046 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 1047 if err != nil { 1048 Log(Error, fmt.Sprintf("Error Verrazzano Resource: %v", err)) 1049 return false 1050 } 1051 if vz.Spec.Components.MySQLOperator == nil || vz.Spec.Components.MySQLOperator.Enabled == nil { 1052 // MySQLOperator component is enabled by default 1053 return true 1054 } 1055 return *vz.Spec.Components.MySQLOperator.Enabled 1056 } 1057 1058 // IsVeleroEnabled returns false if the Velero component is not set, or the value of its Enabled field otherwise 1059 func IsVeleroEnabled(kubeconfigPath string) bool { 1060 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 1061 if err != nil { 1062 Log(Error, fmt.Sprintf("Error Verrazzano Resource: %v", err)) 1063 return false 1064 } 1065 if vz.Spec.Components.Velero == nil || vz.Spec.Components.Velero.Enabled == nil { 1066 return false 1067 } 1068 return *vz.Spec.Components.Velero.Enabled 1069 } 1070 1071 // IsRancherEnabled returns false if the Rancher component is not set, or the value of its Enabled field otherwise 1072 func IsRancherEnabled(kubeconfigPath string) bool { 1073 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 1074 if err != nil { 1075 Log(Error, fmt.Sprintf("Error Verrazzano Resource: %v", err)) 1076 return false 1077 } 1078 if vz.Spec.Components.Rancher == nil || vz.Spec.Components.Rancher.Enabled == nil { 1079 // Rancher component is enabled by default 1080 return true 1081 } 1082 return *vz.Spec.Components.Rancher.Enabled 1083 } 1084 1085 // IsRancherBackupEnabled returns false if the Rancher Backup component is not set, or the value of its Enabled field otherwise 1086 func IsRancherBackupEnabled(kubeconfigPath string) bool { 1087 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 1088 if err != nil { 1089 Log(Error, fmt.Sprintf("Error Verrazzano Resource: %v", err)) 1090 return false 1091 } 1092 if vz.Spec.Components.RancherBackup == nil || vz.Spec.Components.RancherBackup.Enabled == nil { 1093 return false 1094 } 1095 return *vz.Spec.Components.RancherBackup.Enabled 1096 } 1097 1098 // IsClusterAPIEnabled returns true if the ClusterAPI component is not set, or the value of its Enabled field otherwise 1099 func IsClusterAPIEnabled(kubeconfigPath string) bool { 1100 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 1101 if err != nil { 1102 Log(Error, fmt.Sprintf("Error Verrazzano Resource: %v", err)) 1103 return false 1104 } 1105 return vz.Spec.Components.ClusterAPI == nil || vz.Spec.Components.ClusterAPI.Enabled == nil || *vz.Spec.Components.ClusterAPI.Enabled 1106 } 1107 1108 // IsArgoCDEnabled returns false if the Argocd component is not set, or the value of its Enabled field otherwise 1109 func IsArgoCDEnabled(kubeconfigPath string) bool { 1110 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 1111 if err != nil { 1112 Log(Error, fmt.Sprintf("Error Verrazzano Resource: %v", err)) 1113 return false 1114 } 1115 if vz.Spec.Components.ArgoCD == nil || vz.Spec.Components.ArgoCD.Enabled == nil { 1116 return false 1117 } 1118 return *vz.Spec.Components.ArgoCD.Enabled 1119 } 1120 1121 // IsClusterAgentEnabled returns true if the Cluster Agent component is not set, or the value of its Enabled field otherwise 1122 func IsClusterAgentEnabled(kubeconfigPath string) bool { 1123 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 1124 if err != nil { 1125 Log(Error, fmt.Sprintf("Error getting kubeconfig: %v", err)) 1126 return true 1127 } 1128 if vz.Spec.Components.ClusterAgent == nil || vz.Spec.Components.ClusterAgent.Enabled == nil { 1129 return true 1130 } 1131 return *vz.Spec.Components.ClusterAgent.Enabled 1132 } 1133 1134 // IsIstioEnabled returns true if the Istio component is not set, or the value of its Enabled field otherwise 1135 func IsIstioEnabled(kubeconfigPath string) bool { 1136 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 1137 if err != nil { 1138 Log(Error, fmt.Sprintf("Error getting kubeconfig: %v", err)) 1139 return true 1140 } 1141 return vz.Spec.Components.Istio == nil || vz.Spec.Components.Istio.Enabled == nil || *vz.Spec.Components.Istio.Enabled 1142 } 1143 1144 // APIExtensionsClientSet returns a Kubernetes ClientSet for this cluster. 1145 func APIExtensionsClientSet() (*apiextv1.ApiextensionsV1Client, error) { 1146 config, err := k8sutil.GetKubeConfig() 1147 if err != nil { 1148 return nil, err 1149 } 1150 // create the clientset 1151 return apiextv1.NewForConfig(config) 1152 } 1153 1154 // ListServices returns the list of services in a given namespace for the cluster 1155 func ListServices(namespace string) (*corev1.ServiceList, error) { 1156 // Get the Kubernetes clientset 1157 clientset, err := k8sutil.GetKubernetesClientset() 1158 if err != nil { 1159 return nil, err 1160 } 1161 1162 services, err := clientset.CoreV1().Services(namespace).List(context.TODO(), metav1.ListOptions{}) 1163 if err != nil { 1164 Log(Error, fmt.Sprintf("Failed to list services in namespace %s: %v", namespace, err)) 1165 return nil, err 1166 } 1167 return services, nil 1168 } 1169 1170 // GetNamespace returns a namespace 1171 func GetNamespace(name string) (*corev1.Namespace, error) { 1172 // Get the Kubernetes clientset 1173 clientset, err := k8sutil.GetKubernetesClientset() 1174 if err != nil { 1175 return nil, err 1176 } 1177 return GetNamespaceWithClientSet(name, clientset) 1178 } 1179 1180 // GetNamespaceWithClientSet returns a namespace for the given Clientset 1181 func GetNamespaceWithClientSet(name string, clientset *kubernetes.Clientset) (*corev1.Namespace, error) { 1182 return clientset.CoreV1().Namespaces().Get(context.TODO(), name, metav1.GetOptions{}) 1183 } 1184 1185 // GenerateNamespace takes a string and combines that with a UUID to generate a namespace 1186 func GenerateNamespace(name string) string { 1187 return name + "-" + uuid.NewString()[:7] 1188 } 1189 1190 // GetEffectiveKeyCloakPersistenceOverride returns the effective PVC override for Keycloak, if it exists 1191 func GetEffectiveKeyCloakPersistenceOverride(kubeconfigPath string) (*v1beta1.VolumeClaimSpecTemplate, error) { 1192 verrazzano, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 1193 if err != nil { 1194 return nil, err 1195 } 1196 1197 mysqlVolSource := verrazzano.Spec.DefaultVolumeSource 1198 if verrazzano.Spec.Components.Keycloak != nil { 1199 mysqlVolSource = verrazzano.Spec.Components.Keycloak.MySQL.VolumeSource 1200 } 1201 if mysqlVolSource == nil || mysqlVolSource.EmptyDir != nil { 1202 // no override specified, or its an EmptyDir override 1203 return nil, nil 1204 } 1205 for _, template := range verrazzano.Spec.VolumeClaimSpecTemplates { 1206 if template.Name == mysqlVolSource.PersistentVolumeClaim.ClaimName { 1207 return &template, nil 1208 } 1209 } 1210 return nil, fmt.Errorf("did not find matching PVC template for %s", mysqlVolSource.PersistentVolumeClaim.ClaimName) 1211 } 1212 1213 // GetEffectiveVMIPersistenceOverride returns the effective PVC override for the VMI components, if it exists 1214 func GetEffectiveVMIPersistenceOverride(kubeconfigPath string) (*v1beta1.VolumeClaimSpecTemplate, error) { 1215 verrazzano, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 1216 if err != nil { 1217 return nil, err 1218 } 1219 1220 volumeOverride := verrazzano.Spec.DefaultVolumeSource 1221 if volumeOverride == nil || volumeOverride.EmptyDir != nil { 1222 // no override specified, or its an EmptyDir override 1223 return nil, nil 1224 } 1225 for _, template := range verrazzano.Spec.VolumeClaimSpecTemplates { 1226 if template.Name == volumeOverride.PersistentVolumeClaim.ClaimName { 1227 return &template, nil 1228 } 1229 } 1230 return nil, fmt.Errorf("did not find matching PVC template for %s", volumeOverride.PersistentVolumeClaim.ClaimName) 1231 } 1232 1233 // GetNamespaceInCluster returns a namespace in the cluster whose kubeconfigPath is specified 1234 func GetNamespaceInCluster(name string, kubeconfigPath string) (*corev1.Namespace, error) { 1235 // Get the Kubernetes clientset 1236 clientset, err := GetKubernetesClientsetForCluster(kubeconfigPath) 1237 if err != nil { 1238 return nil, err 1239 } 1240 1241 return clientset.CoreV1().Namespaces().Get(context.TODO(), name, metav1.GetOptions{}) 1242 } 1243 1244 // CreateNamespace creates a namespace 1245 func CreateNamespace(name string, labels map[string]string) (*corev1.Namespace, error) { 1246 return CreateNamespaceWithAnnotations(name, labels, nil) 1247 } 1248 1249 // CreateOrUpdateNamespace creates or updates a namespace 1250 func CreateOrUpdateNamespace(name string, labels map[string]string, annotations map[string]string) (*corev1.Namespace, error) { 1251 // Get the Kubernetes clientset 1252 clientset, err := k8sutil.GetKubernetesClientset() 1253 if err != nil { 1254 return nil, err 1255 } 1256 var ns *corev1.Namespace 1257 if ns, err = clientset.CoreV1().Namespaces().Get(context.TODO(), name, metav1.GetOptions{}); err != nil { 1258 if !k8serrors.IsNotFound(err) { 1259 return nil, err 1260 } 1261 return clientset.CoreV1().Namespaces().Create(context.TODO(), 1262 &corev1.Namespace{ 1263 ObjectMeta: metav1.ObjectMeta{ 1264 Name: name, 1265 Labels: labels, 1266 Annotations: annotations, 1267 }, 1268 }, metav1.CreateOptions{}) 1269 } 1270 ns.Labels = mergeMaps(ns.Labels, labels) 1271 ns.Annotations = mergeMaps(ns.Annotations, annotations) 1272 return clientset.CoreV1().Namespaces().Update(context.TODO(), ns, metav1.UpdateOptions{}) 1273 } 1274 1275 func mergeMaps(m1 map[string]string, m2 map[string]string) map[string]string { 1276 result := m1 1277 if result == nil { 1278 result = map[string]string{} 1279 } 1280 // merge keys from m2 into m1, overwriting existing keys of m1. 1281 for k, v := range m2 { 1282 result[k] = v 1283 } 1284 return result 1285 } 1286 1287 func CreateNamespaceWithAnnotations(name string, labels map[string]string, annotations map[string]string) (*corev1.Namespace, error) { 1288 // Get the Kubernetes clientset 1289 clientset, err := k8sutil.GetKubernetesClientset() 1290 if err != nil { 1291 return nil, err 1292 } 1293 return CreateNamespaceWithClientSet(name, labels, clientset, annotations) 1294 } 1295 1296 // CreateNamespaceWithClientSet creates a namespace using the given Clientset 1297 func CreateNamespaceWithClientSet(name string, labels map[string]string, clientset *kubernetes.Clientset, annotations map[string]string) (*corev1.Namespace, error) { 1298 if len(os.Getenv(k8sutil.EnvVarTestKubeConfig)) > 0 { 1299 existingNamespace, err := GetNamespaceWithClientSet(name, clientset) 1300 if err != nil { 1301 Log(Error, fmt.Sprintf("CreateNamespace %s, error while getting existing namespace: %v", name, err)) 1302 return nil, err 1303 } 1304 1305 if existingNamespace != nil && existingNamespace.Name == name { 1306 return existingNamespace, nil 1307 } 1308 return nil, fmt.Errorf("CreateNamespace %s, test is running with custom service account and namespace must be pre-created", name) 1309 } 1310 namespace := &corev1.Namespace{ 1311 ObjectMeta: metav1.ObjectMeta{ 1312 Name: name, 1313 Labels: labels, 1314 Annotations: annotations, 1315 }, 1316 } 1317 ns, err := clientset.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{}) 1318 if err != nil { 1319 Log(Error, fmt.Sprintf("CreateNamespace %s error: %v", name, err)) 1320 return nil, err 1321 } 1322 return ns, nil 1323 } 1324 1325 func RemoveNamespaceFinalizers(namespace *corev1.Namespace) error { 1326 clientset, err := k8sutil.GetKubernetesClientset() 1327 if err != nil { 1328 return err 1329 } 1330 namespace.ObjectMeta.Finalizers = nil 1331 _, err = clientset.CoreV1().Namespaces().Update(context.TODO(), namespace, metav1.UpdateOptions{}) 1332 return err 1333 } 1334 1335 // DeleteNamespace deletes a namespace in the cluster specified in the environment 1336 func DeleteNamespace(name string) error { 1337 if len(os.Getenv(k8sutil.EnvVarTestKubeConfig)) > 0 { 1338 Log(Info, fmt.Sprintf("DeleteNamespace %s, test is running with custom service account and therefore namespace won't be deleted by the test", name)) 1339 return nil 1340 } 1341 1342 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 1343 if err != nil { 1344 Log(Error, fmt.Sprintf("Error getting kubeconfig: %v", err)) 1345 return err 1346 } 1347 1348 return DeleteNamespaceInCluster(name, kubeconfigPath) 1349 } 1350 1351 func DeleteNamespaceInCluster(name string, kubeconfigPath string) error { 1352 // Get the Kubernetes clientset 1353 clientset, err := GetKubernetesClientsetForCluster(kubeconfigPath) 1354 if err != nil { 1355 return err 1356 } 1357 return DeleteNamespaceWithClientSet(name, clientset) 1358 } 1359 1360 // DeleteNamespaceWithClientSet deletes the namespace using the given Clientset 1361 func DeleteNamespaceWithClientSet(name string, clientset *kubernetes.Clientset) error { 1362 err := clientset.CoreV1().Namespaces().Delete(context.TODO(), name, metav1.DeleteOptions{}) 1363 if err != nil { 1364 Log(Error, fmt.Sprintf("Failed to delete namespace %s: %v", name, err)) 1365 } 1366 return err 1367 } 1368 1369 // DoesClusterRoleExist returns whether a cluster role with the given name exists in the cluster 1370 func DoesClusterRoleExist(name string) (bool, error) { 1371 // Get the Kubernetes clientset 1372 clientset, err := k8sutil.GetKubernetesClientset() 1373 if err != nil { 1374 return false, err 1375 } 1376 1377 clusterrole, err := clientset.RbacV1().ClusterRoles().Get(context.TODO(), name, metav1.GetOptions{}) 1378 if err != nil && !k8serrors.IsNotFound(err) { 1379 Log(Error, fmt.Sprintf("Failed to get cluster role %s: %v", name, err)) 1380 return false, err 1381 } 1382 1383 return clusterrole != nil, nil 1384 } 1385 1386 // GetClusterRole returns the cluster role with the given name 1387 func GetClusterRole(name string) (*rbacv1.ClusterRole, error) { 1388 // Get the Kubernetes clientset 1389 clientset, err := k8sutil.GetKubernetesClientset() 1390 if err != nil { 1391 return nil, err 1392 } 1393 1394 clusterrole, err := clientset.RbacV1().ClusterRoles().Get(context.TODO(), name, metav1.GetOptions{}) 1395 if err != nil { 1396 if !k8serrors.IsNotFound(err) { 1397 Log(Error, fmt.Sprintf("Failed to get cluster role %s: %v", name, err)) 1398 } 1399 return nil, err 1400 } 1401 1402 return clusterrole, nil 1403 } 1404 1405 // DoesServiceAccountExist returns whether a service account with the given name and namespace exists in the cluster 1406 func DoesServiceAccountExist(namespace, name string) (bool, error) { 1407 sa, err := GetServiceAccount(namespace, name) 1408 if err != nil { 1409 return false, err 1410 } 1411 return sa != nil, nil 1412 } 1413 1414 // DoesClusterRoleBindingExist returns whether a cluster role with the given name exists in the cluster 1415 func DoesClusterRoleBindingExist(name string) (bool, error) { 1416 // Get the Kubernetes clientset 1417 clientset, err := k8sutil.GetKubernetesClientset() 1418 if err != nil { 1419 return false, err 1420 } 1421 1422 clusterrolebinding, err := clientset.RbacV1().ClusterRoleBindings().Get(context.TODO(), name, metav1.GetOptions{}) 1423 if err != nil && !k8serrors.IsNotFound(err) { 1424 Log(Error, fmt.Sprintf("Failed to get cluster role binding %s: %v", name, err)) 1425 return false, err 1426 } 1427 1428 return clusterrolebinding != nil && len(clusterrolebinding.Name) > 0, nil 1429 } 1430 1431 // GetClusterRoleBinding returns the cluster role with the given name 1432 func GetClusterRoleBinding(name string) (*rbacv1.ClusterRoleBinding, error) { 1433 // Get the Kubernetes clientset 1434 clientset, err := k8sutil.GetKubernetesClientset() 1435 if err != nil { 1436 return nil, err 1437 } 1438 1439 crb, err := clientset.RbacV1().ClusterRoleBindings().Get(context.TODO(), name, metav1.GetOptions{}) 1440 if err != nil { 1441 if !k8serrors.IsNotFound(err) { 1442 Log(Error, fmt.Sprintf("Failed to get cluster role binding %s: %v", name, err)) 1443 } 1444 return nil, err 1445 } 1446 1447 return crb, err 1448 } 1449 1450 // ListClusterRoleBindings returns the list of cluster role bindings for the cluster 1451 func ListClusterRoleBindings() (*rbacv1.ClusterRoleBindingList, error) { 1452 // Get the Kubernetes clientset 1453 clientset, err := k8sutil.GetKubernetesClientset() 1454 if err != nil { 1455 return nil, err 1456 } 1457 1458 bindings, err := clientset.RbacV1().ClusterRoleBindings().List(context.TODO(), metav1.ListOptions{}) 1459 if err != nil { 1460 Log(Error, fmt.Sprintf("Failed to get cluster role bindings: %v", err)) 1461 return nil, err 1462 } 1463 1464 return bindings, err 1465 } 1466 1467 // DoesRoleBindingContainSubject returns true if the RoleBinding exists and it contains the 1468 // specified subject 1469 func DoesRoleBindingContainSubject(namespace, name, subjectKind, subjectName string) (bool, error) { 1470 clientset, err := k8sutil.GetKubernetesClientset() 1471 if err != nil { 1472 return false, err 1473 } 1474 1475 rb, err := clientset.RbacV1().RoleBindings(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 1476 if err != nil { 1477 if !k8serrors.IsNotFound(err) { 1478 Log(Error, fmt.Sprintf("Failed to get RoleBinding %s in namespace %s: %v", name, namespace, err)) 1479 return false, err 1480 } 1481 return false, nil 1482 } 1483 1484 for _, s := range rb.Subjects { 1485 if s.Kind == subjectKind && s.Name == subjectName { 1486 return true, nil 1487 } 1488 } 1489 return false, nil 1490 } 1491 1492 func CreateRoleBinding(userOCID string, namespace string, rolebindingname string, clusterrolename string) error { 1493 1494 subject1 := rbacv1.Subject{ 1495 Kind: "User", 1496 APIGroup: "rbac.authorization.k8s.io", 1497 Name: userOCID, 1498 Namespace: "", 1499 } 1500 subjects := []rbacv1.Subject{0: subject1} 1501 1502 rb := rbacv1.RoleBinding{ 1503 TypeMeta: metav1.TypeMeta{ 1504 Kind: "RoleBinding", 1505 APIVersion: "rbac.authorization.k8s.io/v1", 1506 }, 1507 ObjectMeta: metav1.ObjectMeta{ 1508 Name: rolebindingname, 1509 }, 1510 Subjects: subjects, 1511 RoleRef: rbacv1.RoleRef{ 1512 APIGroup: "rbac.authorization.k8s.io", 1513 Kind: "ClusterRole", 1514 Name: clusterrolename, 1515 }, 1516 } 1517 1518 // Get the Kubernetes clientset 1519 clientset, err := k8sutil.GetKubernetesClientset() 1520 if err != nil { 1521 return err 1522 } 1523 1524 _, err = clientset.RbacV1().RoleBindings(namespace).Create(context.TODO(), &rb, metav1.CreateOptions{}) 1525 if err != nil { 1526 Log(Info, fmt.Sprintf("Failed to create role binding: %v", err)) 1527 } 1528 1529 return err 1530 } 1531 1532 // DoesRoleBindingExist returns whether a cluster role with the given name exists in the cluster 1533 func DoesRoleBindingExist(name string, namespace string) (bool, error) { 1534 // Get the Kubernetes clientset 1535 clientset, err := k8sutil.GetKubernetesClientset() 1536 if err != nil { 1537 return false, err 1538 } 1539 1540 rolebinding, err := clientset.RbacV1().RoleBindings(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 1541 if err != nil { 1542 Log(Info, fmt.Sprintf("Failed to verify role binding %s in namespace %s: %v", name, namespace, err)) 1543 return false, err 1544 } 1545 1546 return rolebinding != nil, nil 1547 } 1548 1549 // Execute executes the given command on the pod and container identified by the given names and returns the 1550 // resulting stdout and stderr 1551 func Execute(podName, containerName, namespace string, command []string) (string, string, error) { 1552 clientset, err := k8sutil.GetKubernetesClientset() 1553 if err != nil { 1554 return "", "", err 1555 } 1556 request := clientset.CoreV1().RESTClient().Post().Resource("pods").Name(podName). 1557 Namespace(namespace).SubResource("exec") 1558 request.VersionedParams( 1559 &corev1.PodExecOptions{ 1560 Command: command, 1561 Container: containerName, 1562 Stdin: false, 1563 Stdout: true, 1564 Stderr: true, 1565 TTY: false, 1566 }, 1567 scheme.ParameterCodec, 1568 ) 1569 client, err := k8sutil.GetKubeConfig() 1570 if err != nil { 1571 return "", "", err 1572 } 1573 executor, err := remotecommand.NewSPDYExecutor(client, "POST", request.URL()) 1574 if err != nil { 1575 return "", "", err 1576 } 1577 var stdout, stderr bytes.Buffer 1578 err = executor.Stream(remotecommand.StreamOptions{Stdout: &stdout, Stderr: &stderr}) 1579 1580 return strings.TrimSpace(stdout.String()), strings.TrimSpace(stderr.String()), err 1581 } 1582 1583 // GetConfigMap returns the config map for the passed in ConfigMap Name and Namespace 1584 func GetConfigMap(configMapName string, namespace string) (*corev1.ConfigMap, error) { 1585 // Get the Kubernetes clientset 1586 clientset, err := k8sutil.GetKubernetesClientset() 1587 if err != nil { 1588 return nil, err 1589 } 1590 cmi := clientset.CoreV1().ConfigMaps(namespace) 1591 configMap, err := cmi.Get(context.TODO(), configMapName, metav1.GetOptions{}) 1592 if err != nil { 1593 if !k8serrors.IsNotFound(err) { 1594 Log(Error, fmt.Sprintf("Failed to get Config Map %s from namespace %s: %v ", configMapName, namespace, err)) 1595 } 1596 return nil, err 1597 } 1598 return configMap, nil 1599 } 1600 1601 /* 1602 The following code adds http headers to the kubernetes client invocations. This is done to emulate the functionality of 1603 kubectl auth can-i ... 1604 1605 WrapTransport is configured to point to the function 1606 WrapTransport will be invoked for custom HTTP behavior after the underlying transport is initialized 1607 (either the transport created from TLSClientConfig, Transport, or http.DefaultTransport). 1608 The config may layer other RoundTrippers on top of the returned RoundTripper. 1609 1610 WrapperFunc wraps an http.RoundTripper when a new transport is created for a client, allowing per connection behavior to be injected. 1611 1612 RoundTripper is an interface representing the ability to execute a single HTTP transaction, obtaining the Response for a given Request. 1613 */ 1614 // headerAdder is an http.RoundTripper that adds additional headers to the request 1615 type headerAdder struct { 1616 headers map[string][]string 1617 1618 rt http.RoundTripper 1619 } 1620 1621 func (h *headerAdder) RoundTrip(req *http.Request) (*http.Response, error) { 1622 for k, vv := range h.headers { 1623 for _, v := range vv { 1624 req.Header.Add(k, v) 1625 } 1626 } 1627 return h.rt.RoundTrip(req) 1628 } 1629 1630 func CanI(userOCID string, namespace string, verb string, resource string) (bool, string, error) { 1631 return CanIForAPIGroup(userOCID, namespace, verb, resource, "") 1632 } 1633 1634 func CanIForAPIGroup(userOCID string, namespace string, verb string, resource string, group string) (bool, string, error) { 1635 return CanIForAPIGroupForServiceAccountOrUser(userOCID, namespace, verb, resource, group, false, "") 1636 } 1637 1638 func CanIForAPIGroupForServiceAccountOrUser(saOrUserOCID string, namespace string, verb string, resource string, group string, isServiceAccount bool, saNamespace string) (bool, string, error) { 1639 canI := &v1.SelfSubjectAccessReview{ 1640 TypeMeta: metav1.TypeMeta{ 1641 Kind: "SelfSubjectAccessReview", 1642 APIVersion: "authorization.k8s.io/v1", 1643 }, 1644 Spec: v1.SelfSubjectAccessReviewSpec{ 1645 ResourceAttributes: &v1.ResourceAttributes{ 1646 Namespace: namespace, 1647 Verb: verb, 1648 Group: group, 1649 Version: "", 1650 Resource: resource, 1651 Subresource: "", 1652 Name: "", 1653 }, 1654 }, 1655 } 1656 1657 config, err := k8sutil.GetKubeConfig() 1658 if err != nil { 1659 return false, "", err 1660 } 1661 1662 wt := config.WrapTransport // Config might already have a transport wrapper 1663 if isServiceAccount { 1664 token, err := GetTokenForServiceAccount(saOrUserOCID, saNamespace) 1665 if err != nil { 1666 return false, "", err 1667 } 1668 1669 kubeconfigPath, err := k8sutil.GetKubeConfigLocation() 1670 if err != nil { 1671 Log(Error, fmt.Sprintf("Error getting kubeconfig, error: %v", err)) 1672 return false, "", err 1673 } 1674 1675 clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( 1676 &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfigPath}, 1677 &clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: ""}}) 1678 rawConfig, err := clientConfig.RawConfig() 1679 if err != nil { 1680 return false, "", fmt.Errorf("could not get rawconfig: %v", err) 1681 } 1682 1683 rawConfig.AuthInfos["sa-token"] = &clientcmdapi.AuthInfo{Token: string(token)} 1684 cluster := "" 1685 if len(rawConfig.Clusters) > 0 { 1686 for k, v := range rawConfig.Clusters { 1687 if v != nil { 1688 cluster = k 1689 break 1690 } 1691 } 1692 } 1693 1694 rawConfig.Contexts["sa-context"] = &clientcmdapi.Context{Cluster: cluster, AuthInfo: "sa-token"} 1695 rawConfig.CurrentContext = "sa-context" 1696 config, err = clientcmd.NewNonInteractiveClientConfig(rawConfig, "sa-context", &clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: ""}}, clientConfig.ConfigAccess()).ClientConfig() 1697 if err != nil { 1698 return false, "", err 1699 } 1700 1701 } else { 1702 config.WrapTransport = func(rt http.RoundTripper) http.RoundTripper { 1703 if wt != nil { 1704 rt = wt(rt) 1705 } 1706 header := &headerAdder{ 1707 rt: rt, 1708 headers: map[string][]string{"Impersonate-User": {saOrUserOCID}}, 1709 } 1710 return header 1711 } 1712 } 1713 1714 clientset, err := kubernetes.NewForConfig(config) 1715 if err != nil { 1716 return false, "", err 1717 } 1718 1719 auth, err := clientset.AuthorizationV1().SelfSubjectAccessReviews().Create(context.TODO(), canI, metav1.CreateOptions{}) 1720 if err != nil { 1721 Log(Error, fmt.Sprintf("Failed to check perms: %v", err)) 1722 return false, "", err 1723 } 1724 1725 return auth.Status.Allowed, auth.Status.Reason, nil 1726 } 1727 1728 // GetTokenForServiceAccount returns the token associated with service account 1729 func GetTokenForServiceAccount(sa string, namespace string) ([]byte, error) { 1730 // In k8s 1.24 and later, secret is not created for service account. Create a service account token secret and get 1731 // the token from the same. 1732 secretName := sa + "-token" 1733 if err := createServiceAccountTokenSecret(sa, namespace); err != nil { 1734 msg := fmt.Sprintf("failed to create a service account token secret %s in namespace %s: %v", secretName, 1735 namespace, err) 1736 Log(Error, msg) 1737 return nil, errors.New(msg) 1738 } 1739 1740 clientset, err := k8sutil.GetKubernetesClientset() 1741 if err != nil { 1742 return nil, err 1743 } 1744 secret, err := clientset.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, metav1.GetOptions{}) 1745 if err != nil { 1746 msg := fmt.Sprintf("failed to get secret %s for service account %s in namespace %s: %v", secretName, sa, namespace, err) 1747 Log(Error, msg) 1748 return nil, errors.New(msg) 1749 } 1750 // API token secret is populated asynchronously. As a work-around, wait for up to 10 seconds for the secret to be 1751 // populated. 1752 // https://github.com/kubernetes/kubernetes/pull/108309/files#diff-9037e55a81aefc2c9dc448fa4329772381d50ed1f270575be0ff48e0a949e12eR475 1753 err = wait.Poll(time.Second, 10*time.Second, func() (bool, error) { 1754 if len(secret.Data["token"]) != 0 { 1755 return true, nil 1756 } 1757 secret, err = clientset.CoreV1().Secrets(namespace).Get(context.TODO(), secretName, metav1.GetOptions{}) 1758 if err != nil { 1759 return false, fmt.Errorf("failed to get secret %s for service account %s in namespace %s: %v", secretName, 1760 sa, namespace, err) 1761 } 1762 return false, nil 1763 }) 1764 if err != nil { 1765 msg := fmt.Sprintf("unable to find token in secret %s for service account %s in namespace %s: %v", secretName, sa, namespace, err) 1766 Log(Error, msg) 1767 return nil, errors.New(msg) 1768 } 1769 return secret.Data["token"], nil 1770 } 1771 1772 func GetServiceAccount(namespace, name string) (*corev1.ServiceAccount, error) { 1773 clientset, err := k8sutil.GetKubernetesClientset() 1774 if err != nil { 1775 return nil, err 1776 } 1777 sa, err := clientset.CoreV1().ServiceAccounts(namespace).Get(context.TODO(), name, metav1.GetOptions{}) 1778 if err != nil { 1779 if !k8serrors.IsNotFound(err) { 1780 Log(Error, fmt.Sprintf("Failed to get service account %s in namespace %s: %v", name, namespace, err)) 1781 } 1782 return nil, err 1783 } 1784 return sa, nil 1785 } 1786 1787 func GetPersistentVolumeClaims(namespace string) (map[string]*corev1.PersistentVolumeClaim, error) { 1788 clientset, err := k8sutil.GetKubernetesClientset() 1789 if err != nil { 1790 return nil, err 1791 } 1792 pvcs, err := clientset.CoreV1().PersistentVolumeClaims(namespace).List(context.TODO(), metav1.ListOptions{}) 1793 if err != nil { 1794 return nil, err 1795 } 1796 1797 volumeClaims := make(map[string]*corev1.PersistentVolumeClaim) 1798 1799 for i, pvc := range pvcs.Items { 1800 volumeClaims[pvc.Name] = &pvcs.Items[i] 1801 } 1802 return volumeClaims, nil 1803 } 1804 1805 func GetPersistentVolumes() (map[string]*corev1.PersistentVolume, error) { 1806 clientset, err := k8sutil.GetKubernetesClientset() 1807 if err != nil { 1808 return nil, err 1809 } 1810 pvList, err := clientset.CoreV1().PersistentVolumes().List(context.TODO(), metav1.ListOptions{}) 1811 if err != nil { 1812 return nil, err 1813 } 1814 1815 pvs := make(map[string]*corev1.PersistentVolume) 1816 1817 for i, pvc := range pvList.Items { 1818 pvs[pvc.Name] = &pvList.Items[i] 1819 } 1820 return pvs, nil 1821 } 1822 1823 // DoesVerrazzanoProjectExistInCluster returns whether a VerrazzanoProject with the given name exists in the specified cluster 1824 func DoesVerrazzanoProjectExistInCluster(name string, kubeconfigPath string) (bool, error) { 1825 // Get the clientset 1826 clientset, err := GetVerrazzanoProjectClientsetInCluster(kubeconfigPath) 1827 if err != nil { 1828 return false, err 1829 } 1830 1831 vp, err := clientset.ClustersV1alpha1().VerrazzanoProjects("verrazzano-mc").Get(context.TODO(), name, metav1.GetOptions{}) 1832 if err != nil && !k8serrors.IsNotFound(err) { 1833 Log(Error, fmt.Sprintf("Failed to get VerrazzanoProject %s: %v", name, err)) 1834 return false, err 1835 } 1836 1837 return vp != nil && len(vp.Name) > 0, nil 1838 } 1839 1840 // ContainerHasExpectedArgs returns true if each of the arguments matches a substring of one of the arguments found in the deployment 1841 func ContainerHasExpectedArgs(namespace string, deploymentName string, containerName string, arguments []string) (bool, error) { 1842 deployment, err := GetDeployment(namespace, deploymentName) 1843 if err != nil { 1844 Log(Error, fmt.Sprintf("Deployment %v is not found in the namespace: %v, error: %v", deploymentName, namespace, err)) 1845 return false, nil 1846 } 1847 for _, container := range deployment.Spec.Template.Spec.Containers { 1848 if container.Name == containerName { 1849 return SlicesContainSubsetSubstring(arguments, container.Args), nil 1850 } 1851 } 1852 return false, nil 1853 } 1854 1855 // UpdateConfigMap updates the config map 1856 func UpdateConfigMap(configMap *corev1.ConfigMap) error { 1857 // Get the Kubernetes clientset 1858 clientset, err := k8sutil.GetKubernetesClientset() 1859 if err != nil { 1860 return err 1861 } 1862 1863 cmi := clientset.CoreV1().ConfigMaps(configMap.GetNamespace()) 1864 _, err = cmi.Update(context.TODO(), configMap, metav1.UpdateOptions{}) 1865 if err != nil { 1866 Log(Error, fmt.Sprintf("Failed to update Config Map %s from namespace %s: %v ", configMap.GetName(), configMap.GetNamespace(), err)) 1867 return err 1868 } 1869 return nil 1870 } 1871 1872 // GetContainerEnv returns an array of environment variables in the specified container for the specified deployment 1873 func GetContainerEnv(namespace string, deploymentName string, containerName string) ([]corev1.EnvVar, error) { 1874 deployment, err := GetDeployment(namespace, deploymentName) 1875 if err != nil { 1876 return nil, fmt.Errorf("deployment %s not found in the namespace: %s, error: %v", deploymentName, namespace, err) 1877 } 1878 for _, container := range deployment.Spec.Template.Spec.Containers { 1879 if container.Name == containerName { 1880 return container.Env, nil 1881 } 1882 } 1883 return nil, fmt.Errorf("container %s not found in the namespace: %s", containerName, namespace) 1884 } 1885 1886 func GetDeploymentLabelSelector(namespace, deploymentName string) (*metav1.LabelSelector, error) { 1887 deployment, err := GetDeployment(namespace, deploymentName) 1888 if err != nil { 1889 Log(Error, fmt.Sprintf("Deployment %v not found in the namespace: %v, error: %v", deploymentName, namespace, err)) 1890 return nil, fmt.Errorf("deployment %s not found in the namespace: %s, error: %v", deploymentName, namespace, err) 1891 } 1892 return deployment.Spec.Selector, err 1893 } 1894 1895 // GetContainerImage returns the image used by the specified container for the specified deployment 1896 func GetContainerImage(namespace string, deploymentName string, containerName string) (string, error) { 1897 deployment, err := GetDeployment(namespace, deploymentName) 1898 if err != nil { 1899 Log(Error, fmt.Sprintf("Deployment %v not found in the namespace: %v, error: %v", deploymentName, namespace, err)) 1900 return "", nil 1901 } 1902 for _, container := range deployment.Spec.Template.Spec.Containers { 1903 if container.Name == containerName { 1904 return container.Image, nil 1905 } 1906 } 1907 return "", fmt.Errorf("container %v not found in the namespace: %v", containerName, namespace) 1908 } 1909 1910 // WaitForVZCondition waits till the VZ CR reaches the given condition 1911 func WaitForVZCondition(conditionType v1beta1.ConditionType, pollingInterval, timeout time.Duration) { 1912 gomega.Eventually(func() bool { 1913 cr, err := GetVerrazzanoV1beta1() 1914 if err != nil { 1915 Log(Error, err.Error()) 1916 return false 1917 } 1918 for _, condition := range cr.Status.Conditions { 1919 Log(Info, fmt.Sprintf("Evaluating condition: [%s - %s]", condition.Type, condition.Status)) 1920 if condition.Type == conditionType && condition.Status == corev1.ConditionTrue { 1921 return true 1922 } 1923 } 1924 return false 1925 }).WithPolling(pollingInterval).WithTimeout(timeout).Should(gomega.BeTrue()) 1926 } 1927 1928 // DeleteConfigMap to delete the ConfigMap with the given name and namespace 1929 func DeleteConfigMap(namespace string, name string) error { 1930 clientset, err := k8sutil.GetKubernetesClientset() 1931 if err != nil { 1932 return err 1933 } 1934 return clientset.CoreV1().ConfigMaps(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}) 1935 } 1936 1937 // CreateConfigMap creates the ConfigMap 1938 func CreateConfigMap(configMap *corev1.ConfigMap) error { 1939 clientset, err := k8sutil.GetKubernetesClientset() 1940 if err != nil { 1941 return err 1942 } 1943 _, err = clientset.CoreV1().ConfigMaps(configMap.Namespace).Create(context.TODO(), configMap, metav1.CreateOptions{}) 1944 if err != nil { 1945 return err 1946 } 1947 return nil 1948 } 1949 1950 func createServiceAccountTokenSecret(serviceAccount string, namespace string) error { 1951 var secret corev1.Secret 1952 secret.Name = serviceAccount + "-token" 1953 secret.Namespace = namespace 1954 secret.Type = corev1.SecretTypeServiceAccountToken 1955 secret.Annotations = map[string]string{ 1956 corev1.ServiceAccountNameKey: serviceAccount, 1957 } 1958 clientset, err := k8sutil.GetKubernetesClientset() 1959 if err != nil { 1960 return err 1961 } 1962 _, err = clientset.CoreV1().Secrets(namespace).Create(context.TODO(), &secret, metav1.CreateOptions{}) 1963 if err != nil && !k8serrors.IsAlreadyExists(err) { 1964 return err 1965 } 1966 return nil 1967 } 1968 1969 func VzReadyV1beta1() (bool, error) { 1970 cr, err := GetVerrazzanoV1beta1() 1971 if err != nil { 1972 return false, err 1973 } 1974 if cr.Status.State == v1beta1.VzStateReady { 1975 return true, nil 1976 } 1977 return false, fmt.Errorf("CR in state %s, not Ready yet", cr.Status.State) 1978 } 1979 1980 // IsDexEnabled returns true if the Dex component is enabled, false otherwise 1981 func IsDexEnabled(kubeconfigPath string) bool { 1982 vz, err := GetVerrazzanoInstallResourceInClusterV1beta1(kubeconfigPath) 1983 if err != nil { 1984 Log(Error, fmt.Sprintf("Error Verrazzano Resource: %v", err)) 1985 return false 1986 } 1987 if vz.Spec.Components.Dex == nil || vz.Spec.Components.Dex.Enabled == nil { 1988 return false 1989 } 1990 return *vz.Spec.Components.Dex.Enabled 1991 } 1992 1993 func ValidateDeploymentContainerImage(namespace, deploymentName, containerName, version string) (bool, error) { 1994 // Get the deployment 1995 deployment, err := GetDeployment(namespace, deploymentName) 1996 if err != nil { 1997 return false, fmt.Errorf("deployment %s not found in the namespace: %s, error: %v", deploymentName, namespace, err) 1998 } 1999 2000 for _, container := range deployment.Spec.Template.Spec.Containers { 2001 if containerName == container.Name { 2002 split := strings.Split(container.Image, ":") 2003 if !strings.HasPrefix(split[len(split)-1], version) { 2004 errStr := fmt.Sprintf("namespace %v deployment %v container %v image %v does not contain correct image version %v", 2005 namespace, deploymentName, containerName, container.Name, version) 2006 Log(Error, errStr) 2007 } 2008 break 2009 } 2010 } 2011 return true, nil 2012 }