Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
65 changes: 65 additions & 0 deletions xds/src/main/java/io/grpc/xds/ClusterMetadataRegistry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2024 The 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 io.grpc.xds;

import com.google.protobuf.Any;
import com.google.protobuf.InvalidProtocolBufferException;
import io.grpc.xds.GcpAuthenticationFilter.AudienceMetadataParser;
import java.util.HashMap;
import java.util.Map;

/**
* Registry for parsing cluster metadata values.
*
* <p>This class maintains a mapping of type URLs to {@link ClusterMetadataValueParser} instances,
* allowing for the parsing of different metadata types.
*/
final class ClusterMetadataRegistry {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't have "Cluster" in its name. It is for all xds metadata.

private static final ClusterMetadataRegistry INSTANCE = new ClusterMetadataRegistry();

private final Map<String, ClusterMetadataValueParser> supportedParsers = new HashMap<>();

private ClusterMetadataRegistry() {
registerParser(new AudienceMetadataParser());
}

static ClusterMetadataRegistry getInstance() {
return INSTANCE;
}

ClusterMetadataValueParser findParser(String typeUrl) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have a @Nullable annotation as the underlying get(key) method could return null if the key (typeUrl) is not found in the HashMap

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nullable is not required in grpc-java. It is inconsistent, and without a null checker, it is nothing more than documentation.

return supportedParsers.get(typeUrl);

Check warning on line 45 in xds/src/main/java/io/grpc/xds/ClusterMetadataRegistry.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/ClusterMetadataRegistry.java#L45

Added line #L45 was not covered by tests
}

private void registerParser(ClusterMetadataValueParser parser) {
supportedParsers.put(parser.getTypeUrl(), parser);
}

interface ClusterMetadataValueParser {

String getTypeUrl();

/**
* Parses the given {@link Any} object into a specific metadata value.
*
* @param any the {@link Any} object to parse.
* @return the parsed metadata value.
* @throws InvalidProtocolBufferException if the parsing fails.
*/
Object parse(Any any) throws InvalidProtocolBufferException;
}
}
30 changes: 30 additions & 0 deletions xds/src/main/java/io/grpc/xds/GcpAuthenticationFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.google.protobuf.Any;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import io.envoyproxy.envoy.extensions.filters.http.gcp_authn.v3.Audience;
import io.envoyproxy.envoy.extensions.filters.http.gcp_authn.v3.GcpAuthnFilterConfig;
import io.envoyproxy.envoy.extensions.filters.http.gcp_authn.v3.TokenCacheConfig;
import io.grpc.CallCredentials;
Expand All @@ -35,6 +36,7 @@
import io.grpc.MethodDescriptor;
import io.grpc.Status;
import io.grpc.auth.MoreCallCredentials;
import io.grpc.xds.ClusterMetadataRegistry.ClusterMetadataValueParser;
import io.grpc.xds.Filter.ClientInterceptorBuilder;
import java.util.LinkedHashMap;
import java.util.Map;
Expand Down Expand Up @@ -219,4 +221,32 @@
return cache.computeIfAbsent(key, create);
}
}

/**
* Parser for Audience metadata type.
*/
static class AudienceMetadataParser implements ClusterMetadataValueParser {

@Override
public String getTypeUrl() {
return "extensions.filters.http.gcp_authn.v3.Audience";
}

@Override
public String parse(Any any) throws InvalidProtocolBufferException {
if (any.is(Audience.class)) {
Audience audience = any.unpack(Audience.class);
String url = audience.getUrl();

Check warning on line 239 in xds/src/main/java/io/grpc/xds/GcpAuthenticationFilter.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/GcpAuthenticationFilter.java#L238-L239

Added lines #L238 - L239 were not covered by tests
if (url.isEmpty()) {
throw new InvalidProtocolBufferException(

Check warning on line 241 in xds/src/main/java/io/grpc/xds/GcpAuthenticationFilter.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/GcpAuthenticationFilter.java#L241

Added line #L241 was not covered by tests
"Audience URL is empty. Metadata value must contain a valid URL.");
}
return url;

Check warning on line 244 in xds/src/main/java/io/grpc/xds/GcpAuthenticationFilter.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/GcpAuthenticationFilter.java#L244

Added line #L244 was not covered by tests
} else {
throw new InvalidProtocolBufferException(
String.format("Unexpected message type: %s. Expected: %s",
any.getTypeUrl(), Audience.getDescriptor().getFullName()));

Check warning on line 248 in xds/src/main/java/io/grpc/xds/GcpAuthenticationFilter.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/GcpAuthenticationFilter.java#L246-L248

Added lines #L246 - L248 were not covered by tests
}
}
}
}
51 changes: 50 additions & 1 deletion xds/src/main/java/io/grpc/xds/XdsClusterResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.protobuf.Any;
import com.google.protobuf.Duration;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import com.google.protobuf.Struct;
import com.google.protobuf.util.Durations;
import io.envoyproxy.envoy.config.cluster.v3.CircuitBreakers.Thresholds;
import io.envoyproxy.envoy.config.cluster.v3.Cluster;
import io.envoyproxy.envoy.config.core.v3.Metadata;
import io.envoyproxy.envoy.config.core.v3.RoutingPriority;
import io.envoyproxy.envoy.config.core.v3.SocketAddress;
import io.envoyproxy.envoy.config.endpoint.v3.ClusterLoadAssignment;
Expand All @@ -42,14 +44,17 @@
import io.grpc.internal.GrpcUtil;
import io.grpc.internal.ServiceConfigUtil;
import io.grpc.internal.ServiceConfigUtil.LbConfig;
import io.grpc.xds.ClusterMetadataRegistry.ClusterMetadataValueParser;
import io.grpc.xds.EnvoyServerProtoData.OutlierDetection;
import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext;
import io.grpc.xds.XdsClusterResource.CdsUpdate;
import io.grpc.xds.client.XdsClient.ResourceUpdate;
import io.grpc.xds.client.XdsResourceType;
import io.grpc.xds.internal.ProtobufJsonConverter;
import io.grpc.xds.internal.security.CommonTlsContextUtil;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;

Expand Down Expand Up @@ -169,9 +174,48 @@
updateBuilder.filterMetadata(
ImmutableMap.copyOf(cluster.getMetadata().getFilterMetadataMap()));

try {
ImmutableMap<String, Object> parsedFilterMetadata =
parseClusterMetadata(cluster.getMetadata());
updateBuilder.parsedMetadata(parsedFilterMetadata);
} catch (InvalidProtocolBufferException e) {
throw new ResourceInvalidException(
"Failed to parse xDS filter metadata for cluster '" + cluster.getName() + "': "
+ e.getMessage(), e);

Check warning on line 184 in xds/src/main/java/io/grpc/xds/XdsClusterResource.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/XdsClusterResource.java#L181-L184

Added lines #L181 - L184 were not covered by tests
}

return updateBuilder.build();
}

private static ImmutableMap<String, Object> parseClusterMetadata(Metadata metadata)
throws InvalidProtocolBufferException {
ImmutableMap.Builder<String, Object> parsedMetadata = ImmutableMap.builder();

ClusterMetadataRegistry registry = ClusterMetadataRegistry.getInstance();
// Process typed_filter_metadata
for (Map.Entry<String, Any> entry : metadata.getTypedFilterMetadataMap().entrySet()) {
String key = entry.getKey();
Any value = entry.getValue();
ClusterMetadataValueParser parser = registry.findParser(value.getTypeUrl());

Check warning on line 199 in xds/src/main/java/io/grpc/xds/XdsClusterResource.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/XdsClusterResource.java#L197-L199

Added lines #L197 - L199 were not covered by tests
if (parser != null) {
Object parsedValue = parser.parse(value);
parsedMetadata.put(key, parsedValue);

Check warning on line 202 in xds/src/main/java/io/grpc/xds/XdsClusterResource.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/XdsClusterResource.java#L201-L202

Added lines #L201 - L202 were not covered by tests
}
}

Check warning on line 204 in xds/src/main/java/io/grpc/xds/XdsClusterResource.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/XdsClusterResource.java#L204

Added line #L204 was not covered by tests

// Process filter_metadata for remaining keys
for (Map.Entry<String, Struct> entry : metadata.getFilterMetadataMap().entrySet()) {
String key = entry.getKey();

Check warning on line 208 in xds/src/main/java/io/grpc/xds/XdsClusterResource.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/XdsClusterResource.java#L208

Added line #L208 was not covered by tests
if (!parsedMetadata.build().containsKey(key)) {
Struct structValue = entry.getValue();
Object jsonValue = ProtobufJsonConverter.convertToJson(structValue);
parsedMetadata.put(key, jsonValue);

Check warning on line 212 in xds/src/main/java/io/grpc/xds/XdsClusterResource.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/XdsClusterResource.java#L210-L212

Added lines #L210 - L212 were not covered by tests
}
}

Check warning on line 214 in xds/src/main/java/io/grpc/xds/XdsClusterResource.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/XdsClusterResource.java#L214

Added line #L214 was not covered by tests

return parsedMetadata.build();
}

private static StructOrError<CdsUpdate.Builder> parseAggregateCluster(Cluster cluster) {
String clusterName = cluster.getName();
Cluster.CustomClusterType customType = cluster.getClusterType();
Expand Down Expand Up @@ -571,13 +615,16 @@

abstract ImmutableMap<String, Struct> filterMetadata();

abstract ImmutableMap<String, Object> parsedMetadata();

private static Builder newBuilder(String clusterName) {
return new AutoValue_XdsClusterResource_CdsUpdate.Builder()
.clusterName(clusterName)
.minRingSize(0)
.maxRingSize(0)
.choiceCount(0)
.filterMetadata(ImmutableMap.of());
.filterMetadata(ImmutableMap.of())
.parsedMetadata(ImmutableMap.of());
}

static Builder forAggregate(String clusterName, List<String> prioritizedClusterNames) {
Expand Down Expand Up @@ -696,6 +743,8 @@

protected abstract Builder filterMetadata(ImmutableMap<String, Struct> filterMetadata);

protected abstract Builder parsedMetadata(ImmutableMap<String, Object> parsedMetadata);

abstract CdsUpdate build();
}
}
Expand Down
61 changes: 61 additions & 0 deletions xds/src/main/java/io/grpc/xds/internal/ProtobufJsonConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2024 The 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 io.grpc.xds.internal;

import com.google.protobuf.Struct;
import com.google.protobuf.Value;
import io.grpc.Internal;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
* Converter for Protobuf {@link Struct} to JSON-like {@link Map}.
*/
@Internal
public final class ProtobufJsonConverter {
private ProtobufJsonConverter() {}

public static Map<String, Object> convertToJson(Struct struct) {
Map<String, Object> result = new HashMap<>();

Check warning on line 34 in xds/src/main/java/io/grpc/xds/internal/ProtobufJsonConverter.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/internal/ProtobufJsonConverter.java#L34

Added line #L34 was not covered by tests
for (Map.Entry<String, Value> entry : struct.getFieldsMap().entrySet()) {
result.put(entry.getKey(), convertValue(entry.getValue()));
}
return result;

Check warning on line 38 in xds/src/main/java/io/grpc/xds/internal/ProtobufJsonConverter.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/internal/ProtobufJsonConverter.java#L36-L38

Added lines #L36 - L38 were not covered by tests
}

public static Object convertValue(Value value) {
switch (value.getKindCase()) {
case STRUCT_VALUE:
return convertToJson(value.getStructValue());

Check warning on line 44 in xds/src/main/java/io/grpc/xds/internal/ProtobufJsonConverter.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/internal/ProtobufJsonConverter.java#L44

Added line #L44 was not covered by tests
case LIST_VALUE:
return value.getListValue().getValuesList().stream()
.map(ProtobufJsonConverter::convertValue)
.collect(Collectors.toList());

Check warning on line 48 in xds/src/main/java/io/grpc/xds/internal/ProtobufJsonConverter.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/internal/ProtobufJsonConverter.java#L46-L48

Added lines #L46 - L48 were not covered by tests
case NUMBER_VALUE:
return value.getNumberValue();

Check warning on line 50 in xds/src/main/java/io/grpc/xds/internal/ProtobufJsonConverter.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/internal/ProtobufJsonConverter.java#L50

Added line #L50 was not covered by tests
case STRING_VALUE:
return value.getStringValue();

Check warning on line 52 in xds/src/main/java/io/grpc/xds/internal/ProtobufJsonConverter.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/internal/ProtobufJsonConverter.java#L52

Added line #L52 was not covered by tests
case BOOL_VALUE:
return value.getBoolValue();

Check warning on line 54 in xds/src/main/java/io/grpc/xds/internal/ProtobufJsonConverter.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/internal/ProtobufJsonConverter.java#L54

Added line #L54 was not covered by tests
case NULL_VALUE:
return null;

Check warning on line 56 in xds/src/main/java/io/grpc/xds/internal/ProtobufJsonConverter.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/internal/ProtobufJsonConverter.java#L56

Added line #L56 was not covered by tests
default:
throw new IllegalArgumentException("Unknown Value type: " + value.getKindCase());

Check warning on line 58 in xds/src/main/java/io/grpc/xds/internal/ProtobufJsonConverter.java

View check run for this annotation

Codecov / codecov/patch

xds/src/main/java/io/grpc/xds/internal/ProtobufJsonConverter.java#L58

Added line #L58 was not covered by tests
}
}
}
Loading