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

[vtctld do not merge] Custom build for vtctld development (cherry-picks on top of 9.0) #204

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions go/cmd/vtctldclient/cli/awk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
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 cli

import (
"fmt"
"sort"
"strings"
"time"

"vitess.io/vitess/go/vt/logutil"
"vitess.io/vitess/go/vt/topo"
"vitess.io/vitess/go/vt/topo/topoproto"

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

// MarshalMapAWK returns a string representation of a string->string map in an
// AWK-friendly format.
func MarshalMapAWK(m map[string]string) string {
pairs := make([]string, len(m))
i := 0

for k, v := range m {
pairs[i] = fmt.Sprintf("%v: %q", k, v)

i++
}

sort.Strings(pairs)

return "[" + strings.Join(pairs, " ") + "]"
}

// MarshalTabletAWK marshals a tablet into an AWK-friendly line.
func MarshalTabletAWK(t *topodatapb.Tablet) string {
ti := topo.TabletInfo{
Tablet: t,
}

keyspace := t.Keyspace
if keyspace == "" {
keyspace = "<null>"
}

shard := t.Shard
if shard == "" {
shard = "<null>"
}

mtst := "<null>"
// special case for old primary that hasn't been updated in the topo
// yet.
if t.MasterTermStartTime != nil && t.MasterTermStartTime.Seconds > 0 {
mtst = logutil.ProtoToTime(t.MasterTermStartTime).Format(time.RFC3339)
}

return fmt.Sprintf("%v %v %v %v %v %v %v %v", topoproto.TabletAliasString(t.Alias), keyspace, shard, topoproto.TabletTypeLString(t.Type), ti.Addr(), ti.MysqlAddr(), MarshalMapAWK(t.Tags), mtst)
}
32 changes: 32 additions & 0 deletions go/cmd/vtctldclient/cli/cobra.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
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 cli

import "github.com/spf13/cobra"

// FinishedParsing transitions a cobra.Command from treating RunE errors as
// usage errors to treating them just as normal runtime errors that should be
// propagated up to the root command's Execute method without also printing the
// subcommand's usage text on stderr. A subcommand should call this function
// from its RunE function when it has finished processing its flags and is
// moving into the pure "business logic" of its entrypoint.
//
// Package vitess.io/vitess/go/cmd/vtctldclient/internal/command has more
// details on why this exists.
func FinishedParsing(cmd *cobra.Command) {
cmd.SilenceUsage = true
}
61 changes: 61 additions & 0 deletions go/cmd/vtctldclient/cli/json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
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 cli

import (
"bytes"
"encoding/json"
"fmt"

"github.com/golang/protobuf/jsonpb"
"github.com/golang/protobuf/proto"
)

// MarshalJSON marshals obj to a JSON string. It uses the jsonpb marshaler for
// proto.Message types, with some sensible defaults, and falls back to the
// standard Go marshaler otherwise. In both cases, the marshaled JSON is
// indented with two spaces for readability.
//
// Unfortunately jsonpb only works for types that implement proto.Message,
// either by being a proto message type or by anonymously embedding one, so for
// other types that may have nested struct fields, we still use the standard Go
// marshaler, which will result in different formattings.
func MarshalJSON(obj interface{}) ([]byte, error) {
switch obj := obj.(type) {
case proto.Message:
b := bytes.NewBuffer(nil)
m := jsonpb.Marshaler{
EnumsAsInts: false,
EmitDefaults: true,
Indent: " ",
OrigName: true,
}

if err := m.Marshal(b, obj); err != nil {
return nil, fmt.Errorf("jsonpb.Marshal = %v", err)
}

return b.Bytes(), nil
default:
data, err := json.MarshalIndent(obj, "", " ")
if err != nil {
return nil, fmt.Errorf("json.Marshal = %v", err)
}

return data, nil
}
}
93 changes: 93 additions & 0 deletions go/cmd/vtctldclient/cli/pflag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
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 cli

import (
"github.com/spf13/pflag"

"vitess.io/vitess/go/flagutil"
"vitess.io/vitess/go/vt/key"
"vitess.io/vitess/go/vt/topo/topoproto"

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

// StringMapValue augments flagutil.StringMapValue so it can be used as a
// pflag.Value.
type StringMapValue struct {
flagutil.StringMapValue
}

// Type is part of the pflag.Value interface.
func (v *StringMapValue) Type() string {
return "cli.StringMapValue"
}

// KeyspaceIDTypeFlag adds the pflag.Value interface to a
// topodatapb.KeyspaceIdType.
type KeyspaceIDTypeFlag topodatapb.KeyspaceIdType

var _ pflag.Value = (*KeyspaceIDTypeFlag)(nil)

// Set is part of the pflag.Value interface.
func (v *KeyspaceIDTypeFlag) Set(arg string) error {
t, err := key.ParseKeyspaceIDType(arg)
if err != nil {
return err
}

*v = KeyspaceIDTypeFlag(t)

return nil
}

// String is part of the pflag.Value interface.
func (v *KeyspaceIDTypeFlag) String() string {
return key.KeyspaceIDTypeString(topodatapb.KeyspaceIdType(*v))
}

// Type is part of the pflag.Value interface.
func (v *KeyspaceIDTypeFlag) Type() string {
return "cli.KeyspaceIdTypeFlag"
}

// KeyspaceTypeFlag adds the pflag.Value interface to a topodatapb.KeyspaceType.
type KeyspaceTypeFlag topodatapb.KeyspaceType

var _ pflag.Value = (*KeyspaceTypeFlag)(nil)

// Set is part of the pflag.Value interface.
func (v *KeyspaceTypeFlag) Set(arg string) error {
kt, err := topoproto.ParseKeyspaceType(arg)
if err != nil {
return err
}

*v = KeyspaceTypeFlag(kt)

return nil
}

// String is part of the pflag.Value interface.
func (v *KeyspaceTypeFlag) String() string {
return topoproto.KeyspaceTypeString(topodatapb.KeyspaceType(*v))
}

// Type is part of the pflag.Value interface.
func (v *KeyspaceTypeFlag) Type() string {
return "cli.KeyspaceTypeFlag"
}
123 changes: 123 additions & 0 deletions go/cmd/vtctldclient/cli/shards.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
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 cli

import (
"sort"

"vitess.io/vitess/go/mysql"
"vitess.io/vitess/go/vt/topo/topoproto"

replicationdatapb "vitess.io/vitess/go/vt/proto/replicationdata"
topodatapb "vitess.io/vitess/go/vt/proto/topodata"
vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata"
)

// ParseKeyspaceShards takes a list of positional arguments and converts them to
// vtctldatapb.Shard objects.
func ParseKeyspaceShards(args []string) ([]*vtctldatapb.Shard, error) {
shards := make([]*vtctldatapb.Shard, 0, len(args))

for _, arg := range args {
keyspace, shard, err := topoproto.ParseKeyspaceShard(arg)
if err != nil {
return nil, err
}

shards = append(shards, &vtctldatapb.Shard{
Keyspace: keyspace,
Name: shard,
})
}

return shards, nil
}

// ReplicatingTablet is a struct to group a Tablet together with its replication
// Status.
type ReplicatingTablet struct {
*replicationdatapb.Status
*topodatapb.Tablet
}

type rTablets []*ReplicatingTablet

func (rts rTablets) Len() int { return len(rts) }
func (rts rTablets) Swap(i, j int) { rts[i], rts[j] = rts[j], rts[i] }
func (rts rTablets) Less(i, j int) bool {
l, r := rts[i], rts[j]

// l or r ReplicationStatus would be nil if we failed to get
// the position (put them at the beginning of the list)
if l.Status == nil {
return r.Status != nil
}

if r.Status == nil {
return false
}

// the type proto has MASTER first, so sort by that. Will show
// the MASTER first, then each replica type sorted by
// replication position.
if l.Tablet.Type < r.Tablet.Type {
return true
}

if l.Tablet.Type > r.Tablet.Type {
return false
}

// then compare replication positions
lpos, err := mysql.DecodePosition(l.Status.Position)
if err != nil {
return true
}

rpos, err := mysql.DecodePosition(r.Status.Position)
if err != nil {
return false
}

return !lpos.AtLeast(rpos)
}

// SortedReplicatingTablets returns a sorted list of replicating tablets (which
// is a struct grouping a Tablet together with its replication Status).
//
// The sorting order is:
// 1. Tablets that do not have a replication Status.
// 2. Any tablets of type MASTER.
// 3. Remaining tablets sorted by comparing replication positions.
func SortedReplicatingTablets(tabletMap map[string]*topodatapb.Tablet, replicationStatuses map[string]*replicationdatapb.Status) []*ReplicatingTablet {
rtablets := make([]*ReplicatingTablet, 0, len(tabletMap))

for alias, tablet := range tabletMap {
if status, ok := replicationStatuses[alias]; ok {
rtablets = append(rtablets, &ReplicatingTablet{
Status: status,
Tablet: tablet,
})
} else {
rtablets = append(rtablets, &ReplicatingTablet{Tablet: tablet})
}
}

sort.Sort(rTablets(rtablets))

return rtablets
}
Loading