cmd/hub/api/backup.go (234 lines of code) (raw):
// Copyright (c) 2022 EPAM Systems, Inc.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//go:build api
package api
import (
"encoding/json"
"fmt"
"log"
"net/url"
"os"
"strings"
"time"
"github.com/epam/hubctl/cmd/hub/config"
"github.com/epam/hubctl/cmd/hub/util"
)
const backupsResource = "hub/api/v1/backups"
func Backups(selector string, showLogs, jsonFormat bool) {
backups, err := backupsBy(selector)
if err != nil {
log.Fatalf("Unable to query for Backups(s): %v", err)
}
if len(backups) == 0 {
if jsonFormat {
log.Print("No Backups")
} else {
fmt.Print("No Backups\n")
}
} else {
if jsonFormat {
var toMarshal interface{}
if len(backups) == 1 {
toMarshal = &backups[0]
} else {
toMarshal = backups
}
out, err := json.MarshalIndent(toMarshal, "", " ")
if err != nil {
log.Fatalf("Error marshalling JSON response for output: %v", err)
}
os.Stdout.Write(out)
os.Stdout.Write([]byte("\n"))
} else {
fmt.Print("Backups:\n")
errors := make([]error, 0)
for _, backup := range backups {
errors = formatBackupEntity(&backup, showLogs, errors)
}
if len(errors) > 0 {
fmt.Print("Errors encountered:\n")
for _, err := range errors {
fmt.Printf("\t%v\n", err)
}
}
}
}
}
func formatBackupEntity(backup *Backup, showLogs bool, errors []error) []error {
title := formatBackupTitle(backup)
if backup.Description != "" {
title = fmt.Sprintf("%s - %s", title, backup.Description)
}
fmt.Printf("\n\t%s @ %v\n", title, backup.Timestamp.Truncate(time.Second))
if len(backup.Tags) > 0 {
fmt.Printf("\t\tTags: %s\n", strings.Join(backup.Tags, ", "))
}
fmt.Printf("\t\tStatus: %s\n", backup.Status)
if len(backup.Components) > 0 {
fmt.Printf("\t\tComponents: %s\n", strings.Join(backup.Components, ", "))
}
if backup.Environment.Name != "" {
fmt.Printf("\t\tEnvironment: %s\n", formatEnvironmentRef(&backup.Environment))
}
if backup.StackInstance.Name != "" {
resource := fmt.Sprintf("%s/%s", backupsResource, backup.Id)
instance, errs := formatStackInstanceRef(&backup.StackInstance, resource)
fmt.Printf("\t\tStack Instance: %s\n", instance)
errors = append(errors, errs...)
}
if backup.Bundle.Kind != "" {
bundle := backup.Bundle
fmt.Printf("\t\tBundle: %s @ %v\n", bundle.Kind, bundle.Timestamp.Truncate(time.Second))
fmt.Printf("\t\t\tStatus: %s\n", bundle.Status)
if len(bundle.Components) > 0 {
fmt.Print("\t\t\tComponents:\n")
for name, comp := range bundle.Components {
fmt.Printf("\t\t\t\t%s [%s] @ %v\n", name, comp.Kind, comp.Timestamp.Truncate(time.Second))
fmt.Printf("\t\t\t\tStatus: %s\n", comp.Status)
if len(comp.Outputs) > 0 {
fmt.Print("\t\t\t\tOutputs:\n")
for _, output := range comp.Outputs {
fmt.Printf("\t\t\t\t\t%s: %s\n", output.Name, output.Value)
}
}
}
}
}
if showLogs && backup.Logs != "" {
fmt.Printf("\t\tLogs:\n\t\t\t%s\n", strings.Join(strings.Split(backup.Logs, "\n"), "\n\t\t\t"))
}
return errors
}
func formatBackup(backup *Backup) {
errors := formatBackupEntity(backup, false, make([]error, 0))
if len(errors) > 0 {
fmt.Print("Errors encountered formatting response:\n")
for _, err := range errors {
fmt.Printf("\t%v\n", err)
}
}
}
func showBackup(id string) {
backup, err := backupById(id)
if err != nil {
fmt.Printf("%v", err)
}
if backup != nil {
formatBackup(backup)
}
}
func backupBy(selector string) (*Backup, error) {
if !util.IsUint(selector) {
return backupByName(selector)
}
return backupById(selector)
}
func backupsBy(selector string) ([]Backup, error) {
if !util.IsUint(selector) {
return backupsByName(selector)
}
backup, err := backupById(selector)
if err != nil {
return nil, err
}
if backup != nil {
return []Backup{*backup}, nil
}
return nil, nil
}
func backupById(id string) (*Backup, error) {
path := fmt.Sprintf("%s/%s", backupsResource, url.PathEscape(id))
var jsResp Backup
code, err := get(hubApi(), path, &jsResp)
if code == 404 {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("Error querying HubCTL Backups: %v", err)
}
if code != 200 {
return nil, fmt.Errorf("Got %d HTTP querying HubCTL Backups, expected 200 HTTP", code)
}
return &jsResp, nil
}
func backupByName(name string) (*Backup, error) {
backups, err := backupsByName(name)
if err != nil {
return nil, fmt.Errorf("Unable to query for Backup `%s`: %v", name, err)
}
if len(backups) == 0 {
return nil, fmt.Errorf("No Backup `%s` found", name)
}
if len(backups) > 1 {
return nil, fmt.Errorf("More than one Backup returned by name `%s`", name)
}
backup := backups[0]
return &backup, nil
}
func backupsByName(name string) ([]Backup, error) {
return backupsByFilter("name", name)
}
func backupsByEnvironmentId(id string) ([]Backup, error) {
return backupsByFilter("environment", id)
}
func backupsByInstanceId(id string) ([]Backup, error) {
return backupsByFilter("instance", id)
}
func backupsByPlatformId(id string) ([]Backup, error) {
return backupsByFilter("platform", id)
}
func backupsByFilter(field, value string) ([]Backup, error) {
path := backupsResource
if field != "" && value != "" {
path += fmt.Sprintf("?%s=%s", url.QueryEscape(field), url.QueryEscape(value))
}
var jsResp []Backup
code, err := get(hubApi(), path, &jsResp)
if code == 404 {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("Error querying HubCTL Backups: %v", err)
}
if code != 200 {
return nil, fmt.Errorf("Got %d HTTP querying HubCTL Backups, expected 200 HTTP", code)
}
return jsResp, nil
}
func formatBackupTitle(backup *Backup) string {
return fmt.Sprintf("%s [%s]", backup.Name, backup.Id)
}
func DeleteBackup(selector string) {
err := deleteBackup(selector)
if err != nil {
log.Fatalf("Unable to delete HubCTL Backup: %v", err)
}
}
func deleteBackup(selector string) error {
backup, err := backupBy(selector)
id := ""
if err != nil {
str := err.Error()
if util.IsUint(selector) &&
(strings.Contains(str, "json: cannot unmarshal") || strings.Contains(str, "cannot parse") || config.Force) {
util.Warn("%v", err)
id = selector
} else {
return err
}
} else if backup == nil {
return error404
} else {
id = backup.Id
}
force := ""
if config.Force {
force = "?force=true"
}
path := fmt.Sprintf("%s/%s%s", backupsResource, url.PathEscape(id), force)
code, err := delete(hubApi(), path)
if err != nil {
return err
}
if code != 202 && code != 204 {
return fmt.Errorf("Got %d HTTP deleting HubCTL Backup, expected [202, 204] HTTP", code)
}
return nil
}