github.com/verrazzano/verrazzano@v1.7.0/tools/vz/cmd/install/state.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 install
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"sort"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/spf13/cobra"
    14  	"github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1alpha1"
    15  	"github.com/verrazzano/verrazzano/tools/vz/pkg/constants"
    16  	"github.com/verrazzano/verrazzano/tools/vz/pkg/helpers"
    17  	"sigs.k8s.io/controller-runtime/pkg/client"
    18  )
    19  
    20  // displayInstallationProgress Checks state of components until all of them are Ready or specified timeout is reached
    21  func displayInstallationProgress(cmd *cobra.Command, vzHelper helpers.VZHelper, duration time.Duration) error {
    22  	client, err := vzHelper.GetClient(cmd)
    23  	if err != nil {
    24  		fmt.Println("Error in client", err)
    25  		return err
    26  	}
    27  	time.Sleep(time.Second * 10)
    28  	fmt.Print("\033[2J\033[H") // Clear the console before entering the loop
    29  	startTime := time.Now()
    30  	for {
    31  		// Save the cursor position
    32  		fmt.Print("\033[s")
    33  		statusMap, err := getEnabledComponentMap(client)
    34  		if err != nil {
    35  			fmt.Println("Error in fetching verrazzano resource", err)
    36  			return err
    37  		}
    38  		if len(statusMap) == 0 {
    39  			return fmt.Errorf("No enabled components found")
    40  		}
    41  		displayComponentStatus(statusMap)
    42  		progress, nonReady := calculateProgress(statusMap)
    43  		displayProgressBar(progress)
    44  		if isAllReady(statusMap) {
    45  			fmt.Println("\nInstallation Completed. All components are ready")
    46  			break
    47  		} else {
    48  			if time.Since(startTime) >= duration {
    49  
    50  				return fmt.Errorf("timed out waiting for components to be ready-> %s", strings.TrimPrefix(strings.Join(nonReady, ", "), ", "))
    51  			}
    52  			time.Sleep(constants.RefreshRate)
    53  			// Restore the cursor position to the beginning of the table and the progress bar
    54  			fmt.Print("\033[u")
    55  		}
    56  	}
    57  	return nil
    58  }
    59  
    60  // displayComponentStatus displays the component status in the console.
    61  // Parameter:- statusMap: A map containing the component names and their status
    62  func displayComponentStatus(statusMap map[string]v1alpha1.CompStateType) {
    63  	// Move the cursor to the top-left position and clear the console
    64  	fmt.Fprintf(os.Stdout, "\033[H\033[J")
    65  	fmt.Println("Component\t\t\t\t\tStatus")
    66  	fmt.Println("-------------------------------------------------------")
    67  	// Create a slice to hold the component names for sorting
    68  	var componentNames []string
    69  	for componentName := range statusMap {
    70  		componentNames = append(componentNames, componentName)
    71  	}
    72  	sort.Strings(componentNames)
    73  	// Display the components in alphabetical order
    74  	for _, componentName := range componentNames {
    75  		status := statusMap[componentName]
    76  		// Move the cursor to the beginning of the current line and clear the line
    77  		fmt.Fprintf(os.Stdout, "\r\033[K")
    78  		fmt.Printf("%-40s\t%s\n", componentName, status)
    79  	}
    80  }
    81  
    82  // isAllReady checks if all components are in "Ready" state.
    83  // Returns: bool: True if all components are in "Ready" state, false otherwise
    84  func isAllReady(statusMap map[string]v1alpha1.CompStateType) bool {
    85  	for _, status := range statusMap {
    86  		if status != "Ready" {
    87  			return false
    88  		}
    89  	}
    90  	return true
    91  }
    92  
    93  // getEnabledComponentMap retrieves the enabled components and their status.
    94  func getEnabledComponentMap(client client.Client) (map[string]v1alpha1.CompStateType, error) {
    95  	vz, err := helpers.FindVerrazzanoResource(client)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	statusMap := make(map[string]v1alpha1.CompStateType)
   100  	for componentName, componentStatus := range vz.Status.Components {
   101  		if componentStatus.State != "Disabled" {
   102  			statusMap[componentName] = v1alpha1.CompStateType(componentStatus.State)
   103  		}
   104  	}
   105  	return statusMap, nil
   106  }
   107  
   108  // calculateProgress calculates the installation progress based on the number of components in "Ready" state.
   109  func calculateProgress(statusMap map[string]v1alpha1.CompStateType) (float64, []string) {
   110  	sync := make([]string, 1)
   111  	readyCount := 0
   112  	totalCount := len(statusMap)
   113  	for index, status := range statusMap {
   114  		if status == "Ready" {
   115  			readyCount++
   116  		} else {
   117  			sync = append(sync, index)
   118  		}
   119  	}
   120  	return float64(readyCount) / float64(totalCount), sync
   121  }
   122  
   123  // displayProgressBar displays the progress bar based on the installation progress.
   124  // Parameter:- progress: The installation progress percentage
   125  func displayProgressBar(progress float64) {
   126  	fmt.Print("[")
   127  	slides := int(progress * float64(constants.TotalWidth))
   128  	for i := 0; i < constants.TotalWidth; i++ {
   129  		if i < slides {
   130  			// Set color to green
   131  			fmt.Print("\033[32m#")
   132  		} else {
   133  			fmt.Print(" ")
   134  		}
   135  	}
   136  	// Reset color back to the default
   137  	fmt.Print("\033[0m")
   138  	fmt.Printf("] %.0f%%\r", progress*100)
   139  }