From 41d48591e0c1e4ac331952cbbb470a673fab65cf Mon Sep 17 00:00:00 2001 From: Ethan Rose Date: Wed, 29 Jul 2026 20:55:51 -0400 Subject: [PATCH 1/2] Add new OM version aspect with test --- .../om/upgrade/DisallowedUntilOmVersion.java | 39 +++++++++++++++++ ...spect.java => OMRequestVersionAspect.java} | 31 ++++++++----- .../src/main/resources/META-INF/aop.xml | 2 +- ...t.java => TestOMRequestVersionAspect.java} | 43 +++++++++++++++++-- 4 files changed, 101 insertions(+), 14 deletions(-) create mode 100644 hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/DisallowedUntilOmVersion.java rename hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/{OMLayoutFeatureAspect.java => OMRequestVersionAspect.java} (79%) rename hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/{TestOMLayoutFeatureAspect.java => TestOMRequestVersionAspect.java} (66%) diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/DisallowedUntilOmVersion.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/DisallowedUntilOmVersion.java new file mode 100644 index 000000000000..ad04f9db0fbc --- /dev/null +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/DisallowedUntilOmVersion.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.hadoop.ozone.om.upgrade; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.apache.hadoop.ozone.OzoneManagerVersion; + +/** + * Annotation used to "disallow" an API until the OM has finalized to the + * associated {@link OzoneManagerVersion}. Helps to keep the method logic + * and upgrade related cross-cutting concerns separate. + * + *

This is the {@link OzoneManagerVersion}-keyed counterpart of + * {@link DisallowedUntilLayoutVersion}, for features added after Zero Downtime Upgrade (ZDU) when + * {@link OMLayoutFeature} was frozen. + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface DisallowedUntilOmVersion { + OzoneManagerVersion value(); +} diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMLayoutFeatureAspect.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMRequestVersionAspect.java similarity index 79% rename from hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMLayoutFeatureAspect.java rename to hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMRequestVersionAspect.java index 479d47c31e62..8d5ea2705c8a 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMLayoutFeatureAspect.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMRequestVersionAspect.java @@ -31,18 +31,29 @@ import org.aspectj.lang.reflect.MethodSignature; /** - * 'Aspect' for OM Layout Feature API. All methods annotated with the - * specific annotation will have pre-processing done here to check layout - * version compatibility. + * 'Aspect' for OM component version API. All methods annotated with the + * specific annotation will have pre-processing done here to check version compatibility. */ @Aspect -public class OMLayoutFeatureAspect { +public class OMRequestVersionAspect { @Before("@annotation(DisallowedUntilLayoutVersion) && execution(* *(..))") public void checkLayoutFeature(JoinPoint joinPoint) throws IOException { ComponentVersion layoutFeature = ((MethodSignature) joinPoint.getSignature()) .getMethod().getAnnotation(DisallowedUntilLayoutVersion.class) .value(); + checkFeatureAllowed(joinPoint, layoutFeature); + } + + @Before("@annotation(DisallowedUntilOmVersion) && execution(* *(..))") + public void checkOmVersion(JoinPoint joinPoint) throws IOException { + ComponentVersion omVersion = ((MethodSignature) joinPoint.getSignature()) + .getMethod().getAnnotation(DisallowedUntilOmVersion.class) + .value(); + checkFeatureAllowed(joinPoint, omVersion); + } + + private void checkFeatureAllowed(JoinPoint joinPoint, ComponentVersion version) throws IOException { OMVersionManager versionManager = null; final Object[] args = joinPoint.getArgs(); if (joinPoint.getTarget() instanceof OzoneManagerRequestHandler) { @@ -61,17 +72,17 @@ public void checkLayoutFeature(JoinPoint joinPoint) throws IOException { + joinPoint.toShortString()); } // Throws an exception that must be propagated if the request is not allowed. - checkIsAllowed(joinPoint.getSignature().toShortString(), versionManager, layoutFeature); + checkIsAllowed(joinPoint.getSignature().toShortString(), versionManager, version); } private void checkIsAllowed(String operationName, OMVersionManager omVersionManager, - ComponentVersion layoutFeature) throws OMException { - if (!omVersionManager.isAllowed(layoutFeature)) { + ComponentVersion version) throws OMException { + if (!omVersionManager.isAllowed(version)) { throw new OMException(String.format("Operation %s cannot be invoked " + "before finalization. It belongs to version %s. Current apparent version is %s", operationName, - layoutFeature, + version, omVersionManager.getApparentVersion()), NOT_SUPPORTED_OPERATION_PRIOR_FINALIZATION); } @@ -81,8 +92,8 @@ private void checkIsAllowed(String operationName, * Note: Without this, it occasionally throws NoSuchMethodError when running * the test. */ - public static OMLayoutFeatureAspect aspectOf() { - return new OMLayoutFeatureAspect(); + public static OMRequestVersionAspect aspectOf() { + return new OMRequestVersionAspect(); } } diff --git a/hadoop-ozone/ozone-manager/src/main/resources/META-INF/aop.xml b/hadoop-ozone/ozone-manager/src/main/resources/META-INF/aop.xml index a96b5e8b71bc..7c512cebc16a 100644 --- a/hadoop-ozone/ozone-manager/src/main/resources/META-INF/aop.xml +++ b/hadoop-ozone/ozone-manager/src/main/resources/META-INF/aop.xml @@ -14,7 +14,7 @@ --> - + diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOMLayoutFeatureAspect.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOMRequestVersionAspect.java similarity index 66% rename from hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOMLayoutFeatureAspect.java rename to hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOMRequestVersionAspect.java index 14b3d3809f56..77bb3da0ae66 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOMLayoutFeatureAspect.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOMRequestVersionAspect.java @@ -27,9 +27,11 @@ import java.nio.file.Path; import org.apache.hadoop.hdds.ComponentVersion; import org.apache.hadoop.hdds.conf.OzoneConfiguration; +import org.apache.hadoop.ozone.OzoneManagerVersion; import org.apache.hadoop.ozone.om.OzoneManager; import org.apache.hadoop.ozone.om.exceptions.OMException; import org.apache.hadoop.ozone.om.request.snapshot.OMSnapshotCreateRequest; +import org.apache.hadoop.ozone.protocolPB.OzoneManagerRequestHandler; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.reflect.MethodSignature; import org.junit.jupiter.api.BeforeEach; @@ -40,7 +42,7 @@ * Class to test annotation based interceptor that checks whether layout * feature API is allowed. */ -public class TestOMLayoutFeatureAspect { +public class TestOMRequestVersionAspect { @TempDir private Path temporaryFolder; @@ -54,7 +56,7 @@ public void setUp() throws IOException { } /** - * Exercises {@link OMLayoutFeatureAspect#checkLayoutFeature} for an + * Exercises {@link OMRequestVersionAspect#checkLayoutFeature} for an * {@link org.apache.hadoop.ozone.om.request.OMClientRequest#preExecute} join * point using the real {@link OMSnapshotCreateRequest#preExecute} metadata * (including {@link DisallowedUntilLayoutVersion}). @@ -67,7 +69,7 @@ public void testDisallowedUntilLayoutVersion() throws Throwable { when(om.getVersionManager()).thenReturn(ovm); OMSnapshotCreateRequest request = mock(OMSnapshotCreateRequest.class); - OMLayoutFeatureAspect aspect = new OMLayoutFeatureAspect(); + OMRequestVersionAspect aspect = new OMRequestVersionAspect(); JoinPoint joinPoint = mock(JoinPoint.class); when(joinPoint.getTarget()).thenReturn(request); @@ -86,4 +88,39 @@ public void testDisallowedUntilLayoutVersion() throws Throwable { assertThat(omException.getMessage()) .contains("cannot be invoked before finalization"); } + + /** + * Exercises {@link OMRequestVersionAspect#checkOmVersion} for an + * {@link OzoneManagerRequestHandler} join point using a locally + * {@link DisallowedUntilOmVersion}-annotated method. + */ + @Test + public void testDisallowedUntilOmVersion() throws Throwable { + OzoneManager om = mock(OzoneManager.class); + OMVersionManager ovm = mock(OMVersionManager.class); + when(ovm.isAllowed(any(ComponentVersion.class))).thenReturn(false); + when(om.getVersionManager()).thenReturn(ovm); + + OzoneManagerRequestHandler handler = mock(OzoneManagerRequestHandler.class); + when(handler.getOzoneManager()).thenReturn(om); + OMRequestVersionAspect aspect = new OMRequestVersionAspect(); + + JoinPoint joinPoint = mock(JoinPoint.class); + when(joinPoint.getTarget()).thenReturn(handler); + when(joinPoint.getArgs()).thenReturn(new Object[]{}); + + MethodSignature methodSignature = mock(MethodSignature.class); + when(methodSignature.getMethod()) + .thenReturn(getClass().getDeclaredMethod("omVersionGated")); + when(joinPoint.getSignature()).thenReturn(methodSignature); + + OMException omException = assertThrows(OMException.class, + () -> aspect.checkOmVersion(joinPoint)); + assertThat(omException.getMessage()) + .contains("cannot be invoked before finalization"); + } + + @DisallowedUntilOmVersion(OzoneManagerVersion.ZDU) + void omVersionGated() { + } } From b7905d4540a090bd586030c27931024a3fd99be3 Mon Sep 17 00:00:00 2001 From: Ethan Rose <33912936+errose28@users.noreply.github.com> Date: Thu, 30 Jul 2026 19:35:44 -0400 Subject: [PATCH 2/2] Fix aspect error message Co-authored-by: Zita Dombi <50611074+dombizita@users.noreply.github.com> --- .../apache/hadoop/ozone/om/upgrade/OMRequestVersionAspect.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMRequestVersionAspect.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMRequestVersionAspect.java index 8d5ea2705c8a..351175eb8ff1 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMRequestVersionAspect.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMRequestVersionAspect.java @@ -67,7 +67,7 @@ private void checkFeatureAllowed(JoinPoint joinPoint, ComponentVersion version) versionManager = ozoneManager.getVersionManager(); } else { throw new IOException( - "Unable to resolve OMVersionManager for layout validation; " + "Unable to resolve OMVersionManager for version validation; " + "expected OzoneManagerRequestHandler or OMClientRequest.preExecute: " + joinPoint.toShortString()); }