2023-03-28 18:39:22 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2022-09-26 16:50:17 +00:00
|
|
|
package cachetype
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
external "github.com/hashicorp/consul/agent/grpc-external"
|
2023-02-17 21:14:46 +00:00
|
|
|
"github.com/hashicorp/consul/proto/private/pbpeering"
|
2022-09-26 16:50:17 +00:00
|
|
|
"github.com/mitchellh/hashstructure"
|
|
|
|
"google.golang.org/grpc"
|
|
|
|
"google.golang.org/grpc/codes"
|
|
|
|
"google.golang.org/grpc/status"
|
|
|
|
|
|
|
|
"github.com/hashicorp/consul/agent/cache"
|
|
|
|
"github.com/hashicorp/consul/agent/structs"
|
|
|
|
)
|
|
|
|
|
|
|
|
// PeeringListName is the recommended name for registration.
|
|
|
|
const PeeringListName = "peers"
|
|
|
|
|
|
|
|
type PeeringListRequest struct {
|
|
|
|
Request *pbpeering.PeeringListRequest
|
|
|
|
structs.QueryOptions
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *PeeringListRequest) CacheInfo() cache.RequestInfo {
|
|
|
|
info := cache.RequestInfo{
|
|
|
|
Token: r.Token,
|
|
|
|
Datacenter: "",
|
|
|
|
MinIndex: 0,
|
|
|
|
Timeout: 0,
|
|
|
|
MustRevalidate: false,
|
|
|
|
|
|
|
|
// OPTIMIZE(peering): Cache.notifyPollingQuery polls at this interval. We need to revisit how that polling works.
|
|
|
|
// Using an exponential backoff when the result hasn't changed may be preferable.
|
|
|
|
MaxAge: 1 * time.Second,
|
|
|
|
}
|
|
|
|
|
|
|
|
v, err := hashstructure.Hash([]interface{}{
|
|
|
|
r.Request.Partition,
|
|
|
|
}, nil)
|
|
|
|
if err == nil {
|
|
|
|
// If there is an error, we don't set the key. A blank key forces
|
|
|
|
// no cache for this request so the request is forwarded directly
|
|
|
|
// to the server.
|
|
|
|
info.Key = strconv.FormatUint(v, 10)
|
|
|
|
}
|
|
|
|
|
|
|
|
return info
|
|
|
|
}
|
|
|
|
|
|
|
|
// Peerings supports fetching the list of peers for a given partition or wildcard-specifier.
|
|
|
|
type Peerings struct {
|
|
|
|
RegisterOptionsNoRefresh
|
|
|
|
Client PeeringLister
|
|
|
|
}
|
|
|
|
|
|
|
|
//go:generate mockery --name PeeringLister --inpackage --filename mock_PeeringLister_test.go
|
|
|
|
type PeeringLister interface {
|
|
|
|
PeeringList(
|
|
|
|
ctx context.Context, in *pbpeering.PeeringListRequest, opts ...grpc.CallOption,
|
|
|
|
) (*pbpeering.PeeringListResponse, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Peerings) Fetch(_ cache.FetchOptions, req cache.Request) (cache.FetchResult, error) {
|
|
|
|
var result cache.FetchResult
|
|
|
|
|
|
|
|
// The request should be a PeeringListRequest.
|
|
|
|
// We do not need to make a copy of this request type like in other cache types
|
|
|
|
// because the RequestInfo is synthetic.
|
|
|
|
reqReal, ok := req.(*PeeringListRequest)
|
|
|
|
if !ok {
|
|
|
|
return result, fmt.Errorf(
|
|
|
|
"Internal cache failure: request wrong type: %T", req)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Always allow stale - there's no point in hitting leader if the request is
|
|
|
|
// going to be served from cache and end up arbitrarily stale anyway. This
|
|
|
|
// allows cached service-discover to automatically read scale across all
|
|
|
|
// servers too.
|
|
|
|
reqReal.QueryOptions.SetAllowStale(true)
|
|
|
|
|
|
|
|
ctx, err := external.ContextWithQueryOptions(context.Background(), reqReal.QueryOptions)
|
|
|
|
if err != nil {
|
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fetch
|
|
|
|
reply, err := t.Client.PeeringList(ctx, reqReal.Request)
|
|
|
|
if err != nil {
|
|
|
|
// Return an empty result if the error is due to peering being disabled.
|
|
|
|
// This allows mesh gateways to receive an update and confirm that the watch is set.
|
|
|
|
if e, ok := status.FromError(err); ok && e.Code() == codes.FailedPrecondition {
|
|
|
|
result.Index = 1
|
|
|
|
result.Value = &pbpeering.PeeringListResponse{}
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
|
|
|
|
result.Value = reply
|
|
|
|
result.Index = reply.Index
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|