func readMetadata(client *gcs.GCSClient) (*backupMetadata, error) {
	metaObject, err := client.GetObject(bucketName, backuprestore.GetMetadataObjectName(name))
	if err != nil {
		return nil, err
	}
	r, err := metaObject.NewReader(context.Background())
	if err != nil {
		return nil, err
	}
	defer r.Close()
	m := &backupMetadata{}
	if err := json.NewDecoder(r).Decode(m); err != nil {
		return nil, err
	}
	return m, nil
}
readMetadata reads backup metadata from GCS.
package main
import (
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"
	log "github.com/sirupsen/logrus"
	"golang.org/x/net/context"
	"github.com/travelaudience/aerospike-operator/pkg/backuprestore"
	"github.com/travelaudience/aerospike-operator/pkg/backuprestore/gcs"
	flagutils "github.com/travelaudience/aerospike-operator/pkg/utils/flags"
)
const (
	backupCommand  = "backup"
	restoreCommand = "restore"
	debugFlag      = "debug"
	bucketNameFlag = "bucket-name"
	nameFlag       = "name"
	secretPathFlag = "secret-path"
	hostFlag       = "host"
	portFlag       = "port"
	namespaceFlag  = "namespace"
)
var (
	bfs *flag.FlagSet
	rfs *flag.FlagSet
	debug      bool
	bucketName string
	name       string
	secretPath string
	host       string
	port       int
	namespace  string
)
type backupMetadata struct {
	Namespace string `json:"namespace"`
}
func init() {
	bfs = flag.NewFlagSet(backupCommand, flag.ExitOnError)
	bfs.BoolVar(&debug, debugFlag, false, "[DEPRECATED] whether to enable debug logging")
	bfs.StringVar(&bucketName, bucketNameFlag, "", "the name of the bucket to upload the backup to")
	bfs.StringVar(&name, nameFlag, "", "the name of the backup file to be stored on GCS")
	bfs.StringVar(&secretPath, secretPathFlag, "/secret/key.json", "the path to the service account credentials file")
	bfs.StringVar(&host, hostFlag, "", "the host to which asbackup will connect")
	bfs.IntVar(&port, portFlag, 3000, "the port to which asbackup will connect")
	bfs.StringVar(&namespace, namespaceFlag, "", "the name of the namespace which to backup")
	rfs = flag.NewFlagSet(restoreCommand, flag.ExitOnError)
	rfs.BoolVar(&debug, debugFlag, false, "[DEPRECATED] whether to enable debug logging")
	rfs.StringVar(&bucketName, bucketNameFlag, "", "the name of the bucket to download the backup from")
	rfs.StringVar(&name, nameFlag, "", "the name of the backup file to be retrieved from GCS")
	rfs.StringVar(&secretPath, secretPathFlag, "/secret/key.json", "the path to the service account credentials file")
	rfs.StringVar(&host, hostFlag, "", "the host to which asrestore will connect")
	rfs.IntVar(&port, portFlag, 3000, "the port to which asrestore will connect")
	rfs.StringVar(&namespace, namespaceFlag, "", "the name of the namespace which to restore data into")
}
func main() {
	if len(os.Args) == 1 {
		log.Fatalf("too few arguments")
	}
	switch os.Args[1] {
	case backupCommand:
		bfs.Parse(os.Args[2:])
		flagutils.DeprecateFlags(bfs, debugFlag)
		if debug {
			log.SetLevel(log.DebugLevel)
		}
		log.Info("backup is starting")
		if err := doBackup(); err != nil {
			log.Fatal(err)
		}
		log.Info("backup is complete")
	case restoreCommand:
		rfs.Parse(os.Args[2:])
		flagutils.DeprecateFlags(rfs, debugFlag)
		if debug {
			log.SetLevel(log.DebugLevel)
		}
		log.Info("restore is starting")
		if err := doRestore(); err != nil {
			log.Fatal(err)
		}
		log.Info("restore is complete")
	default:
		log.Fatalf("invalid command %q", os.Args[1])
	}
}
func doBackup() error {
	log.Debug("initing cloud storage")
	client, err := gcs.NewGCSClientFromCredentials(secretPath)
	if err != nil {
		return err
	}
	defer client.Close()
	log.Debug("dumping metadata")
	if err := dumpMetadata(client); err != nil {
		return err
	}
	cmd := exec.Command("asbackup", "-h", host, "-p", strconv.Itoa(port), "-n", namespace, "-o", "-", "-c", "-v")
	o, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}
	errw := log.New().Writer()
	defer errw.Close()
	cmd.Stderr = errw
	log.Debug("==== asbackup ====")
	log.Debug(strings.Join(cmd.Args, " "))
	log.Debug("==================")
	log.Debug("running asbackup and streaming to cloud storage")
	if err := cmd.Start(); err != nil {
		return err
	}
	if err := client.TransferToGCS(o, bucketName, backuprestore.GetBackupObjectName(name)); err != nil {
		return err
	}
	return cmd.Wait()
}
func doRestore() error {
	log.Debug("initing cloud storage")
	client, err := gcs.NewGCSClientFromCredentials(secretPath)
	if err != nil {
		return err
	}
	defer client.Close()
	log.Debug("reading metadata")
	n, err := readMetadata(client)
	if err != nil {
		return err
	}
	cmd := exec.Command("asrestore", "-h", host, "-p", strconv.Itoa(port), "-i", "-", "-n", fmt.Sprintf("%s,%s", n.Namespace, namespace), "-v")
	i, err := cmd.StdinPipe()
	errw := log.New().Writer()
	defer errw.Close()
	cmd.Stderr = errw
	log.Debug("==== asrestore ====")
	log.Debug(strings.Join(cmd.Args, " "))
	log.Debug("===================")
	log.Debug("running asrestore and streaming from cloud storage")
	if err := cmd.Start(); err != nil {
		return err
	}
	if err := client.TransferFromGCS(i, bucketName, backuprestore.GetBackupObjectName(name)); err != nil {
		return err
	}
	if err := i.Close(); err != nil {
		return err
	}
	return cmd.Wait()
}
func dumpMetadata(client *gcs.GCSClient) error {
	metaObject, err := client.GetObject(bucketName, backuprestore.GetMetadataObjectName(name))
	if err != nil {
		return err
	}
	w := metaObject.NewWriter(context.Background())
	defer w.Close()
	m := &backupMetadata{Namespace: namespace}
	if err := json.NewEncoder(w).Encode(m); err != nil {
		return err
	}
	return nil
}