Skip to content

Commit

Permalink
Merge branch 'grpc:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
SreeramdasLavanya authored Oct 1, 2024
2 parents 6a66054 + fa26a8b commit 4113845
Show file tree
Hide file tree
Showing 44 changed files with 1,052 additions and 647 deletions.
4 changes: 3 additions & 1 deletion RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ would be used to create all `v1.7` tags (e.g. `v1.7.0`, `v1.7.1`).
```bash
git fetch upstream
git checkout -b v$MAJOR.$MINOR.x \
$(git log --pretty=format:%H --grep "^Start $MAJOR.$((MINOR+1)).0 development cycle$" upstream/master)^
$(git log --pretty=format:%H --grep "^Start $MAJOR.$((MINOR+1)).0 development cycle" upstream/master)^
git push upstream v$MAJOR.$MINOR.x
```
5. Continue with Google-internal steps at go/grpc-java/releasing, but stop
Expand Down Expand Up @@ -132,7 +132,9 @@ Tagging the Release
compiler/src/test{,Lite}/golden/Test{,Deprecated}Service.java.txt
./gradlew build
git commit -a -m "Bump version to $MAJOR.$MINOR.$((PATCH+1))-SNAPSHOT"
git push -u origin release-v$MAJOR.$MINOR.$PATCH
```
Raise a PR and set the base branch of the PR to v$MAJOR.$MINOR.x of the upstream grpc-java repo.
6. Go through PR review and push the release tag and updated release branch to
GitHub (DO NOT click the merge button on the GitHub page):

Expand Down
7 changes: 5 additions & 2 deletions android/src/main/java/io/grpc/android/UdsChannelBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,15 @@ public static ManagedChannelBuilder<?> forPath(String path, Namespace namespace)
throw new UnsupportedOperationException("OkHttpChannelBuilder not found on the classpath");
}
try {
// Target 'dns:///localhost' is unused, but necessary as an argument for OkHttpChannelBuilder.
// Target 'dns:///127.0.0.1' is unused, but necessary as an argument for OkHttpChannelBuilder.
// An IP address is used instead of localhost to avoid a DNS lookup (see #11442). This should
// work even if IPv4 is unavailable, as the DNS resolver doesn't need working IPv4 to parse an
// IPv4 address. Unavailable IPv4 fails when we connect(), not at resolution time.
// TLS is unsupported because Conscrypt assumes the platform Socket implementation to improve
// performance by using the file descriptor directly.
Object o = OKHTTP_CHANNEL_BUILDER_CLASS
.getMethod("forTarget", String.class, ChannelCredentials.class)
.invoke(null, "dns:///localhost", InsecureChannelCredentials.create());
.invoke(null, "dns:///127.0.0.1", InsecureChannelCredentials.create());
ManagedChannelBuilder<?> builder = OKHTTP_CHANNEL_BUILDER_CLASS.cast(o);
OKHTTP_CHANNEL_BUILDER_CLASS
.getMethod("socketFactory", SocketFactory.class)
Expand Down
10 changes: 9 additions & 1 deletion core/src/main/java/io/grpc/internal/ManagedChannelImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -956,7 +956,15 @@ void updateConfigSelector(@Nullable InternalConfigSelector config) {
// Must run in SynchronizationContext.
void onConfigError() {
if (configSelector.get() == INITIAL_PENDING_SELECTOR) {
updateConfigSelector(null);
// Apply Default Service Config if initial name resolution fails.
if (defaultServiceConfig != null) {
updateConfigSelector(defaultServiceConfig.getDefaultConfigSelector());
lastServiceConfig = defaultServiceConfig;
channelLogger.log(ChannelLogLevel.ERROR,
"Initial Name Resolution error, using default service config");
} else {
updateConfigSelector(null);
}
}
}

Expand Down
122 changes: 122 additions & 0 deletions core/src/main/java/io/grpc/internal/SpiffeUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* 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.internal;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.base.Splitter;
import java.util.Locale;

/**
* Helper utility to work with SPIFFE URIs.
* @see <a href="https://github.com/spiffe/spiffe/blob/master/standards/SPIFFE-ID.md">Standard</a>
*/
public final class SpiffeUtil {

private static final String PREFIX = "spiffe://";

private SpiffeUtil() {}

/**
* Parses a URI string, applies validation rules described in SPIFFE standard, and, in case of
* success, returns parsed TrustDomain and Path.
*
* @param uri a String representing a SPIFFE ID
*/
public static SpiffeId parse(String uri) {
doInitialUriValidation(uri);
checkArgument(uri.toLowerCase(Locale.US).startsWith(PREFIX), "Spiffe Id must start with "
+ PREFIX);
String domainAndPath = uri.substring(PREFIX.length());
String trustDomain;
String path;
if (!domainAndPath.contains("/")) {
trustDomain = domainAndPath;
path = "";
} else {
String[] parts = domainAndPath.split("/", 2);
trustDomain = parts[0];
path = parts[1];
checkArgument(!path.isEmpty(), "Path must not include a trailing '/'");
}
validateTrustDomain(trustDomain);
validatePath(path);
if (!path.isEmpty()) {
path = "/" + path;
}
return new SpiffeId(trustDomain, path);
}

private static void doInitialUriValidation(String uri) {
checkArgument(checkNotNull(uri, "uri").length() > 0, "Spiffe Id can't be empty");
checkArgument(uri.length() <= 2048, "Spiffe Id maximum length is 2048 characters");
checkArgument(!uri.contains("#"), "Spiffe Id must not contain query fragments");
checkArgument(!uri.contains("?"), "Spiffe Id must not contain query parameters");
}

private static void validateTrustDomain(String trustDomain) {
checkArgument(!trustDomain.isEmpty(), "Trust Domain can't be empty");
checkArgument(trustDomain.length() < 256, "Trust Domain maximum length is 255 characters");
checkArgument(trustDomain.matches("[a-z0-9._-]+"),
"Trust Domain must contain only letters, numbers, dots, dashes, and underscores"
+ " ([a-z0-9.-_])");
}

private static void validatePath(String path) {
if (path.isEmpty()) {
return;
}
checkArgument(!path.endsWith("/"), "Path must not include a trailing '/'");
for (String segment : Splitter.on("/").split(path)) {
validatePathSegment(segment);
}
}

private static void validatePathSegment(String pathSegment) {
checkArgument(!pathSegment.isEmpty(), "Individual path segments must not be empty");
checkArgument(!(pathSegment.equals(".") || pathSegment.equals("..")),
"Individual path segments must not be relative path modifiers (i.e. ., ..)");
checkArgument(pathSegment.matches("[a-zA-Z0-9._-]+"),
"Individual path segments must contain only letters, numbers, dots, dashes, and underscores"
+ " ([a-zA-Z0-9.-_])");
}

/**
* Represents a SPIFFE ID as defined in the SPIFFE standard.
* @see <a href="https://github.com/spiffe/spiffe/blob/master/standards/SPIFFE-ID.md">Standard</a>
*/
public static class SpiffeId {

private final String trustDomain;
private final String path;

private SpiffeId(String trustDomain, String path) {
this.trustDomain = trustDomain;
this.path = path;
}

public String getTrustDomain() {
return trustDomain;
}

public String getPath() {
return path;
}
}

}
53 changes: 52 additions & 1 deletion core/src/test/java/io/grpc/internal/ManagedChannelImplTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
import io.grpc.InternalChannelz;
import io.grpc.InternalChannelz.ChannelStats;
import io.grpc.InternalChannelz.ChannelTrace;
import io.grpc.InternalChannelz.ChannelTrace.Event.Severity;
import io.grpc.InternalConfigSelector;
import io.grpc.InternalInstrumented;
import io.grpc.LoadBalancer;
Expand Down Expand Up @@ -123,6 +124,7 @@
import io.grpc.internal.ManagedChannelImplBuilder.ClientTransportFactoryBuilder;
import io.grpc.internal.ManagedChannelImplBuilder.FixedPortProvider;
import io.grpc.internal.ManagedChannelImplBuilder.UnsupportedClientTransportFactoryBuilder;
import io.grpc.internal.ManagedChannelServiceConfig.MethodInfo;
import io.grpc.internal.ServiceConfigUtil.PolicySelection;
import io.grpc.internal.TestUtils.MockClientTransportInfo;
import io.grpc.stub.ClientCalls;
Expand Down Expand Up @@ -1127,6 +1129,55 @@ public void noMoreCallbackAfterLoadBalancerShutdown_configError() throws Interru
verifyNoMoreInteractions(mockLoadBalancer);
}

@Test
public void addressResolutionError_noPriorNameResolution_usesDefaultServiceConfig()
throws Exception {
Map<String, Object> rawServiceConfig =
parseConfig("{\"methodConfig\":[{"
+ "\"name\":[{\"service\":\"service\"}],"
+ "\"waitForReady\":true}]}");
ManagedChannelServiceConfig managedChannelServiceConfig =
createManagedChannelServiceConfig(rawServiceConfig, null);
FakeNameResolverFactory nameResolverFactory =
new FakeNameResolverFactory.Builder(expectedUri)
.setServers(Collections.singletonList(new EquivalentAddressGroup(socketAddress)))
.setResolvedAtStart(false)
.build();
nameResolverFactory.nextConfigOrError.set(
ConfigOrError.fromConfig(managedChannelServiceConfig));
channelBuilder.nameResolverFactory(nameResolverFactory);
Map<String, Object> defaultServiceConfig =
parseConfig("{\"methodConfig\":[{"
+ "\"name\":[{\"service\":\"service\"}],"
+ "\"waitForReady\":true}]}");
channelBuilder.defaultServiceConfig(defaultServiceConfig);
Status resolutionError = Status.UNAVAILABLE.withDescription("Resolution failed");
channelBuilder.maxTraceEvents(10);
createChannel();
FakeNameResolverFactory.FakeNameResolver resolver = nameResolverFactory.resolvers.get(0);

resolver.listener.onError(resolutionError);

InternalConfigSelector configSelector = channel.getConfigSelector();
ManagedChannelServiceConfig config =
(ManagedChannelServiceConfig) configSelector.selectConfig(null).getConfig();
MethodInfo methodConfig = config.getMethodConfig(method);
assertThat(methodConfig.waitForReady).isTrue();
timer.forwardNanos(1234);
assertThat(getStats(channel).channelTrace.events).contains(new ChannelTrace.Event.Builder()
.setDescription("Initial Name Resolution error, using default service config")
.setSeverity(Severity.CT_ERROR)
.setTimestampNanos(0)
.build());

// Check that "lastServiceConfig" variable has been set above: a config resolution with the same
// config simply gets ignored and not gets reassigned.
resolver.resolved();
timer.forwardNanos(1234);
assertThat(getStats(channel).channelTrace.events.stream().filter(
event -> event.description.equals("Service config changed")).count()).isEqualTo(0);
}

@Test
public void interceptor() throws Exception {
final AtomicLong atomic = new AtomicLong();
Expand Down Expand Up @@ -4595,7 +4646,7 @@ public void notUseDefaultImmediatelyIfEnableLookUp() throws Exception {
int size = getStats(channel).channelTrace.events.size();
assertThat(getStats(channel).channelTrace.events.get(size - 1))
.isNotEqualTo(new ChannelTrace.Event.Builder()
.setDescription("Using default service config")
.setDescription("timer.forwardNanos(1234);")
.setSeverity(ChannelTrace.Event.Severity.CT_INFO)
.setTimestampNanos(timer.getTicker().read())
.build());
Expand Down
Loading

0 comments on commit 4113845

Please sign in to comment.