Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix test
Browse files Browse the repository at this point in the history
Signed-off-by: husharp <jinhao.hu@pingcap.com>
HuSharp committed Dec 7, 2023
1 parent 4eac0ef commit cd38ad6
Showing 4 changed files with 228 additions and 5 deletions.
21 changes: 17 additions & 4 deletions client/http/api.go
Original file line number Diff line number Diff line change
@@ -38,11 +38,17 @@ const (
store = "/pd/api/v1/store"
Stores = "/pd/api/v1/stores"
StatsRegion = "/pd/api/v1/stats/region"
LeaderPrefix = "/pd/api/v1/leader"
leaderTransferPrefix = "/pd/api/v1/leader/transfer"
Cluster = "/pd/api/v1/cluster"
Health = "/pd/api/v1/health"
Members = "/pd/api/v1/members"
// Config
Config = "/pd/api/v1/config"
ClusterVersion = "/pd/api/v1/config/cluster-version"
ScheduleConfig = "/pd/api/v1/config/schedule"
ReplicateConfig = "/pd/api/v1/config/replicate"
Config = "/pd/api/v1/config"
ClusterVersion = "/pd/api/v1/config/cluster-version"
ScheduleConfig = "/pd/api/v1/config/schedule"
ReplicateConfig = "/pd/api/v1/config/replicate"
evictLeaderSchedulerConfigPrefix = "/pd/api/v1/scheduler-config/evict-leader-scheduler/list"
// Rule
PlacementRule = "/pd/api/v1/config/rule"
PlacementRules = "/pd/api/v1/config/rules"
@@ -72,8 +78,15 @@ const (
MinResolvedTSPrefix = "/pd/api/v1/min-resolved-ts"
Status = "/pd/api/v1/status"
Version = "/pd/api/v1/version"

autoscalingPrefix = "autoscaling"
)

// TransferLeader transfers leader from a source store to a target store.
func TransferLeader(memberName string) string {
return fmt.Sprintf("%s/%s", leaderTransferPrefix, memberName)
}

// RegionByID returns the path of PD HTTP API to get region by ID.
func RegionByID(regionID uint64) string {
return fmt.Sprintf("%s/%d", RegionByIDPrefix, regionID)
108 changes: 107 additions & 1 deletion client/http/client.go
Original file line number Diff line number Diff line change
@@ -20,6 +20,8 @@ import (
"crypto/tls"
"encoding/json"
"fmt"
"github.com/pingcap/kvproto/pkg/metapb"
"github.com/pingcap/kvproto/pkg/pdpb"
"io"
"net/http"
"strings"
@@ -79,6 +81,15 @@ type Client interface {
/* Other interfaces */
GetMinResolvedTSByStoresIDs(context.Context, []uint64) (uint64, map[uint64]uint64, error)

GetHealth(context.Context) (*HealthInfo, error)
GetMembers(context.Context) (*MembersInfo, error)
GetCluster(context.Context) (*metapb.Cluster, error)
GetPDLeader(context.Context) (*pdpb.Member, error)
GetAutoscalingPlans(context.Context, Strategy) ([]Plan, error)
GetEvictLeaderSchedulerConfig(context.Context) (*evictLeaderSchedulerConfig, error)
UpdateReplicationConfig(context.Context, ReplicationConfig) error
TransferPDLeader(context.Context, string) error

/* Client-related methods */
// WithCallerID sets and returns a new client with the given caller ID.
WithCallerID(string) Client
@@ -220,7 +231,8 @@ func (c *client) execDuration(name string, duration time.Duration) {

// Header key definition constants.
const (
pdAllowFollowerHandleKey = "PD-Allow-Follower-Handle"
pdAllowFollowerHandleKey = "PD-Allow-Follower-Handle" // #nosec G101
contentTypeKey = "Content-Type"
componentSignatureKey = "component"
)

@@ -234,6 +246,13 @@ func WithAllowFollowerHandle() HeaderOption {
}
}

// WithHeaderContentTypeJSON sets the header field to indicate the content type is JSON.
func WithHeaderContentTypeJSON() HeaderOption {
return func(header http.Header) {
header.Set(contentTypeKey, "application/json")
}
}

// At present, we will use the retry strategy of polling by default to keep
// it consistent with the current implementation of some clients (e.g. TiDB).
func (c *client) requestWithRetry(
@@ -340,6 +359,93 @@ func (c *client) GetRegionByID(ctx context.Context, regionID uint64) (*RegionInf
return &region, nil
}

func (c *client) GetHealth(ctx context.Context) (*HealthInfo, error) {
var healths []MemberHealth
err := c.requestWithRetry(ctx,
"GetHealth", Health,
http.MethodGet, http.NoBody, &healths)
if err != nil {
return nil, err
}
return &HealthInfo{Healths: healths}, nil
}

func (c *client) GetMembers(ctx context.Context) (*MembersInfo, error) {
members := &MembersInfo{}
err := c.requestWithRetry(ctx,
"GetMembers", Members,
http.MethodGet, http.NoBody, &members)
if err != nil {
return nil, err
}
return members, nil
}

func (c *client) GetCluster(ctx context.Context) (*metapb.Cluster, error) {
cluster := &metapb.Cluster{}
err := c.requestWithRetry(ctx,
"GetCluster", Cluster,
http.MethodGet, http.NoBody, &cluster)
if err != nil {
return nil, err
}

return cluster, nil
}

func (c *client) GetPDLeader(ctx context.Context) (*pdpb.Member, error) {
leader := &pdpb.Member{}
err := c.requestWithRetry(ctx,
"GetPDLeader", LeaderPrefix,
http.MethodGet, http.NoBody, &leader)
if err != nil {
return nil, err
}
return leader, nil
}

func (c *client) TransferPDLeader(ctx context.Context, memberName string) error {
return c.requestWithRetry(ctx,
"TransferPDLeader", TransferLeader(memberName),
http.MethodPost, http.NoBody, nil)
}

func (c *client) UpdateReplicationConfig(ctx context.Context, data ReplicationConfig) error {
reqData, err := json.Marshal(data)
if err != nil {
return errors.Trace(err)
}
return c.requestWithRetry(ctx,
"UpdateReplicationConfig", ReplicateConfig,
http.MethodPost, bytes.NewBuffer(reqData), nil, WithHeaderContentTypeJSON())
}

func (c *client) GetEvictLeaderSchedulerConfig(ctx context.Context) (*EvictLeaderSchedulerConfig, error) {
var config EvictLeaderSchedulerConfig
err := c.requestWithRetry(ctx,
"GetEvictLeaderSchedulerConfig", evictLeaderSchedulerConfigPrefix,
http.MethodGet, http.NoBody, &config)
if err != nil {
return nil, err
}
return &config, nil
}

func (c *client) GetAutoscalingPlans(ctx context.Context, strategy Strategy) ([]Plan, error) {
data, err := json.Marshal(strategy)
if err != nil {
return nil, errors.Trace(err)
}
var plans []Plan
err = c.requestWithRetry(ctx,
"GetAutoscalingPlans", autoscalingPrefix,
http.MethodPost, bytes.NewBuffer(data), plans)
if err != nil {
return nil, err
}
return plans, nil
}

// GetRegionByKey gets the region info by key.
func (c *client) GetRegionByKey(ctx context.Context, key []byte) (*RegionInfo, error) {
var region RegionInfo
95 changes: 95 additions & 0 deletions client/http/types.go
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ package http
import (
"encoding/hex"
"encoding/json"
"github.com/pingcap/kvproto/pkg/pdpb"
"net/url"
"time"

@@ -574,3 +575,97 @@ type LabelRulePatch struct {
SetRules []*LabelRule `json:"sets"`
DeleteRules []string `json:"deletes"`
}

// following struct definitions are copied from github.com/pingcap/pd/server/api/store
// these are not exported by that package

// HealthInfo define PD's healthy info
type HealthInfo struct {
Healths []MemberHealth
}

// MemberHealth define a pd member's healthy info
type MemberHealth struct {
Name string `json:"name"`
MemberID uint64 `json:"member_id"`
ClientUrls []string `json:"client_urls"`
Health bool `json:"health"`
}

// MembersInfo is PD members info returned from PD RESTful interface
// type Members map[string][]*pdpb.Member
type MembersInfo struct {
Header *pdpb.ResponseHeader `json:"header,omitempty"`
Members []*pdpb.Member `json:"members,omitempty"`
Leader *pdpb.Member `json:"leader,omitempty"`
EtcdLeader *pdpb.Member `json:"etcd_leader,omitempty"`
}

// EvictLeaderSchedulerConfig holds configuration for evict leader
// https://github.com/pingcap/pd/blob/b21855a3aeb787c71b0819743059e432be217dcd/server/schedulers/evict_leader.go#L81-L86
// note that we use `interface{}` as the type of value because we don't care
// about the value for now
type EvictLeaderSchedulerConfig struct {
StoreIDWithRanges map[uint64]interface{} `json:"store-id-ranges"`
}

// Strategy within an HTTP request provides rules and resources to help make decision for auto scaling.
type Strategy struct {
Rules []*Rule `json:"rules"`
Resources []*Resource `json:"resources"`
}

// Resource represents a kind of resource set including CPU, memory, storage.
type Resource struct {
ResourceType string `json:"resource_type"`
// The basic unit of CPU is milli-core.
CPU uint64 `json:"cpu"`
// The basic unit of memory is byte.
Memory uint64 `json:"memory"`
// The basic unit of storage is byte.
Storage uint64 `json:"storage"`
// If count is not set, it indicates no limit.
Count *uint64 `json:"count,omitempty"`
}

// Plan is the final result of auto-scaling, which indicates how to scale in or scale out.
type Plan struct {
Component string `json:"component"`
Count uint64 `json:"count"`
ResourceType string `json:"resource_type"`
Labels map[string]string `json:"labels"`
}

// ReplicationConfig is the replication configuration.
// NOTE: This type is exported by HTTP API. Please pay more attention when modifying it.
type ReplicationConfig struct {
// MaxReplicas is the number of replicas for each region.
MaxReplicas uint64 `toml:"max-replicas" json:"max-replicas"`

// The label keys specified the location of a store.
// The placement priorities is implied by the order of label keys.
// For example, ["zone", "rack"] means that we should place replicas to
// different zones first, then to different racks if we don't have enough zones.
LocationLabels StringSlice `toml:"location-labels" json:"location-labels"`
// StrictlyMatchLabel strictly checks if the label of TiKV is matched with LocationLabels.
StrictlyMatchLabel bool `toml:"strictly-match-label" json:"strictly-match-label,string"`

// When PlacementRules feature is enabled. MaxReplicas, LocationLabels and IsolationLabels are not used any more.
EnablePlacementRules bool `toml:"enable-placement-rules" json:"enable-placement-rules,string"`

// EnablePlacementRuleCache controls whether use cache during rule checker
EnablePlacementRulesCache bool `toml:"enable-placement-rules-cache" json:"enable-placement-rules-cache,string"`

// IsolationLevel is used to isolate replicas explicitly and forcibly if it's not empty.
// Its value must be empty or one of LocationLabels.
// Example:
// location-labels = ["zone", "rack", "host"]
// isolation-level = "zone"
// With configuration like above, PD ensure that all replicas be placed in different zones.
// Even if a zone is down, PD will not try to make up replicas in other zone
// because other zones already have replicas on it.
IsolationLevel string `toml:"isolation-level" json:"isolation-level"`
}

// StringSlice is more friendly to json encode/decode
type StringSlice []string
9 changes: 9 additions & 0 deletions tests/integrations/client/http_client_test.go
Original file line number Diff line number Diff line change
@@ -81,6 +81,15 @@ func (suite *httpClientTestSuite) TearDownSuite() {
suite.cluster.Destroy()
}

func (suite *httpClientTestSuite) TestHealth() {
re := suite.Require()
info, err := suite.client.GetHealth(context.Background())
println(info.Healths[0].Name)
println(info.Healths[0].ClientUrls)
println(info.Healths[0].Health)
re.NoError(err)
}

func (suite *httpClientTestSuite) TestMeta() {
re := suite.Require()
region, err := suite.client.GetRegionByID(suite.ctx, 10)

0 comments on commit cd38ad6

Please sign in to comment.