github.com/verrazzano/verrazzano-monitoring-operator@v0.0.30/verrazzano-backup-hook/main.go (about)

     1  // Copyright (c) 2022, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package main
     5  
     6  import (
     7  	"flag"
     8  	"fmt"
     9  	"github.com/verrazzano/verrazzano-monitoring-operator/verrazzano-backup-hook/constants"
    10  	"github.com/verrazzano/verrazzano-monitoring-operator/verrazzano-backup-hook/log"
    11  	"github.com/verrazzano/verrazzano-monitoring-operator/verrazzano-backup-hook/opensearch"
    12  	model "github.com/verrazzano/verrazzano-monitoring-operator/verrazzano-backup-hook/types"
    13  	futil "github.com/verrazzano/verrazzano-monitoring-operator/verrazzano-backup-hook/utilities"
    14  	kutil "github.com/verrazzano/verrazzano-monitoring-operator/verrazzano-backup-hook/utilities/k8s"
    15  	"go.uber.org/zap"
    16  	"k8s.io/client-go/dynamic"
    17  	"k8s.io/client-go/kubernetes"
    18  	"k8s.io/client-go/rest"
    19  	"net/http"
    20  	"os"
    21  	ctrl "sigs.k8s.io/controller-runtime"
    22  	"sigs.k8s.io/controller-runtime/pkg/client"
    23  	kzap "sigs.k8s.io/controller-runtime/pkg/log/zap"
    24  	"strings"
    25  )
    26  
    27  var (
    28  	VeleroBackupName string
    29  	Component        string
    30  	Operation        string
    31  	Profile          string
    32  	VeleroNamespace  string
    33  )
    34  
    35  func main() {
    36  	flag.StringVar(&VeleroBackupName, "velero-backup-name", "", "The Velero-backup-name associated with this operation.")
    37  	flag.StringVar(&Component, "component", "opensearch", "The Verrazzano component to be backed up or restored (Default = opensearch).")
    38  	flag.StringVar(&Operation, "operation", "", "Operation must be one of 'backup' or 'restore'.")
    39  	flag.StringVar(&Profile, "profile", "default", "Object store credentials profile.")
    40  	flag.StringVar(&VeleroNamespace, "namespace", "verrazzano-backup", "Namespace where Velero component is deployed.")
    41  
    42  	// Add the zap logger flag set to the CLI.
    43  	opts := kzap.Options{}
    44  	opts.BindFlags(flag.CommandLine)
    45  
    46  	flag.Parse()
    47  	kzap.UseFlagOptions(&opts)
    48  
    49  	// Flag validations
    50  	if Operation == "" {
    51  		fmt.Printf("Operation cannot be empty . It has to be 'backup/restore\n")
    52  		os.Exit(1)
    53  	}
    54  	if Operation != constants.BackupOperation && Operation != constants.RestoreOperation {
    55  		fmt.Printf("Operation has to be 'backup/restore\n")
    56  		os.Exit(1)
    57  	}
    58  	if VeleroBackupName == "" {
    59  		fmt.Printf("VeleroBackupName must refer to an existing Velero backup.\n")
    60  		os.Exit(1)
    61  	}
    62  
    63  	// Initialize the zap log
    64  	file, err := os.CreateTemp(os.TempDir(), fmt.Sprintf("verrazzano-%s-hook-*.log", strings.ToLower(Operation)))
    65  	if err != nil {
    66  		fmt.Printf("Unable to create temp file")
    67  		os.Exit(1)
    68  	}
    69  	defer file.Close()
    70  	log, err := log.Logger(file.Name())
    71  	if err != nil {
    72  		fmt.Printf("Unable to fetch logger")
    73  		os.Exit(1)
    74  	}
    75  	log.Info("Verrazzano backup and restore helper invoked.")
    76  
    77  	// Gathering k8s clients
    78  	done := false
    79  	retryCount := 0
    80  	k8sContextReady := true
    81  	var config *rest.Config
    82  	var kubeClientInterface *kubernetes.Clientset
    83  	var kubeClient client.Client
    84  	var dynamicKubeClientInterface dynamic.Interface
    85  
    86  	globalTimeout := futil.GetEnvWithDefault(constants.OpenSearchHealthCheckTimeoutKey, constants.OpenSearchHealthCheckTimeoutDefaultValue)
    87  	var checkConData model.ConnectionData
    88  	checkConData.VeleroTimeout = globalTimeout
    89  	// Initialize Opensearch object
    90  	search := opensearch.New(constants.OpenSearchURL, globalTimeout, http.DefaultClient, &checkConData, log)
    91  	// Check OpenSearch health before proceeding with backup or restore
    92  	err = search.EnsureOpenSearchIsHealthy()
    93  	if err != nil {
    94  		log.Errorf("Operation cannot be performed as OpenSearch is not healthy")
    95  		os.Exit(1)
    96  	}
    97  
    98  	// Feedback loop to gather k8s context
    99  	for !done {
   100  		config, err = ctrl.GetConfig()
   101  		if err != nil {
   102  			log.Errorf("Failed to get kubeconfig: %v", err)
   103  			k8sContextReady = false
   104  		}
   105  		kubeClientInterface, err = kubernetes.NewForConfig(config)
   106  		if err != nil {
   107  			log.Errorf("Failed to get clientset: %v", err)
   108  			k8sContextReady = false
   109  		}
   110  		kubeClient, err = client.New(config, client.Options{})
   111  		if err != nil {
   112  			log.Errorf("Failed to get controller-runtime client: %v", err)
   113  			k8sContextReady = false
   114  		}
   115  		dynamicKubeClientInterface, err = dynamic.NewForConfig(config)
   116  		if err != nil {
   117  			log.Errorf("Failed to get dynamic client: %v", err)
   118  			k8sContextReady = false
   119  		}
   120  
   121  		if !k8sContextReady {
   122  			if retryCount <= constants.RetryCount {
   123  				message := "Unable to get context"
   124  				_, err := futil.WaitRandom(message, globalTimeout, log)
   125  				if err != nil {
   126  					log.Panic(err)
   127  				}
   128  				retryCount = retryCount + 1
   129  				// setting k8sContextReady flag to true os that subsequent checks dont re-use old value
   130  				// This flag will be changed to false if any k8s go-client retrieval fails.
   131  				log.Info("Resetting k8s context flag to true...")
   132  				k8sContextReady = true
   133  			}
   134  		} else {
   135  			done = true
   136  			log.Info("kubecontext retrieval successful")
   137  		}
   138  	}
   139  
   140  	// Initialize K8s object
   141  	k8s := kutil.New(dynamicKubeClientInterface, kubeClient, kubeClientInterface, config, Profile, log)
   142  
   143  	// Get S3 access details from Velero Backup Storage location associated with Backup given as input
   144  	// Ensure the Backup Storage Location is NOT default
   145  	openSearchConData, err := k8s.PopulateConnData(VeleroNamespace, VeleroBackupName)
   146  	if err != nil {
   147  		log.Errorf("Unable to fetch secret: %v", err)
   148  		os.Exit(1)
   149  	}
   150  
   151  	// Update OpenSearch keystore
   152  	_, err = k8s.UpdateKeystore(openSearchConData, globalTimeout)
   153  	if err != nil {
   154  		log.Errorf("Unable to update keystore")
   155  		os.Exit(1)
   156  	}
   157  
   158  	openSearch := opensearch.New(constants.OpenSearchURL, globalTimeout, http.DefaultClient, openSearchConData, log)
   159  	err = search.ReloadOpensearchSecureSettings()
   160  	if err != nil {
   161  		log.Errorf("Unable to reload security settings")
   162  		os.Exit(1)
   163  	}
   164  
   165  	switch strings.ToLower(Operation) {
   166  	// OpenSearch backup handling
   167  	case constants.BackupOperation:
   168  		log.Info("Commencing opensearch backup ..")
   169  		err = openSearch.Backup()
   170  		if err != nil {
   171  			log.Errorf("Operation '%s' unsuccessfull due to %v", Operation, zap.Error(err))
   172  			os.Exit(1)
   173  		}
   174  		log.Infof("%s backup was successfull", strings.ToTitle(Component))
   175  
   176  	case constants.RestoreOperation:
   177  		// OpenSearch restore handling
   178  		log.Infof("Commencing OpenSearch restore ..")
   179  		err = k8s.ScaleDeployment(constants.VMOLabelSelector, constants.VerrazzanoSystemNamespace, constants.VMODeploymentName, int32(0))
   180  		if err != nil {
   181  			log.Errorf("Unable to scale deployment '%s' due to %v", constants.VMODeploymentName, zap.Error(err))
   182  			os.Exit(1)
   183  		}
   184  		err = k8s.ScaleDeployment(constants.IngestLabelSelector, constants.VerrazzanoSystemNamespace, constants.IngestDeploymentName, int32(0))
   185  		if err != nil {
   186  			log.Errorf("Unable to scale deployment '%s' due to %v", constants.IngestDeploymentName, zap.Error(err))
   187  			os.Exit(1)
   188  		}
   189  		err = openSearch.Restore()
   190  		if err != nil {
   191  			log.Errorf("Operation '%s' unsuccessfull due to %v", Operation, zap.Error(err))
   192  			os.Exit(1)
   193  		}
   194  
   195  		ok, err := k8s.CheckDeployment(constants.KibanaDeploymentLabelSelector, constants.VerrazzanoSystemNamespace)
   196  		if err != nil {
   197  			log.Errorf("Unable to detect Kibana deployment '%s' due to %v", constants.KibanaDeploymentLabelSelector, zap.Error(err))
   198  			os.Exit(1)
   199  		}
   200  		// If kibana is deployed then scale it down
   201  		if ok {
   202  			err = k8s.ScaleDeployment(constants.KibanaLabelSelector, constants.VerrazzanoSystemNamespace, constants.KibanaDeploymentName, int32(0))
   203  			if err != nil {
   204  				log.Errorf("Unable to scale deployment '%s' due to %v", constants.IngestDeploymentName, zap.Error(err))
   205  			}
   206  		}
   207  		err = k8s.ScaleDeployment(constants.VMOLabelSelector, constants.VerrazzanoSystemNamespace, constants.VMODeploymentName, int32(1))
   208  		if err != nil {
   209  			log.Errorf("Unable to scale deployment '%s' due to %v", constants.VMODeploymentName, zap.Error(err))
   210  		}
   211  
   212  		err = k8s.CheckAllPodsAfterRestore()
   213  		if err != nil {
   214  			log.Errorf("Unable to check deployments after restoring Verrazzano Monitoring Operator %v", zap.Error(err))
   215  		}
   216  
   217  		log.Infof("%s restore was successfull", strings.ToTitle(Component))
   218  
   219  	}
   220  
   221  }