Skip to content
This repository has been archived by the owner on Dec 16, 2022. It is now read-only.

Slack vitess 9 2021.06.07r13.vtctld #217

Merged
32 changes: 27 additions & 5 deletions go/cmd/vtctldclient/internal/command/backups.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,50 @@ import (
"github.com/spf13/cobra"

"vitess.io/vitess/go/cmd/vtctldclient/cli"
"vitess.io/vitess/go/vt/topo/topoproto"

vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata"
)

// GetBackups makes a GetBackups gRPC call to a vtctld.
var GetBackups = &cobra.Command{
Use: "GetBackups keyspace shard",
Args: cobra.ExactArgs(2),
Use: "GetBackups <keyspace/shard>",
Args: cobra.ExactArgs(1),
RunE: commandGetBackups,
}

var getBackupsOptions = struct {
Limit uint32
OutputJSON bool
}{}

func commandGetBackups(cmd *cobra.Command, args []string) error {
cli.FinishedParsing(cmd)
keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0))
if err != nil {
return err
}

keyspace := cmd.Flags().Arg(0)
shard := cmd.Flags().Arg(1)
cli.FinishedParsing(cmd)

resp, err := client.GetBackups(commandCtx, &vtctldatapb.GetBackupsRequest{
Keyspace: keyspace,
Shard: shard,
Limit: getBackupsOptions.Limit,
})
if err != nil {
return err
}

if getBackupsOptions.OutputJSON {
data, err := cli.MarshalJSON(resp)
if err != nil {
return err
}

fmt.Printf("%s\n", data)
return nil
}

names := make([]string, len(resp.Backups))
for i, b := range resp.Backups {
names[i] = b.Name
Expand All @@ -58,5 +78,7 @@ func commandGetBackups(cmd *cobra.Command, args []string) error {
}

func init() {
GetBackups.Flags().Uint32VarP(&getBackupsOptions.Limit, "limit", "l", 0, "Retrieve only the most recent N backups")
GetBackups.Flags().BoolVarP(&getBackupsOptions.OutputJSON, "json", "j", false, "Output backup info in JSON format rather than a list of backups")
Root.AddCommand(GetBackups)
}
52 changes: 46 additions & 6 deletions go/cmd/vtctldclient/internal/command/serving_graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,27 @@ import (
var (
// GetSrvKeyspaces makes a GetSrvKeyspaces gRPC call to a vtctld.
GetSrvKeyspaces = &cobra.Command{
Use: "GetSrvKeyspaces <keyspace> [<cell> ...]",
Args: cobra.MinimumNArgs(1),
RunE: commandGetSrvKeyspaces,
Use: "GetSrvKeyspaces <keyspace> [<cell> ...]",
Short: "Returns the SrvKeyspaces for the given keyspace in one or more cells.",
Args: cobra.MinimumNArgs(1),
RunE: commandGetSrvKeyspaces,
DisableFlagsInUseLine: true,
}
// GetSrvVSchema makes a GetSrvVSchema gRPC call to a vtctld.
GetSrvVSchema = &cobra.Command{
Use: "GetSrvVSchema cell",
Args: cobra.ExactArgs(1),
RunE: commandGetSrvVSchema,
Use: "GetSrvVSchema cell",
Short: "Returns the SrvVSchema for the given cell.",
Args: cobra.ExactArgs(1),
RunE: commandGetSrvVSchema,
DisableFlagsInUseLine: true,
}
// GetSrvVSchemas makes a GetSrvVSchemas gRPC call to a vtctld.
GetSrvVSchemas = &cobra.Command{
Use: "GetSrvVSchemas [<cell> ...]",
Short: "Returns the SrvVSchema for all cells, optionally filtered by the given cells.",
Args: cobra.ArbitraryArgs,
RunE: commandGetSrvVSchemas,
DisableFlagsInUseLine: true,
}
)

Expand Down Expand Up @@ -87,7 +99,35 @@ func commandGetSrvVSchema(cmd *cobra.Command, args []string) error {
return nil
}

func commandGetSrvVSchemas(cmd *cobra.Command, args []string) error {
cli.FinishedParsing(cmd)

cells := cmd.Flags().Args()[0:]

resp, err := client.GetSrvVSchemas(commandCtx, &vtctldatapb.GetSrvVSchemasRequest{
Cells: cells,
})
if err != nil {
return err
}

// By default, an empty array will serialize as `null`, but `[]` is a little nicer.
data := []byte("[]")

if len(resp.SrvVSchemas) > 0 {
data, err = cli.MarshalJSON(resp.SrvVSchemas)
if err != nil {
return err
}
}

fmt.Printf("%s\n", data)

return nil
}

func init() {
Root.AddCommand(GetSrvKeyspaces)
Root.AddCommand(GetSrvVSchema)
Root.AddCommand(GetSrvVSchemas)
}
44 changes: 44 additions & 0 deletions go/protoutil/time.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
Copyright 2021 The Vitess Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package protoutil

import (
"time"

"vitess.io/vitess/go/vt/proto/vttime"
)

// TimeFromProto converts a vttime.Time proto message into a time.Time object.
func TimeFromProto(tpb *vttime.Time) time.Time {
if tpb == nil {
return time.Time{}
}

return time.Unix(tpb.Seconds, int64(tpb.Nanoseconds))
}

// TimeToProto converts a time.Time object into a vttime.Time proto mesasge.
func TimeToProto(t time.Time) *vttime.Time {
secs, nanos := t.Unix(), t.UnixNano()

nsecs := secs * 1e9
extraNanos := nanos - nsecs
return &vttime.Time{
Seconds: secs,
Nanoseconds: int32(extraNanos),
}
}
52 changes: 52 additions & 0 deletions go/protoutil/time_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
Copyright 2021 The Vitess Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package protoutil

import (
"testing"
"time"

"github.com/stretchr/testify/assert"

"vitess.io/vitess/go/test/utils"
"vitess.io/vitess/go/vt/proto/vttime"
)

func TestTimeFromProto(t *testing.T) {
now := time.Date(2021, time.June, 12, 13, 14, 15, 0 /* nanos */, time.UTC)
vtt := TimeToProto(now)

utils.MustMatch(t, now, TimeFromProto(vtt))

vtt.Nanoseconds = 100
utils.MustMatch(t, now.Add(100*time.Nanosecond), TimeFromProto(vtt))

vtt.Nanoseconds = 1e9
utils.MustMatch(t, now.Add(time.Second), TimeFromProto(vtt))

assert.True(t, TimeFromProto(nil).IsZero(), "expected Go time from nil vttime to be Zero")
}

func TestTimeToProto(t *testing.T) {
now := time.Date(2021, time.June, 12, 13, 14, 15, 0 /* nanos */, time.UTC)
secs := now.Unix()
utils.MustMatch(t, &vttime.Time{Seconds: secs}, TimeToProto(now))

// Testing secs/nanos conversions
utils.MustMatch(t, &vttime.Time{Seconds: secs, Nanoseconds: 100}, TimeToProto(now.Add(100*time.Nanosecond)))
utils.MustMatch(t, &vttime.Time{Seconds: secs + 1}, TimeToProto(now.Add(1e9*time.Nanosecond))) // this should rollover to a full second
}
37 changes: 37 additions & 0 deletions go/vt/mysqlctl/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,18 @@ import (
"os"
"path/filepath"
"strings"
"time"

"context"

"vitess.io/vitess/go/mysql"
"vitess.io/vitess/go/vt/log"
"vitess.io/vitess/go/vt/mysqlctl/backupstorage"
"vitess.io/vitess/go/vt/proto/vtrpc"
"vitess.io/vitess/go/vt/topo/topoproto"
"vitess.io/vitess/go/vt/vterrors"

topodatapb "vitess.io/vitess/go/vt/proto/topodata"
)

// This file handles the backup and restore related code
Expand Down Expand Up @@ -132,6 +136,39 @@ func Backup(ctx context.Context, params BackupParams) error {
return finishErr
}

// ParseBackupName parses the backup name for a given dir/name, according to
// the format generated by mysqlctl.Backup. An error is returned only if the
// backup name does not have the expected number of parts; errors parsing the
// timestamp and tablet alias are logged, and a nil value is returned for those
// fields in case of error.
func ParseBackupName(dir string, name string) (backupTime *time.Time, alias *topodatapb.TabletAlias, err error) {
parts := strings.Split(name, ".")
if len(parts) != 3 {
return nil, nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "cannot backup name %s, expected <date>.<time>.<tablet_alias>", name)
}

// parts[0]: date part of BackupTimestampFormat
// parts[1]: time part of BackupTimestampFormat
// parts[2]: tablet alias
timestamp := strings.Join(parts[:2], ".")
aliasStr := parts[2]

btime, err := time.Parse(BackupTimestampFormat, timestamp)
if err != nil {
log.Errorf("error parsing backup time for %s/%s: %s", dir, name, err)
} else {
backupTime = &btime
}

alias, err = topoproto.ParseTabletAlias(aliasStr)
if err != nil {
log.Errorf("error parsing tablet alias for %s/%s: %s", dir, name, err)
alias = nil
}

return backupTime, alias, nil
}

// checkNoDB makes sure there is no user data already there.
// Used by Restore, as we do not want to destroy an existing DB.
// The user's database name must be given since we ignore all others.
Expand Down
17 changes: 16 additions & 1 deletion go/vt/mysqlctl/mysqlctlproto/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,30 @@ limitations under the License.
package mysqlctlproto

import (
"vitess.io/vitess/go/protoutil"
"vitess.io/vitess/go/vt/mysqlctl"
"vitess.io/vitess/go/vt/mysqlctl/backupstorage"

mysqlctlpb "vitess.io/vitess/go/vt/proto/mysqlctl"
)

// BackupHandleToProto returns a BackupInfo proto from a BackupHandle.
func BackupHandleToProto(bh backupstorage.BackupHandle) *mysqlctlpb.BackupInfo {
return &mysqlctlpb.BackupInfo{
bi := &mysqlctlpb.BackupInfo{
Name: bh.Name(),
Directory: bh.Directory(),
}

btime, alias, err := mysqlctl.ParseBackupName(bi.Directory, bi.Name)
if err != nil { // if bi.Name does not match expected format, don't parse any further fields
return bi
}

if btime != nil {
bi.Time = protoutil.TimeToProto(*btime)
}

bi.TabletAlias = alias

return bi
}
Loading