Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@
public class ConsoleGlobalProperty {
@GlobalProperty(name="ConsoleProxy.agentPackageName", defaultValue = "consoleproxy-5.5.0.tar.gz")
public static String AGENT_PACKAGE_NAME;
@GlobalProperty(name="MN.network.", defaultValue = "")
@GlobalProperty(name="MN.network.", defaultValue = "", immutable = true)
public static List<String> MN_NETWORKS;
}
1 change: 1 addition & 0 deletions core/src/main/java/org/zstack/core/GlobalProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@
String[] defaultListValue() default {};
boolean required() default false;
boolean encrypted() default false;
boolean immutable() default false;
}
3 changes: 3 additions & 0 deletions core/src/main/java/org/zstack/core/Platform.java
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ private static void linkGlobalProperty(Class clz, Map<String, String> properties
ret = ret.stream().map(it -> rsa.decrypt(it, encryptionKey)).collect(Collectors.toList());
}

if (at.immutable()) {
ret = Collections.unmodifiableList(ret);
}
valueToSet = ret;
} else {
String value = propertiesMap.get(name);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package org.zstack.core.property;

import org.junit.Test;
import org.zstack.core.GlobalProperty;
import org.zstack.core.GlobalPropertyDefinition;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.*;

/**
* Gate check: all @GlobalProperty List fields must be marked immutable = true.
* Fields that are intentionally mutable at runtime must be added to the MUTABLE_ALLOWLIST.
*
* If this test fails, either:
* 1. Add immutable = true to your @GlobalProperty annotation, or
* 2. Add the field to MUTABLE_ALLOWLIST with a comment explaining why it needs to be mutable.
*/
public class TestGlobalPropertyImmutableListGate {

/**
* Allowlist for List fields that are intentionally reassigned at runtime.
* Format: "FullClassName.fieldName"
*/
private static final Set<String> MUTABLE_ALLOWLIST = new HashSet<>(Arrays.asList(
"org.zstack.core.CoreGlobalProperty.CHRONY_SERVERS", // reassigned by ZOpsManagerImpl / ManagementNodeBackend
"org.zstack.core.cloudbus.CloudBusGlobalProperty.SERVER_IPS" // reassigned at runtime for cluster membership
));

@Test
public void allGlobalPropertyListFieldsMustBeImmutable() {
List<String> violations = new ArrayList<>();

for (Class<?> clz : findGlobalPropertyDefinitionClasses()) {
for (Field f : clz.getDeclaredFields()) {
GlobalProperty at = f.getAnnotation(GlobalProperty.class);
if (at == null) {
continue;
}

if (!List.class.isAssignableFrom(f.getType())) {
continue;
}

String fieldKey = clz.getName() + "." + f.getName();
if (MUTABLE_ALLOWLIST.contains(fieldKey)) {
continue;
}

if (!at.immutable()) {
violations.add(String.format(
"%s.%s: @GlobalProperty List field missing immutable=true. " +
"Add immutable=true or add to MUTABLE_ALLOWLIST with justification.",
clz.getSimpleName(), f.getName()));
}
}
}

if (!violations.isEmpty()) {
StringBuilder sb = new StringBuilder();
sb.append(String.format("%d @GlobalProperty List field(s) not marked immutable:\n", violations.size()));
for (String v : violations) {
sb.append(" - ").append(v).append("\n");
}
throw new AssertionError(sb.toString());
}
}

private List<Class<?>> findGlobalPropertyDefinitionClasses() {
List<Class<?>> result = new ArrayList<>();
String[] basePackages = {"org.zstack"};

for (String pkg : basePackages) {
String path = pkg.replace('.', '/');
try {
Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources(path);
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
if ("file".equals(resource.getProtocol())) {
scanDirectory(new File(resource.toURI()), pkg, result);
}
}
} catch (Exception e) {
// skip unresolvable classpath entries
}
}

return result;
}

private void scanDirectory(File dir, String packageName, List<Class<?>> result) {
if (!dir.exists()) {
return;
}

File[] files = dir.listFiles();
if (files == null) {
return;
}

for (File file : files) {
if (file.isDirectory()) {
scanDirectory(file, packageName + "." + file.getName(), result);
} else if (file.getName().endsWith(".class")) {
String className = packageName + "." + file.getName().replace(".class", "");
try {
Class<?> clz = Class.forName(className, false, Thread.currentThread().getContextClassLoader());
if (clz.isAnnotationPresent(GlobalPropertyDefinition.class)) {
result.add(clz);
}
} catch (Throwable e) {
// skip classes that can't be loaded
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ public class CephGlobalProperty {
public static String PRIMARY_STORAGE_MODULE_PATH;
@GlobalProperty(name="Ceph.vendor.getXskyLicense.Port", defaultValue = "8051")
public static String GET_XSKY_LICENSE_PORT;
@GlobalProperty(name="MN.network.", defaultValue = "")
@GlobalProperty(name="MN.network.", defaultValue = "", immutable = true)
public static List<String> MN_NETWORKS;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@
import org.zstack.header.apimediator.StopRoutingException;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;

/**
* @ Author : yh.w
* @ Date : Created in 10:43 2021/10/26
*/
public class FlatNetworkServiceValidator extends ScatteredValidator {
private static List<Method> methods;
private static final List<Method> methods;

static {
// method signature: static void xxx(String hostUuid)
methods = collectValidatorMethods(FlatNetworkServiceValidatorMethod.class, String.class);
methods = Collections.unmodifiableList(collectValidatorMethods(FlatNetworkServiceValidatorMethod.class, String.class));
}

public boolean validate(String hostUuid) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,22 @@ public class KVMGlobalProperty {
public static int AGENT_PORT;
@GlobalProperty(name="KvmAgentServer.port", defaultValue = "10001")
public static int AGENT_SERVER_PORT;
@GlobalProperty(name="KvmHost.iptables.rule.", defaultValue = "")
@GlobalProperty(name="KvmHost.iptables.rule.", defaultValue = "", immutable = true)
public static List<String> IPTABLES_RULES;
@GlobalProperty(name="KvmHost.maxThreads.ratio", defaultValue = "0.6")
public static double KVM_HOST_MAX_THREDS_RATIO;
@GlobalProperty(name="KvmAgent.tcpServerPort", defaultValue = "7123")
public static int TCP_SERVER_PORT;
@GlobalProperty(name="KvmHost.takeOverFlagPath", defaultValue = "/var/run/zstack/takeOver")
public static String TAKEVOERFLAGPATH;
@GlobalProperty(name="MN.network.", defaultValue = "")
@GlobalProperty(name="MN.network.", defaultValue = "", immutable = true)
public static List<String> MN_NETWORKS;
@GlobalProperty(name = "host.skip.packages", defaultValue = "qemu, qemu-kvm, qemu-kvm-ev, qemu-img, qemu-img-ev")
public static String SKIP_PACKAGES;
@GlobalProperty(name = "host.stop.shutdown.vm", defaultValue = "false")
public static boolean HOST_STOP_SHUTDOWN_VM;

@GlobalProperty(name = "host.network.need.alarm.interface.service", defaultListValue = {"ManagementNetwork", "MigrationNetwork"})
@GlobalProperty(name = "host.network.need.alarm.interface.service", defaultListValue = {"ManagementNetwork", "MigrationNetwork"}, immutable = true)
@AvailableValues(value = {"ManagementNetwork", "TenantNetwork", "StorageNetwork", "BackupNetwork", "MigrationNetwork"})
public static List<String> HOST_NETWORK_NEED_ALARM_INTERFACE_SERVICE;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ public class SftpBackupStorageGlobalProperty {
public static String AGENT_URL_ROOT_PATH;
@GlobalProperty(name="SftpBackupStorage.DownloadCmd.timeout", defaultValue = "7200")
public static int DOWNLOAD_CMD_TIMEOUT;
@GlobalProperty(name="MN.network.", defaultValue = "")
@GlobalProperty(name="MN.network.", defaultValue = "", immutable = true)
public static List<String> MN_NETWORKS;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ public class VirtualRouterGlobalProperty {
public static String AGENT_URL_ROOT_PATH;
@GlobalProperty(name="VirtualRouter.agentUrlScheme", defaultValue = "http")
public static String AGENT_URL_SCHEME;
@GlobalProperty(name="VirtualRouter.portsOpenOnManagementNic.tcp.")
@GlobalProperty(name="VirtualRouter.portsOpenOnManagementNic.tcp.", immutable = true)
public static List<String> TCP_PORTS_ON_MGMT_NIC;
@GlobalProperty(name="VirtualRouter.portsOpenOnManagementNic.udp.")
@GlobalProperty(name="VirtualRouter.portsOpenOnManagementNic.udp.", immutable = true)
public static List<String> UDP_PORTS_ON_MGMT_NIC;
@GlobalProperty(name="VirtualRouter.enableMultiSnat", defaultValue = "true")
public static boolean ENABLE_MULTI_SNAT;

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ public class ZbsGlobalProperty {
public static int PRIMARY_STORAGE_AGENT_PORT;
@GlobalProperty(name="Zbs.primaryStorageAgent.urlRootPath", defaultValue = "")
public static String PRIMARY_STORAGE_AGENT_URL_ROOT_PATH;
@GlobalProperty(name="MN.network.", defaultValue = "")
@GlobalProperty(name="MN.network.", defaultValue = "", immutable = true)
public static List<String> MN_NETWORKS;
}