Skip to content

Commit 1e93d34

Browse files
committed
minimal impl
1 parent b2299e2 commit 1e93d34

File tree

89 files changed

+2955
-943
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+2955
-943
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
com.linecorp.armeria.xds.api.StrictXdsValidatorIndex

xds-api/src/main/java/com/linecorp/armeria/xds/api/DefaultXdsValidatorIndex.java

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,37 +19,33 @@
1919
import com.linecorp.armeria.common.annotation.UnstableApi;
2020
import com.linecorp.armeria.xds.validator.XdsValidatorIndex;
2121

22-
import io.envoyproxy.pgv.ReflectiveValidatorIndex;
23-
import io.envoyproxy.pgv.ValidationException;
24-
import io.envoyproxy.pgv.Validator;
25-
import io.envoyproxy.pgv.ValidatorIndex;
26-
2722
/**
28-
* The default validator which uses reflection in conjunction with pgv to validate messages.
23+
* The default validator which composes pgv (protoc-gen-validate) structural validation with
24+
* supported-field validation.
2925
*/
3026
@UnstableApi
31-
public final class DefaultXdsValidatorIndex implements ValidatorIndex, XdsValidatorIndex {
27+
public final class DefaultXdsValidatorIndex implements XdsValidatorIndex {
28+
29+
private static final DefaultXdsValidatorIndex INSTANCE = new DefaultXdsValidatorIndex();
3230

33-
private final ValidatorIndex delegate;
31+
private final PgvValidator pgvValidator = PgvValidator.of();
32+
private final SupportedFieldValidator supportedFieldValidator = SupportedFieldValidator.of();
3433

3534
/**
36-
* Creates a validator.
35+
* Creates a validator that composes pgv and supported-field validation.
3736
*/
38-
public DefaultXdsValidatorIndex() {
39-
delegate = new ReflectiveValidatorIndex();
40-
}
37+
public DefaultXdsValidatorIndex() {}
4138

42-
@Override
43-
public <T> Validator<T> validatorFor(Class clazz) {
44-
return delegate.validatorFor(clazz);
39+
/**
40+
* Returns the default singleton instance.
41+
*/
42+
public static DefaultXdsValidatorIndex of() {
43+
return INSTANCE;
4544
}
4645

4746
@Override
4847
public void assertValid(Object message) {
49-
try {
50-
validatorFor(message).assertValid(message);
51-
} catch (ValidationException e) {
52-
throw new IllegalArgumentException(e);
53-
}
48+
pgvValidator.assertValid(message);
49+
supportedFieldValidator.assertValid(message);
5450
}
5551
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2025 LY Corporation
3+
*
4+
* LY Corporation licenses this file to you under the Apache License,
5+
* version 2.0 (the "License"); you may not use this file except in compliance
6+
* with the License. You may obtain a copy of the License at:
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package com.linecorp.armeria.xds.api;
18+
19+
enum IgnoreUnsupportedFieldHandler implements UnsupportedFieldHandler {
20+
INSTANCE;
21+
22+
@Override
23+
public void handle(String fieldPath) {
24+
// no-op
25+
}
26+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2025 LY Corporation
3+
*
4+
* LY Corporation licenses this file to you under the Apache License,
5+
* version 2.0 (the "License"); you may not use this file except in compliance
6+
* with the License. You may obtain a copy of the License at:
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package com.linecorp.armeria.xds.api;
18+
19+
import static java.util.Objects.requireNonNull;
20+
21+
import com.linecorp.armeria.common.annotation.UnstableApi;
22+
import com.linecorp.armeria.common.metric.MeterIdPrefix;
23+
24+
import io.micrometer.core.instrument.Counter;
25+
import io.micrometer.core.instrument.MeterRegistry;
26+
27+
/**
28+
* An {@link UnsupportedFieldHandler} that records metrics for unsupported xDS field usage.
29+
* A counter is incremented for each unsupported field path detected, allowing users to
30+
* visualize which unsupported fields their xDS configuration relies on.
31+
*
32+
* <p>The counter name is {@code <prefix>.unsupported.fields} with a {@code field} tag
33+
* containing the dotted field path.
34+
*/
35+
@UnstableApi
36+
public final class MeterUnsupportedFieldHandler implements UnsupportedFieldHandler {
37+
38+
private final MeterRegistry meterRegistry;
39+
private final MeterIdPrefix meterIdPrefix;
40+
41+
/**
42+
* Creates a new handler that records unsupported field metrics.
43+
*
44+
* @param meterRegistry the registry to record metrics in
45+
* @param meterIdPrefix the prefix for meter IDs (e.g., {@code "armeria.xds"})
46+
*/
47+
public MeterUnsupportedFieldHandler(MeterRegistry meterRegistry, MeterIdPrefix meterIdPrefix) {
48+
this.meterRegistry = requireNonNull(meterRegistry, "meterRegistry");
49+
this.meterIdPrefix = requireNonNull(meterIdPrefix, "meterIdPrefix");
50+
}
51+
52+
@Override
53+
public void handle(String fieldPath) {
54+
final Counter counter = Counter.builder(meterIdPrefix.name("unsupported.fields"))
55+
.tags(meterIdPrefix.tags())
56+
.tag("field", fieldPath)
57+
.register(meterRegistry);
58+
counter.increment();
59+
}
60+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2025 LY Corporation
3+
*
4+
* LY Corporation licenses this file to you under the Apache License,
5+
* version 2.0 (the "License"); you may not use this file except in compliance
6+
* with the License. You may obtain a copy of the License at:
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package com.linecorp.armeria.xds.api;
18+
19+
import com.linecorp.armeria.common.annotation.UnstableApi;
20+
21+
import io.envoyproxy.pgv.ReflectiveValidatorIndex;
22+
import io.envoyproxy.pgv.ValidationException;
23+
import io.envoyproxy.pgv.ValidatorIndex;
24+
25+
/**
26+
* Validates xDS protobuf messages using pgv (protoc-gen-validate) structural validation.
27+
*/
28+
@UnstableApi
29+
public final class PgvValidator {
30+
31+
private static final PgvValidator INSTANCE = new PgvValidator();
32+
33+
private final ValidatorIndex delegate = new ReflectiveValidatorIndex();
34+
35+
private PgvValidator() {}
36+
37+
/**
38+
* Returns the singleton {@link PgvValidator} instance.
39+
*/
40+
public static PgvValidator of() {
41+
return INSTANCE;
42+
}
43+
44+
/**
45+
* Validates the given message using pgv structural validation.
46+
*
47+
* @throws IllegalArgumentException if validation fails
48+
*/
49+
public void assertValid(Object message) {
50+
try {
51+
delegate.validatorFor(message).assertValid(message);
52+
} catch (ValidationException e) {
53+
throw new IllegalArgumentException(e);
54+
}
55+
}
56+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2025 LY Corporation
3+
*
4+
* LY Corporation licenses this file to you under the Apache License,
5+
* version 2.0 (the "License"); you may not use this file except in compliance
6+
* with the License. You may obtain a copy of the License at:
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package com.linecorp.armeria.xds.api;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
import com.linecorp.armeria.common.annotation.UnstableApi;
23+
import com.linecorp.armeria.xds.validator.XdsValidatorIndex;
24+
25+
/**
26+
* A strict {@link XdsValidatorIndex} that composes pgv (protoc-gen-validate) structural validation
27+
* with supported-field validation. All unsupported field violations are collected and reported
28+
* together in a single {@link IllegalArgumentException}.
29+
*
30+
* <p>This validator is intended for use in test environments via SPI to catch unsupported
31+
* field usage early. Its {@link #priority()} is {@code 1}, which is higher than
32+
* {@link DefaultXdsValidatorIndex} ({@code 0}), so it wins SPI selection when present
33+
* on the classpath.
34+
*/
35+
@UnstableApi
36+
public final class StrictXdsValidatorIndex implements XdsValidatorIndex {
37+
38+
private final PgvValidator pgvValidator = PgvValidator.of();
39+
40+
@Override
41+
public void assertValid(Object message) {
42+
pgvValidator.assertValid(message);
43+
final List<String> violations = new ArrayList<>();
44+
final SupportedFieldValidator validator =
45+
new SupportedFieldValidator(violations::add);
46+
validator.assertValid(message);
47+
if (!violations.isEmpty()) {
48+
throw new IllegalArgumentException(
49+
"Unsupported xDS fields detected: " + String.join(", ", violations));
50+
}
51+
}
52+
53+
@Override
54+
public int priority() {
55+
return 1;
56+
}
57+
}

0 commit comments

Comments
 (0)