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

Commit

Permalink
Merge pull request vitessio#8321 from tinyspeck/am_additional_backup_…
Browse files Browse the repository at this point in the history
…info

[vtctldserver] Add additional backup info fields
  • Loading branch information
ajm188 authored Jun 14, 2021
2 parents 5374002 + 411950f commit 83dfb9a
Show file tree
Hide file tree
Showing 16 changed files with 1,693 additions and 428 deletions.
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)
}
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
}
36 changes: 36 additions & 0 deletions go/vt/mysqlctl/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ import (
"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 @@ -138,6 +141,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
}
114 changes: 114 additions & 0 deletions go/vt/mysqlctl/mysqlctlproto/backup_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
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 mysqlctlproto

import (
"path"
"testing"
"time"

"vitess.io/vitess/go/protoutil"
"vitess.io/vitess/go/test/utils"
"vitess.io/vitess/go/vt/mysqlctl/backupstorage"

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

type backupHandle struct {
backupstorage.BackupHandle
name string
directory string
}

func (bh *backupHandle) Name() string { return bh.name }
func (bh *backupHandle) Directory() string { return bh.directory }
func (bh *backupHandle) testname() string { return path.Join(bh.directory, bh.name) }

func TestBackupHandleToProto(t *testing.T) {
t.Parallel()

now := time.Date(2021, time.June, 12, 15, 4, 5, 0, time.UTC)
tests := []struct {
bh *backupHandle
want *mysqlctlpb.BackupInfo
}{
{
bh: &backupHandle{
name: "2021-06-12.150405.zone1-100",
directory: "foo",
},
want: &mysqlctlpb.BackupInfo{
Name: "2021-06-12.150405.zone1-100",
Directory: "foo",
TabletAlias: &topodatapb.TabletAlias{
Cell: "zone1",
Uid: 100,
},
Time: protoutil.TimeToProto(now),
},
},
{
bh: &backupHandle{
name: "bar",
directory: "foo",
},
want: &mysqlctlpb.BackupInfo{
Name: "bar",
Directory: "foo",
},
},
{
bh: &backupHandle{
name: "invalid.time.zone1-100",
directory: "foo",
},
want: &mysqlctlpb.BackupInfo{
Name: "invalid.time.zone1-100",
Directory: "foo",
TabletAlias: &topodatapb.TabletAlias{
Cell: "zone1",
Uid: 100,
},
Time: nil,
},
},
{
bh: &backupHandle{
name: "2021-06-12.150405.not_an_alias",
directory: "foo",
},
want: &mysqlctlpb.BackupInfo{
Name: "2021-06-12.150405.not_an_alias",
Directory: "foo",
TabletAlias: nil,
Time: protoutil.TimeToProto(now),
},
},
}

for _, tt := range tests {
tt := tt

t.Run(tt.bh.testname(), func(t *testing.T) {
t.Parallel()

got := BackupHandleToProto(tt.bh)
utils.MustMatch(t, tt.want, got)
})
}
}
Loading

0 comments on commit 83dfb9a

Please sign in to comment.