github.com/verrazzano/verrazzano@v1.7.0/tools/vz/cmd/helpers/vpo.go (about) 1 // Copyright (c) 2022, 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 helpers 5 6 import ( 7 "bufio" 8 "context" 9 "fmt" 10 "io" 11 "net/http" 12 "os" 13 "regexp" 14 "strings" 15 "time" 16 17 "github.com/spf13/cobra" 18 vzconstants "github.com/verrazzano/verrazzano/pkg/constants" 19 "github.com/verrazzano/verrazzano/pkg/k8s/ready" 20 "github.com/verrazzano/verrazzano/pkg/k8sutil" 21 "github.com/verrazzano/verrazzano/pkg/semver" 22 "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1beta1" 23 vpoconst "github.com/verrazzano/verrazzano/platform-operator/constants" 24 "github.com/verrazzano/verrazzano/tools/vz/pkg/constants" 25 "github.com/verrazzano/verrazzano/tools/vz/pkg/helpers" 26 appsv1 "k8s.io/api/apps/v1" 27 corev1 "k8s.io/api/core/v1" 28 "k8s.io/apimachinery/pkg/api/errors" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/labels" 31 "k8s.io/apimachinery/pkg/selection" 32 "k8s.io/apimachinery/pkg/types" 33 "k8s.io/client-go/kubernetes" 34 clipkg "sigs.k8s.io/controller-runtime/pkg/client" 35 ) 36 37 const VpoSimpleLogFormatRegexp = `"level":"(.*?)","@timestamp":"(.*?)",(.*?)"message":"(.*?)",` 38 const accessErrorMsg = "Failed to access the Verrazzano operator.yaml file %s: %s" 39 const applyErrorMsg = "Failed to apply the Verrazzano operator.yaml file %s: %s" 40 41 // deleteLeftoverPlatformOperatorSig is a function needed for unit test override 42 type deleteLeftoverPlatformOperatorSig func(client clipkg.Client) error 43 44 // DeleteFunc is the default deleteLeftoverPlatformOperator function 45 var DeleteFunc deleteLeftoverPlatformOperatorSig = deleteLeftoverPlatformOperator 46 47 func SetDeleteFunc(f deleteLeftoverPlatformOperatorSig) { 48 DeleteFunc = f 49 } 50 51 func SetDefaultDeleteFunc() { 52 DeleteFunc = deleteLeftoverPlatformOperator 53 } 54 55 func FakeDeleteFunc(client clipkg.Client) error { 56 return nil 57 } 58 59 // applyYAMLSig Allow overriding the applyYAML function for unit testing 60 type applyYAMLSig func(filename string, client clipkg.Client, vzHelper helpers.VZHelper) error 61 62 var applyYAMLFunc applyYAMLSig = applyYAML 63 64 func SetApplyYAMLFunc(f applyYAMLSig) { 65 applyYAMLFunc = f 66 } 67 68 func SetDefaultApplyYAMLFunc() { 69 applyYAMLFunc = applyYAML 70 } 71 72 // vpoIsReadySig Allow overriding the vpoIsReady function for unit testing 73 type vpoIsReadySig func(client clipkg.Client) (bool, error) 74 75 var vpoIsReadyFunc vpoIsReadySig = vpoIsReady 76 77 func SetVPOIsReadyFunc(f vpoIsReadySig) { 78 vpoIsReadyFunc = f 79 } 80 81 func SetDefaultVPOIsReadyFunc() { 82 vpoIsReadyFunc = vpoIsReady 83 } 84 85 // GetExistingVPODeployment - get existing Verrazzano Platform operator Deployment from the cluster 86 func GetExistingVPODeployment(client clipkg.Client) (*appsv1.Deployment, error) { 87 deploy := appsv1.Deployment{} 88 namespacedName := types.NamespacedName{Name: constants.VerrazzanoPlatformOperator, Namespace: vzconstants.VerrazzanoInstallNamespace} 89 if err := client.Get(context.TODO(), namespacedName, &deploy); err != nil { 90 if errors.IsNotFound(err) { 91 return nil, nil 92 } 93 return nil, failedToGetVPODeployment(err) 94 95 } 96 return &deploy, nil 97 } 98 99 // GetExistingVPOWebhookDeployment - get existing Verrazzano Platform operator webhook deployment from the cluster 100 func GetExistingVPOWebhookDeployment(client clipkg.Client) (*appsv1.Deployment, error) { 101 deploy := appsv1.Deployment{} 102 namespacedName := types.NamespacedName{Name: constants.VerrazzanoPlatformOperatorWebhook, Namespace: vzconstants.VerrazzanoInstallNamespace} 103 if err := client.Get(context.TODO(), namespacedName, &deploy); err != nil { 104 if errors.IsNotFound(err) { 105 return nil, nil 106 } 107 return nil, fmt.Errorf("Failed to get existing %s deployment: %s", constants.VerrazzanoPlatformOperatorWebhook, err.Error()) 108 109 } 110 return &deploy, nil 111 } 112 113 // GetExistingPrivateRegistrySettings gets the private registry env var settings on existing 114 // VPO Deployment, if present 115 func getExistingPrivateRegistrySettings(vpoDeploy *appsv1.Deployment) (string, string) { 116 registry := "" 117 imagePrefix := "" 118 for _, container := range vpoDeploy.Spec.Template.Spec.Containers { 119 if container.Name == constants.VerrazzanoPlatformOperator { 120 for _, env := range container.Env { 121 if env.Name == vpoconst.RegistryOverrideEnvVar { 122 registry = env.Value 123 } else if env.Name == vpoconst.ImageRepoOverrideEnvVar { 124 imagePrefix = env.Value 125 } 126 } 127 } 128 } 129 return registry, imagePrefix 130 } 131 132 // UsePlatformOperatorUninstallJob determines whether the version of the platform operator is using an uninstall job. 133 // The uninstall job was removed with Verrazzano 1.4.0. 134 func UsePlatformOperatorUninstallJob(client clipkg.Client) (bool, error) { 135 deployment := &appsv1.Deployment{} 136 err := client.Get(context.TODO(), types.NamespacedName{Namespace: vzconstants.VerrazzanoInstallNamespace, Name: constants.VerrazzanoPlatformOperator}, deployment) 137 if err != nil { 138 return false, fmt.Errorf("Failed to find %s/%s: %s", vzconstants.VerrazzanoInstallNamespace, constants.VerrazzanoPlatformOperator, err.Error()) 139 } 140 141 // label does not exist therefore uninstall job is being used 142 version, ok := deployment.Labels["app.kubernetes.io/version"] 143 if !ok { 144 return true, nil 145 } 146 147 minVersion := semver.SemVersion{Major: 1, Minor: 4, Patch: 0} 148 vzVersion, err := semver.NewSemVersion(version) 149 if err != nil { 150 return false, err 151 } 152 153 // Version of platform operator is less than 1.4.0 therefore uninstall job is being used 154 if vzVersion.IsLessThan(&minVersion) { 155 return true, nil 156 } 157 158 return false, nil 159 } 160 161 // ApplyPlatformOperatorYaml applies a given version of the platform operator yaml file 162 func ApplyPlatformOperatorYaml(cmd *cobra.Command, client clipkg.Client, vzHelper helpers.VZHelper, version string) error { 163 localOperatorFilename, userVisibleFilename, isTempFile, err := getOrDownloadOperatorYAML(cmd, version, vzHelper) 164 if err != nil { 165 return err 166 } 167 if isTempFile { 168 // the operator YAML is a temporary file that must be deleted after applying it 169 defer os.Remove(localOperatorFilename) 170 } 171 172 if localOperatorFilename, err = processOperatorYAMLPrivateRegistry(cmd, localOperatorFilename); err != nil { 173 return err 174 } 175 176 if _, err := os.Stat(localOperatorFilename); err != nil { 177 return err 178 } 179 180 // Delete previous verrazzano-platform-operator deployments when we have successfully downloaded new one. 181 // This allows for the verrazzano-platform-operator validatingWebhookConfiguration to be updated with the correct caBundle. 182 if err = DeleteFunc(client); err != nil { 183 return err 184 } 185 186 // Apply the Verrazzano operator.yaml 187 fmt.Fprintf(vzHelper.GetOutputStream(), fmt.Sprintf("Applying the file %s\n", userVisibleFilename)) 188 return applyYAMLFunc(localOperatorFilename, client, vzHelper) 189 } 190 191 func applyYAML(filename string, client clipkg.Client, vzHelper helpers.VZHelper) error { 192 yamlApplier := k8sutil.NewYAMLApplier(client, "") 193 err := yamlApplier.ApplyF(filename) 194 if err != nil { 195 return fmt.Errorf(applyErrorMsg, filename, err.Error()) 196 } 197 198 // Dump out the object result messages 199 for _, result := range yamlApplier.ObjectResultMsgs() { 200 fmt.Fprintf(vzHelper.GetOutputStream(), fmt.Sprintf("%s\n", strings.ToLower(result))) 201 } 202 return nil 203 } 204 205 // processOperatorYAMLPrivateRegistry - examines private registry related command flags and processes 206 // the operator YAML file as needed 207 func processOperatorYAMLPrivateRegistry(cmd *cobra.Command, operatorFilename string) (string, error) { 208 // check for private registry flags 209 if !cmd.PersistentFlags().Changed(constants.ImageRegistryFlag) && 210 !cmd.PersistentFlags().Changed(constants.ImagePrefixFlag) { 211 return operatorFilename, nil 212 } 213 var imageRegistry string 214 var imagePrefix string 215 var err error 216 if imageRegistry, err = cmd.PersistentFlags().GetString(constants.ImageRegistryFlag); err != nil { 217 return operatorFilename, err 218 } 219 if imagePrefix, err = cmd.PersistentFlags().GetString(constants.ImagePrefixFlag); err != nil { 220 return operatorFilename, err 221 } 222 223 return updateOperatorYAMLPrivateRegistry(operatorFilename, imageRegistry, imagePrefix) 224 } 225 226 func getOrDownloadOperatorYAML(cmd *cobra.Command, version string, vzHelper helpers.VZHelper) (string, string, bool, error) { 227 // Was an operator-file passed on the command line? 228 operatorFile, err := getOperatorFileFromFlag(cmd) 229 if err != nil { 230 return "", "", false, err 231 } 232 233 isTempFile := false 234 // If the operatorFile was specified, is it a local or remote file? 235 url := "" 236 localOperatorFilename := "" 237 if len(operatorFile) > 0 { 238 if strings.HasPrefix(strings.ToLower(operatorFile), "https://") { 239 url = operatorFile 240 } else { 241 localOperatorFilename = operatorFile 242 } 243 } else { 244 url, err = helpers.GetOperatorYaml(version) 245 if err != nil { 246 return "", "", false, err 247 } 248 } 249 250 userVisibleFilename := operatorFile 251 // if we have a URL, download the file 252 if len(url) > 0 { 253 isTempFile = true 254 userVisibleFilename = url 255 if localOperatorFilename, err = downloadOperatorYAML(url, vzHelper); err != nil { 256 return localOperatorFilename, userVisibleFilename, isTempFile, err 257 } 258 } 259 return localOperatorFilename, userVisibleFilename, isTempFile, nil 260 } 261 262 // downloadOperatorYAML downloads the operator YAML file from the given URL and returns the 263 // path to the temp file where it is stored. 264 func downloadOperatorYAML(url string, vzHelper helpers.VZHelper) (string, error) { 265 // Get the Verrazzano operator.yaml and store it in a temp file 266 httpClient := vzHelper.GetHTTPClient() 267 resp, err := httpClient.Get(url) 268 if err != nil { 269 return "", fmt.Errorf(accessErrorMsg, url, err.Error()) 270 } 271 if resp.StatusCode != http.StatusOK { 272 return "", fmt.Errorf(accessErrorMsg, url, resp.Status) 273 } 274 // Store response in a temporary file 275 tmpFile, err := os.CreateTemp("", "vz") 276 if err != nil { 277 return "", fmt.Errorf(applyErrorMsg, url, err.Error()) 278 } 279 _, err = tmpFile.ReadFrom(resp.Body) 280 if err != nil { 281 os.Remove(tmpFile.Name()) 282 return "", fmt.Errorf(applyErrorMsg, url, err.Error()) 283 } 284 return tmpFile.Name(), nil 285 } 286 287 // WaitForPlatformOperator waits for the verrazzano-platform-operator to be ready 288 func WaitForPlatformOperator(client clipkg.Client, vzHelper helpers.VZHelper, condType v1beta1.ConditionType, timeout time.Duration) (string, error) { 289 // Provide the user with feedback while waiting for the verrazzano-platform-operator to be ready 290 feedbackChan := make(chan bool) 291 defer close(feedbackChan) 292 go func(outputStream io.Writer) { 293 seconds := 0 294 for { 295 select { 296 case <-feedbackChan: 297 fmt.Fprint(outputStream, "\n") 298 return 299 default: 300 time.Sleep(constants.VerrazzanoPlatformOperatorWait * time.Second) 301 seconds += constants.VerrazzanoPlatformOperatorWait 302 fmt.Fprintf(outputStream, fmt.Sprintf("\rWaiting for %s to be ready before starting %s - %d seconds", constants.VerrazzanoPlatformOperator, getOperationString(condType), seconds)) 303 } 304 } 305 }(vzHelper.GetOutputStream()) 306 307 // Wait for the verrazzano-platform-operator pod to be found 308 secondsWaited := 0 309 maxSecondsToWait := int(timeout.Seconds()) 310 for { 311 ready, err := vpoIsReadyFunc(client) 312 if ready { 313 break 314 } 315 316 if secondsWaited > maxSecondsToWait { 317 feedbackChan <- true 318 return "", fmt.Errorf("Waiting for %s pod in namespace %s: %v", constants.VerrazzanoPlatformOperator, vzconstants.VerrazzanoInstallNamespace, err) 319 } 320 time.Sleep(constants.VerrazzanoPlatformOperatorWait * time.Second) 321 secondsWaited += constants.VerrazzanoPlatformOperatorWait 322 } 323 feedbackChan <- true 324 325 // Return the platform operator pod name 326 return GetVerrazzanoPlatformOperatorPodName(client) 327 } 328 329 // WaitForOperationToComplete waits for the Verrazzano install/upgrade to complete and 330 // shows the logs of the ongoing Verrazzano install/upgrade. 331 func WaitForOperationToComplete(client clipkg.Client, kubeClient kubernetes.Interface, vzHelper helpers.VZHelper, namespacedName types.NamespacedName, timeout time.Duration, vpoTimeout time.Duration, logFormat LogFormat, condType v1beta1.ConditionType) error { 332 resChan := make(chan error, 1) 333 defer close(resChan) 334 335 feedbackChan := make(chan bool) 336 defer close(feedbackChan) 337 338 // goroutine to stream log file output - this goroutine will be left running when this 339 // function is exited because there is no way to cancel the blocking read to the input stream. 340 re := regexp.MustCompile(VpoSimpleLogFormatRegexp) 341 go func(kubeClient kubernetes.Interface, outputStream io.Writer) { 342 var sc *bufio.Scanner 343 var err error 344 secondsWaited := 0 345 maxSecondsToWait := int(vpoTimeout.Seconds()) 346 const secondsPerRetry = 10 347 348 for { 349 if sc == nil { 350 sc, err = getScanner(client, kubeClient) 351 if err != nil { 352 fmt.Fprintf(outputStream, fmt.Sprintf("Failed to connect to the console output, waited %d of %d seconds to recover: %v\n", secondsWaited, maxSecondsToWait, err)) 353 secondsWaited += secondsPerRetry 354 if secondsWaited > maxSecondsToWait { 355 return 356 } 357 time.Sleep(secondsPerRetry * time.Second) 358 continue 359 } 360 secondsWaited = 0 361 sc.Split(bufio.ScanLines) 362 } 363 364 scannedOk := sc.Scan() 365 if !scannedOk { 366 errText := "" 367 if sc.Err() != nil { 368 errText = fmt.Sprintf(": %v", sc.Err()) 369 } 370 fmt.Fprintf(outputStream, fmt.Sprintf("Lost connection to the console output, attempting to reconnect%s\n", errText)) 371 sc = nil 372 continue 373 } 374 if logFormat == LogFormatSimple { 375 PrintSimpleLogFormat(sc, outputStream, re) 376 } else if logFormat == LogFormatJSON { 377 fmt.Fprintf(outputStream, fmt.Sprintf("%s\n", sc.Text())) 378 } 379 } 380 }(kubeClient, vzHelper.GetOutputStream()) 381 382 startTime := time.Now().UTC() 383 384 // goroutine to wait for the completion of the operation 385 go func() { 386 for { 387 // Pause before each status check 388 time.Sleep(1 * time.Second) 389 select { 390 case <-feedbackChan: 391 return 392 default: 393 // Return when the Verrazzano operation has completed 394 vz, err := helpers.GetVerrazzanoResource(client, namespacedName) 395 if err != nil { 396 // Retry if there is a problem getting the resource. It is ok to keep retrying since 397 // WaitForOperationToComplete main routine will timeout. 398 time.Sleep(10 * time.Second) 399 continue 400 } 401 for _, condition := range vz.Status.Conditions { 402 // Operation condition met for install/upgrade 403 if condition.Type == condType { 404 condTime, err := time.Parse(time.RFC3339, condition.LastTransitionTime) 405 if err != nil { 406 resChan <- fmt.Errorf("Failed parsing status condition lastTransitionTime: %s", err.Error()) 407 return 408 } 409 // There can be multiple conditions with the same type. Make sure we find a match 410 // beyond the start time. 411 if condTime.After(startTime) { 412 resChan <- nil 413 return 414 } 415 } 416 } 417 } 418 } 419 }() 420 421 select { 422 case result := <-resChan: 423 return result 424 case <-time.After(timeout): 425 if timeout.Nanoseconds() != 0 { 426 return fmt.Errorf("Timeout %v exceeded waiting for %s to complete", timeout.String(), getOperationString(condType)) 427 } 428 } 429 430 return nil 431 } 432 433 func getScanner(client clipkg.Client, kubeClient kubernetes.Interface) (*bufio.Scanner, error) { 434 vpoPodName, err := GetVerrazzanoPlatformOperatorPodName(client) 435 if err != nil { 436 return nil, err 437 } 438 439 rc, err := GetVpoLogStream(kubeClient, vpoPodName) 440 if err != nil { 441 return nil, fmt.Errorf("failed to stream log output: %v", err) 442 } 443 444 return bufio.NewScanner(rc), nil 445 } 446 447 // GetVerrazzanoPlatformOperatorPodName returns the VPO pod name 448 func GetVerrazzanoPlatformOperatorPodName(client clipkg.Client) (string, error) { 449 appLabel, _ := labels.NewRequirement("app", selection.Equals, []string{constants.VerrazzanoPlatformOperator}) 450 labelSelector := labels.NewSelector() 451 labelSelector = labelSelector.Add(*appLabel) 452 podList := corev1.PodList{} 453 err := client.List( 454 context.TODO(), 455 &podList, 456 &clipkg.ListOptions{ 457 Namespace: vzconstants.VerrazzanoInstallNamespace, 458 LabelSelector: labelSelector, 459 }) 460 if err != nil { 461 return "", fmt.Errorf("Waiting for %s, failed to list pods: %s", constants.VerrazzanoPlatformOperator, err.Error()) 462 } 463 if len(podList.Items) == 0 { 464 return "", fmt.Errorf("Failed to find the Verrazzano platform operator in namespace %s", vzconstants.VerrazzanoInstallNamespace) 465 } 466 if len(podList.Items) > 1 { 467 return "", fmt.Errorf("Waiting for %s, more than one %s pod was found in namespace %s", constants.VerrazzanoPlatformOperator, constants.VerrazzanoPlatformOperator, vzconstants.VerrazzanoInstallNamespace) 468 } 469 470 return podList.Items[0].Name, nil 471 } 472 473 // GetVpoLogStream returns the stream to the verrazzano-platform-operator log file 474 func GetVpoLogStream(kubeClient kubernetes.Interface, vpoPodName string) (io.ReadCloser, error) { 475 // Tail the log messages from the verrazzano-platform-operator starting at the current time. 476 // 477 // The stream is intentionally not closed due to not being able to cancel a blocking read. The calls to 478 // read input from this stream (sc.Scan) are blocking. If you try to close the stream, it hangs until the 479 // next read is satisfied, which may never occur if there is no more log output. 480 sinceTime := metav1.Now() 481 rc, err := kubeClient.CoreV1().Pods(vzconstants.VerrazzanoInstallNamespace).GetLogs(vpoPodName, &corev1.PodLogOptions{ 482 Container: constants.VerrazzanoPlatformOperator, 483 Follow: true, 484 SinceTime: &sinceTime, 485 }).Stream(context.TODO()) 486 if err != nil { 487 return nil, fmt.Errorf("Failed to read the %s log file: %s", constants.VerrazzanoPlatformOperator, err.Error()) 488 } 489 return rc, nil 490 } 491 492 // PrintSimpleLogFormat display a VPO log message with the simple log format 493 func PrintSimpleLogFormat(sc *bufio.Scanner, outputStream io.Writer, regexp *regexp.Regexp) { 494 res := regexp.FindAllStringSubmatch(sc.Text(), -1) 495 // res[0][2] is the timestamp 496 // res[0][1] is the level 497 // res[0][4] is the message 498 499 if res != nil && isAcceptableMessageForSimpleLogFormat(res[0][1], res[0][4]) { 500 // Print each log message in the form "timestamp level message". 501 // For example, "2022-06-03T00:05:10.042Z info Component keycloak successfully installed" 502 fmt.Fprintf(outputStream, fmt.Sprintf("%s %s %s\n", res[0][2], res[0][1], res[0][4])) 503 } 504 } 505 506 // isNotAcceptableMessageForSimpleLogFormat returns true if a message should be filtered out from the console 507 func isAcceptableMessageForSimpleLogFormat(level string, message string) bool { 508 if (level != "error" && level != "info") || strings.Contains(message, "Starting EventSource") { 509 return false 510 } 511 if level == "info" && (strings.Contains(message, "replica") || strings.Contains(message, "certificate") || strings.Contains(message, "Associating NetworkPolicy") || strings.Contains(message, "Updating the Labels and Annotations") || strings.Contains(message, "Resetting initial MySQL pod readiness check") || strings.Contains(message, "waiting for readiness gates") || strings.Contains(message, "required ingresses") || strings.Contains(message, "waiting for") || strings.Contains(message, "Waiting for") || strings.Contains(message, "Certificate") || strings.Contains(message, "Replica") || strings.Contains(message, "Starting Controller") || strings.Contains(message, "Starting workers") || strings.Contains(message, "Helm") || strings.Contains(message, "helm") || strings.Contains(message, "Applying yaml") || strings.Contains(message, "Successfully deleted")) { 512 return false 513 } 514 return true 515 516 } 517 518 // return the operation string to display 519 func getOperationString(condType v1beta1.ConditionType) string { 520 operation := "install" 521 if condType == v1beta1.CondUpgradeComplete { 522 operation = "upgrade" 523 } 524 return operation 525 } 526 527 // vpoIsReady check that the named deployments have the minimum number of specified replicas ready and available 528 func vpoIsReady(client clipkg.Client) (bool, error) { 529 var expectedReplicas int32 = 1 530 deployment := appsv1.Deployment{} 531 namespacedName := types.NamespacedName{Name: constants.VerrazzanoPlatformOperator, Namespace: vzconstants.VerrazzanoInstallNamespace} 532 if err := client.Get(context.TODO(), namespacedName, &deployment); err != nil { 533 if errors.IsNotFound(err) { 534 return false, nil 535 } 536 return false, fmt.Errorf("Failed getting deployment %s: %s", constants.VerrazzanoPlatformOperator, err.Error()) 537 } 538 if deployment.Status.UpdatedReplicas < expectedReplicas { 539 return false, nil 540 } 541 if deployment.Status.AvailableReplicas < expectedReplicas { 542 return false, nil 543 } 544 545 if !ready.PodsReadyDeployment(nil, client, namespacedName, deployment.Spec.Selector, expectedReplicas, constants.VerrazzanoPlatformOperator) { 546 return false, nil 547 } 548 549 return true, nil 550 } 551 552 func failedToGetVPODeployment(err error) error { 553 return fmt.Errorf("Failed to get existing %s deployment: %s", constants.VerrazzanoPlatformOperator, err.Error()) 554 } 555 556 // deleteLeftoverPlatformOperator deletes leftover verrazzano-platform-operator deployments after an abort. 557 // This allows for the verrazzano-platform-operator validatingWebhookConfiguration to be updated with an updated caBundle. 558 func deleteLeftoverPlatformOperator(client clipkg.Client) error { 559 vpoDeployment := appsv1.Deployment{ 560 ObjectMeta: metav1.ObjectMeta{ 561 Namespace: vzconstants.VerrazzanoInstallNamespace, 562 Name: constants.VerrazzanoPlatformOperator, 563 }, 564 } 565 if err := client.Delete(context.TODO(), &vpoDeployment); err != nil { 566 if !errors.IsNotFound(err) { 567 return fmt.Errorf("Failed to delete leftover %s deployment: %s", constants.VerrazzanoPlatformOperator, err.Error()) 568 } 569 } 570 vpoWebHookDeployment := appsv1.Deployment{ 571 ObjectMeta: metav1.ObjectMeta{ 572 Namespace: vzconstants.VerrazzanoInstallNamespace, 573 Name: constants.VerrazzanoPlatformOperatorWebhook, 574 }, 575 } 576 if err := client.Delete(context.TODO(), &vpoWebHookDeployment); err != nil { 577 if !errors.IsNotFound(err) { 578 return fmt.Errorf("Failed to delete leftover %s deployment: %s", constants.VerrazzanoPlatformOperatorWebhook, err.Error()) 579 } 580 } 581 582 return nil 583 } 584 585 // ValidatePrivateRegistry - Validate private registry settings in command against 586 // those in existing VPO deployment, if any 587 func ValidatePrivateRegistry(cmd *cobra.Command, client clipkg.Client) error { 588 vpoDeploy, err := GetExistingVPODeployment(client) 589 if err != nil { 590 return fmt.Errorf("Failed to get existing %s deployment: %v", 591 constants.VerrazzanoPlatformOperator, err) 592 } 593 if vpoDeploy == nil { 594 // no existing VPO deployment, nothing to validate 595 return nil 596 } 597 existingImageRegistry, existingImagePrefix := getExistingPrivateRegistrySettings(vpoDeploy) 598 newRegistry, err := cmd.PersistentFlags().GetString(constants.ImageRegistryFlag) 599 if err != nil { 600 return err 601 } 602 newImagePrefix, err := cmd.PersistentFlags().GetString(constants.ImagePrefixFlag) 603 if err != nil { 604 return err 605 } 606 if existingImageRegistry != newRegistry || existingImagePrefix != newImagePrefix { 607 return fmt.Errorf( 608 imageRegistryMismatchError(existingImageRegistry, existingImagePrefix, newRegistry, newImagePrefix)) 609 } 610 return nil 611 } 612 613 func imageRegistryMismatchError(existingRegistry, existingPrefix, newRegistry, newPrefix string) string { 614 existingRegistryMsg := "" 615 newRegistryMsg := "" 616 if existingRegistry == "" && existingPrefix == "" { 617 existingRegistryMsg = "the public Verrazzano image repository" 618 } else { 619 existingRegistryMsg = fmt.Sprintf("image-registry %s and image-prefix %s", existingRegistry, existingPrefix) 620 } 621 if newRegistry == "" && newPrefix == "" { 622 newRegistryMsg = "the public Verrazzano image repository" 623 } else { 624 newRegistryMsg = fmt.Sprintf("image-registry %s and image-prefix %s", newRegistry, newPrefix) 625 } 626 return fmt.Sprintf("The existing Verrazzano installation uses %s, but you provided %s", existingRegistryMsg, newRegistryMsg) 627 }