blob: cf1dff7fe748ebfdce83f488d423387052b52505 [file] [log] [blame]
/*
*
* Copyright 2022 gRPC 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 xdsresource implements the xDS data model layer.
//
// Provides resource-type specific functionality to unmarshal xDS protos into
// internal data structures that contain only fields gRPC is interested in.
// These internal data structures are passed to components in the xDS stack
// (resolver/balancers/server) that have expressed interest in receiving
// updates to specific resources.
package xdsresource
import (
"fmt"
"google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/xds/bootstrap"
xdsinternal "google.golang.org/grpc/xds/internal"
"google.golang.org/grpc/xds/internal/xdsclient/xdsresource/version"
"google.golang.org/protobuf/types/known/anypb"
)
func init() {
xdsinternal.ResourceTypeMapForTesting = make(map[string]any)
xdsinternal.ResourceTypeMapForTesting[version.V3ListenerURL] = listenerType
xdsinternal.ResourceTypeMapForTesting[version.V3RouteConfigURL] = routeConfigType
xdsinternal.ResourceTypeMapForTesting[version.V3ClusterURL] = clusterType
xdsinternal.ResourceTypeMapForTesting[version.V3EndpointsURL] = endpointsType
internal.TriggerXDSResourceNameNotFoundForTesting = triggerResourceNotFoundForTesting
}
// Producer contains a single method to discover resource configuration from a
// remote management server using xDS APIs.
//
// The xdsclient package provides a concrete implementation of this interface.
type Producer interface {
// WatchResource uses xDS to discover the resource associated with the
// provided resource name. The resource type implementation determines how
// xDS requests are sent out and how responses are deserialized and
// validated. Upon receipt of a response from the management server, an
// appropriate callback on the watcher is invoked.
WatchResource(rType Type, resourceName string, watcher ResourceWatcher) (cancel func())
}
// ResourceWatcher wraps the callbacks to be invoked for different events
// corresponding to the resource being watched.
type ResourceWatcher interface {
// OnUpdate is invoked to report an update for the resource being watched.
// The ResourceData parameter needs to be type asserted to the appropriate
// type for the resource being watched.
OnUpdate(ResourceData)
// OnError is invoked under different error conditions including but not
// limited to the following:
// - authority mentioned in the resource is not found
// - resource name parsing error
// - resource deserialization error
// - resource validation error
// - ADS stream failure
// - connection failure
OnError(error)
// OnResourceDoesNotExist is invoked for a specific error condition where
// the requested resource is not found on the xDS management server.
OnResourceDoesNotExist()
}
// TODO: Once the implementation is complete, rename this interface as
// ResourceType and get rid of the existing ResourceType enum.
// Type wraps all resource-type specific functionality. Each supported resource
// type will provide an implementation of this interface.
type Type interface {
// TypeURL is the xDS type URL of this resource type for v3 transport.
TypeURL() string
// TypeName identifies resources in a transport protocol agnostic way. This
// can be used for logging/debugging purposes, as well in cases where the
// resource type name is to be uniquely identified but the actual
// functionality provided by the resource type is not required.
//
// TODO: once Type is renamed to ResourceType, rename TypeName to
// ResourceTypeName.
TypeName() string
// AllResourcesRequiredInSotW indicates whether this resource type requires
// that all resources be present in every SotW response from the server. If
// true, a response that does not include a previously seen resource will be
// interpreted as a deletion of that resource.
AllResourcesRequiredInSotW() bool
// Decode deserializes and validates an xDS resource serialized inside the
// provided `Any` proto, as received from the xDS management server.
//
// If protobuf deserialization fails or resource validation fails,
// returns a non-nil error. Otherwise, returns a fully populated
// DecodeResult.
Decode(*DecodeOptions, *anypb.Any) (*DecodeResult, error)
}
// ResourceData contains the configuration data sent by the xDS management
// server, associated with the resource being watched. Every resource type must
// provide an implementation of this interface to represent the configuration
// received from the xDS management server.
type ResourceData interface {
isResourceData()
// Equal returns true if the passed in resource data is equal to that of the
// receiver.
Equal(ResourceData) bool
// ToJSON returns a JSON string representation of the resource data.
ToJSON() string
Raw() *anypb.Any
}
// DecodeOptions wraps the options required by ResourceType implementation for
// decoding configuration received from the xDS management server.
type DecodeOptions struct {
// BootstrapConfig contains the bootstrap configuration passed to the
// top-level xdsClient. This contains useful data for resource validation.
BootstrapConfig *bootstrap.Config
}
// DecodeResult is the result of a decode operation.
type DecodeResult struct {
// Name is the name of the resource being watched.
Name string
// Resource contains the configuration associated with the resource being
// watched.
Resource ResourceData
}
// resourceTypeState wraps the static state associated with concrete resource
// type implementations, which can then embed this struct and get the methods
// implemented here for free.
type resourceTypeState struct {
typeURL string
typeName string
allResourcesRequiredInSotW bool
}
func (r resourceTypeState) TypeURL() string {
return r.typeURL
}
func (r resourceTypeState) TypeName() string {
return r.typeName
}
func (r resourceTypeState) AllResourcesRequiredInSotW() bool {
return r.allResourcesRequiredInSotW
}
func triggerResourceNotFoundForTesting(cb func(Type, string) error, typeName, resourceName string) error {
var typ Type
switch typeName {
case ListenerResourceTypeName:
typ = listenerType
case RouteConfigTypeName:
typ = routeConfigType
case ClusterResourceTypeName:
typ = clusterType
case EndpointsResourceTypeName:
typ = endpointsType
default:
return fmt.Errorf("unknown type name %q", typeName)
}
return cb(typ, resourceName)
}