T getBean(String beanName) {
+ if (context == null) {
+ logger.info("SpringContext.init()");
+ init();
+ }
+ if (context != null) {
+ logger.info("SpringContext.context not null");
+ return (T) context.getBean(beanName);
+ } else {
+ logger.info("SpringContext.context is null");
+ return null;
+ }
+ }
+}
diff --git a/liquibase-database-upgrade/src/main/java/org/apache/ranger/db/upgrade/StatusCheckScheduledExecutorService.java b/liquibase-database-upgrade/src/main/java/org/apache/ranger/db/upgrade/StatusCheckScheduledExecutorService.java
new file mode 100644
index 0000000000..7e387ec524
--- /dev/null
+++ b/liquibase-database-upgrade/src/main/java/org/apache/ranger/db/upgrade/StatusCheckScheduledExecutorService.java
@@ -0,0 +1,84 @@
+/*
+ * 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.ranger.db.upgrade;
+
+import liquibase.command.CommandResults;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+@Component
+@Scope("prototype")
+class StatusCheckScheduledExecutorService {
+ private static final Logger LOG = LoggerFactory.getLogger(StatusCheckScheduledExecutorService.class);
+ private static final int SLEEP_DURATION = 30;
+ private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
+ @Autowired
+ ICommandDriver commandDriver;
+ private String changelogFilename;
+ private ScheduledFuture> statusHandler;
+
+ public void startStatusChecks(String serviceName, String changelogFilename) {
+ LOG.info("StatusCheckScheduledExecutorService commandDriverClass={} \n", commandDriver.getClass());
+ this.changelogFilename = changelogFilename;
+ stopStatusChecks();
+ final Runnable liquibaseStatusCommand = () -> {
+ LOG.info("Executing status check command");
+ try {
+ CommandResults statusCheckResult = commandDriver.status(serviceName, changelogFilename);
+ LOG.info("statusCheckResult={}", statusCheckResult.getResults());
+ } catch (Exception e) {
+ stopStatusChecks();
+ throw new RuntimeException(e);
+ }
+ };
+ LOG.info("Starting status check scheduler");
+ statusHandler = scheduler.scheduleWithFixedDelay(liquibaseStatusCommand, 0, SLEEP_DURATION, SECONDS);
+ }
+
+ public void stopStatusChecks() {
+ if (statusHandler == null) {
+ LOG.info("statusHandler is null, stop not required for statusChecks");
+ return;
+ }
+ LOG.info("Attempting to cancel statusChecks");
+ try {
+ scheduler.shutdown();
+ statusHandler.cancel(true);
+ } catch (Exception e) {
+ LOG.info("Exception while trying to shutdown/cancel status check schedule");
+ }
+ if (statusHandler.isCancelled()) {
+ LOG.info("Canceled the Status Check service for {}", changelogFilename);
+ } else {
+ try {
+ LOG.info("Could not cancel the Status Check service for {}", changelogFilename);
+ Thread.sleep(3000);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
diff --git a/liquibase-database-upgrade/src/main/java/org/apache/ranger/db/upgrade/Utils.java b/liquibase-database-upgrade/src/main/java/org/apache/ranger/db/upgrade/Utils.java
new file mode 100644
index 0000000000..f17f8b71bd
--- /dev/null
+++ b/liquibase-database-upgrade/src/main/java/org/apache/ranger/db/upgrade/Utils.java
@@ -0,0 +1,31 @@
+/*
+ * 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.ranger.db.upgrade;
+
+public class Utils {
+ private Utils() {
+ }
+
+ public static String getPostUpdateTagName(String tagName) {
+ return tagName + Constants.TAG_POST_UPGRADE_POSTFIX;
+ }
+
+ public static String getFinalizeTagName(String tagName) {
+ return tagName + Constants.TAG_FINALIZE_POSTFIX;
+ }
+}
diff --git a/liquibase-database-upgrade/src/main/java/org/apache/ranger/db/upgrade/patches/kms/UpdateKMSAttrs.java b/liquibase-database-upgrade/src/main/java/org/apache/ranger/db/upgrade/patches/kms/UpdateKMSAttrs.java
new file mode 100644
index 0000000000..02e91e5509
--- /dev/null
+++ b/liquibase-database-upgrade/src/main/java/org/apache/ranger/db/upgrade/patches/kms/UpdateKMSAttrs.java
@@ -0,0 +1,115 @@
+/**
+ * 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.ranger.db.upgrade.patches.kms;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import liquibase.change.custom.CustomTaskChange;
+import liquibase.change.custom.CustomTaskRollback;
+import liquibase.database.Database;
+import liquibase.exception.CustomChangeException;
+import liquibase.exception.SetupException;
+import liquibase.exception.ValidationErrors;
+import liquibase.resource.ResourceAccessor;
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.crypto.key.RangerKMSDB;
+import org.apache.hadoop.crypto.key.RangerKeyStoreProvider;
+import org.apache.ranger.entity.XXRangerKeyStore;
+import org.apache.ranger.kms.dao.DaoManager;
+import org.apache.ranger.kms.dao.RangerKMSDao;
+
+import java.util.List;
+import java.util.Map;
+
+@SuppressWarnings("PMD.JUnit4TestShouldUseBeforeAnnotation")
+public class UpdateKMSAttrs implements CustomTaskChange, CustomTaskRollback {
+ @Override
+ public void execute(Database database) throws CustomChangeException {
+ try {
+ Gson gson = new GsonBuilder().create();
+ Configuration conf = RangerKeyStoreProvider.getDBKSConf();
+ RangerKMSDB rangerKMSDB = new RangerKMSDB(conf);
+ DaoManager daoManager = rangerKMSDB.getDaoManager();
+ RangerKMSDao kmsDao = daoManager.getRangerKMSDao();
+ List rangerKeyStoreList = kmsDao.getAllKeys();
+ for (XXRangerKeyStore xxRangerKeyStore : rangerKeyStoreList) {
+ String kmsAttrsStr = xxRangerKeyStore.getAttributes();
+ Map kmsAttrs = gson.fromJson(kmsAttrsStr, Map.class);
+ if (MapUtils.isNotEmpty(kmsAttrs)) {
+ String kmsAclName = kmsAttrs.get("key.acl.name");
+ if (StringUtils.isNotEmpty(kmsAclName)) {
+ kmsAttrs.put("key.acl.new.name", kmsAclName);
+ //kmsAttrs.remove("key.acl.name"); -- For backward compatibility
+ kmsAttrsStr = gson.toJson(kmsAttrs);
+ xxRangerKeyStore.setAttributes(kmsAttrsStr);
+ kmsDao.update(xxRangerKeyStore);
+ }
+ }
+ }
+ } catch (Exception e) {
+ throw new CustomChangeException(e);
+ }
+ }
+
+ @Override
+ public void rollback(Database database) throws CustomChangeException {
+ try {
+ Gson gson = new GsonBuilder().create();
+ Configuration conf = RangerKeyStoreProvider.getDBKSConf();
+ RangerKMSDB rangerKMSDB = new RangerKMSDB(conf);
+ DaoManager daoManager = rangerKMSDB.getDaoManager();
+ RangerKMSDao kmsDao = daoManager.getRangerKMSDao();
+ List rangerKeyStoreList = kmsDao.getAllKeys();
+ for (XXRangerKeyStore xxRangerKeyStore : rangerKeyStoreList) {
+ String kmsAttrsStr = xxRangerKeyStore.getAttributes();
+ Map kmsAttrs = gson.fromJson(kmsAttrsStr, Map.class);
+ if (MapUtils.isNotEmpty(kmsAttrs)) {
+ String kmsAclName = kmsAttrs.get("key.acl.new.name");
+ if (StringUtils.isNotEmpty(kmsAclName)) {
+ kmsAttrs.remove("key.acl.new.name");
+ kmsAttrsStr = gson.toJson(kmsAttrs);
+ xxRangerKeyStore.setAttributes(kmsAttrsStr);
+ kmsDao.update(xxRangerKeyStore);
+ }
+ }
+ }
+ } catch (Exception e) {
+ throw new CustomChangeException(e);
+ }
+ }
+
+ @Override
+ public String getConfirmationMessage() {
+ return null;
+ }
+
+ @Override
+ public void setUp() throws SetupException {
+ }
+
+ @Override
+ public void setFileOpener(ResourceAccessor resourceAccessor) {
+ }
+
+ @Override
+ public ValidationErrors validate(Database database) {
+ return null;
+ }
+}
diff --git a/liquibase-database-upgrade/src/main/java/org/apache/ranger/db/upgrade/patches/ranger/SampleLiquibasePatchForOzoneServiceDefUpdateJ10041.java b/liquibase-database-upgrade/src/main/java/org/apache/ranger/db/upgrade/patches/ranger/SampleLiquibasePatchForOzoneServiceDefUpdateJ10041.java
new file mode 100644
index 0000000000..f1ae77457f
--- /dev/null
+++ b/liquibase-database-upgrade/src/main/java/org/apache/ranger/db/upgrade/patches/ranger/SampleLiquibasePatchForOzoneServiceDefUpdateJ10041.java
@@ -0,0 +1,371 @@
+/*
+ * 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.ranger.db.upgrade.patches.ranger;
+
+import liquibase.change.custom.CustomTaskChange;
+import liquibase.change.custom.CustomTaskRollback;
+import liquibase.database.Database;
+import liquibase.exception.CustomChangeException;
+import liquibase.exception.RollbackImpossibleException;
+import liquibase.exception.SetupException;
+import liquibase.exception.ValidationErrors;
+import liquibase.resource.ResourceAccessor;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.ranger.biz.ServiceDBStore;
+import org.apache.ranger.common.JSONUtil;
+import org.apache.ranger.common.RangerValidatorFactory;
+import org.apache.ranger.db.RangerDaoManager;
+import org.apache.ranger.db.upgrade.SpringContext;
+import org.apache.ranger.entity.XXService;
+import org.apache.ranger.entity.XXServiceDef;
+import org.apache.ranger.plugin.model.RangerPolicy;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.model.validation.RangerServiceDefValidator;
+import org.apache.ranger.plugin.model.validation.RangerValidator;
+import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil;
+import org.apache.ranger.plugin.util.SearchFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.TransactionDefinition;
+import org.springframework.transaction.support.TransactionTemplate;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+// Sample for demonstration of how java patch PatchForOzoneServiceDefUpdate_J10041 can be ported into liquibase.
+// Instead of @Autowired, SpringContext.getBean(...) is used to initialize the spring components required in this class
+// Since @Transactional annotation cannot be used in this class, TransactionTemplate is used to start a transaction which is required to write into the database
+// rollback() method is only partially implemented here for demonstration purposes only and does not fully rollback changes in the execute() method
+@SuppressWarnings("PMD.JUnit4TestShouldUseBeforeAnnotation")
+public class SampleLiquibasePatchForOzoneServiceDefUpdateJ10041
+ implements CustomTaskChange, CustomTaskRollback {
+ private static final Logger logger = LoggerFactory.getLogger(
+ SampleLiquibasePatchForOzoneServiceDefUpdateJ10041.class);
+ private static final List OZONE_CONFIGS = new ArrayList<>(
+ Arrays.asList("dfs.datanode.kerberos.principal", "dfs.namenode.kerberos.principal", "dfs.secondary.namenode.kerberos.principal", "commonNameForCertificate"));
+ private static final String OZONE_RESOURCE_VOLUME = "volume";
+ private static final String OZONE_RESOURCE_KEY = "key";
+ private static final String ACCESS_TYPE_READ_ACL = "read_acl";
+ private static final String ACCESS_TYPE_WRITE_ACL = "write_acl";
+
+ //@Autowired
+ RangerDaoManager daoMgr = SpringContext.getBean(RangerDaoManager.class);
+ ServiceDBStore svcDBStore = SpringContext.getBean(ServiceDBStore.class);
+ JSONUtil jsonUtil = SpringContext.getBean(JSONUtil.class);
+ PlatformTransactionManager txManager = SpringContext.getBean("transactionManager");
+ RangerValidatorFactory validatorFactory = SpringContext.getBean(RangerValidatorFactory.class);
+
+ @Override
+ public void execute(Database database) throws CustomChangeException {
+ boolean isServiceDefUpdateSuccessful;
+ try {
+ TransactionTemplate txTemplate = new TransactionTemplate(txManager);
+ txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
+
+ isServiceDefUpdateSuccessful = Boolean.TRUE.equals(txTemplate.execute(status -> {
+ try {
+ return updateOzoneServiceDef();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }));
+ } catch (Exception e) {
+ throw new CustomChangeException(e);
+ }
+ if (!isServiceDefUpdateSuccessful) {
+ throw new CustomChangeException("Updating ozone service def was unsuccessful");
+ }
+ }
+
+ @Override
+ public String getConfirmationMessage() {
+ return null;
+ }
+
+ @Override
+ public void setUp() throws SetupException {
+ }
+
+ @Override
+ public void setFileOpener(ResourceAccessor resourceAccessor) {
+ }
+
+ @Override
+ public ValidationErrors validate(Database database) {
+ return null;
+ }
+
+ @Override
+ public void rollback(Database database)
+ throws CustomChangeException, RollbackImpossibleException {
+ boolean isServiceDefUpdateSuccessful;
+ try {
+ TransactionTemplate txTemplate = new TransactionTemplate(txManager);
+ txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
+ isServiceDefUpdateSuccessful = Boolean.TRUE.equals(txTemplate.execute(status -> {
+ try {
+ return rollbackOzoneServiceDef();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }));
+ } catch (Exception e) {
+ throw new CustomChangeException(e);
+ }
+ if (!isServiceDefUpdateSuccessful) {
+ throw new CustomChangeException("Rollback service def was unsuccessful");
+ }
+ }
+
+ protected Map jsonStringToMap(String jsonStr) {
+ Map ret = null;
+ if (!StringUtils.isEmpty(jsonStr)) {
+ try {
+ ret = jsonUtil.jsonToMap(jsonStr);
+ } catch (Exception ex) {
+ // fallback to earlier format: "name1=value1;name2=value2"
+ for (String optionString : jsonStr.split(";")) {
+ if (StringUtils.isEmpty(optionString)) {
+ continue;
+ }
+ String[] nvArr = optionString.split("=");
+ String name = (nvArr != null && nvArr.length > 0) ? nvArr[0].trim() : null;
+ String value = (nvArr != null && nvArr.length > 1) ? nvArr[1].trim() : null;
+ if (StringUtils.isEmpty(name)) {
+ continue;
+ }
+ if (ret == null) {
+ ret = new HashMap();
+ }
+ ret.put(name, value);
+ }
+ }
+ }
+ return ret;
+ }
+
+ private boolean rollbackOzoneServiceDef() throws Exception {
+ RangerServiceDef dbOzoneServiceDef = svcDBStore.getServiceDefByName(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_OZONE_NAME);
+ RangerServiceDef embeddedOzoneServiceDef = EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_OZONE_NAME);
+ List embeddedOzoneAccessTypes = embeddedOzoneServiceDef.getAccessTypes();
+ logger.info("Before deletion embeddedOzoneAccessTypes=" + embeddedOzoneAccessTypes);
+ logger.info("Before deletion dbOzoneServiceDef.accessTypes=" + dbOzoneServiceDef.getAccessTypes().toString());
+ embeddedOzoneAccessTypes.removeIf(item -> item.getItemId() == 101L);
+ logger.info("After deletion embeddedOzoneAccessTypes=" + embeddedOzoneAccessTypes);
+ boolean ret = false;
+ if (!embeddedOzoneAccessTypes.toString().equalsIgnoreCase(dbOzoneServiceDef.getAccessTypes().toString())) {
+ logger.info("Trying to write to DB new access types after deleting access type added suring update");
+ dbOzoneServiceDef.setAccessTypes(embeddedOzoneAccessTypes);
+ ret = true;
+ } else {
+ logger.info("Deletion of access type with id 101 not possible as not present. Rollback failed");
+ }
+ return ret;
+ }
+
+ private boolean updateOzoneServiceDef() throws Exception {
+ RangerServiceDef ret;
+ RangerServiceDef embeddedOzoneServiceDef;
+ RangerServiceDef dbOzoneServiceDef;
+ List embeddedOzoneConfigDefs;
+ List embeddedOzoneResourceDefs;
+ List embeddedOzoneAccessTypes;
+ XXServiceDef xXServiceDefObj;
+
+ embeddedOzoneServiceDef = EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_OZONE_NAME);
+ logger.info("EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_OZONE_NAME" + EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_OZONE_NAME);
+ if (embeddedOzoneServiceDef != null) {
+ if (daoMgr == null) {
+ logger.error("daoMgr is null");
+ } else if (daoMgr.getXXServiceDef() == null) {
+ logger.error("daoMgr.getXXServiceDef() is null");
+ } else if (daoMgr.getXXServiceDef().findByName(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_OZONE_NAME) == null) {
+ logger.error("daoMgr.getXXServiceDef().findByName(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_OZONE_NAME) is null");
+ } else {
+ logger.info("Nothing was null, all good");
+ }
+ xXServiceDefObj = daoMgr.getXXServiceDef().findByName(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_OZONE_NAME);
+ Map serviceDefOptionsPreUpdate;
+ String jsonPreUpdate;
+
+ if (xXServiceDefObj != null) {
+ jsonPreUpdate = xXServiceDefObj.getDefOptions();
+ serviceDefOptionsPreUpdate = jsonStringToMap(jsonPreUpdate);
+ } else {
+ logger.error("Ozone service-definition does not exist in the Ranger DAO. No patching is needed!!");
+ return true;
+ }
+ dbOzoneServiceDef = svcDBStore.getServiceDefByName(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_OZONE_NAME);
+
+ if (dbOzoneServiceDef != null) {
+ // Remove old Ozone configs
+ embeddedOzoneConfigDefs = embeddedOzoneServiceDef.getConfigs();
+ if (checkNotConfigPresent(embeddedOzoneConfigDefs)) {
+ dbOzoneServiceDef.setConfigs(embeddedOzoneConfigDefs);
+ }
+
+ // Update volume resource with recursive flag false and key resource with recursive flag true
+ embeddedOzoneResourceDefs = embeddedOzoneServiceDef.getResources();
+ if (checkVolKeyResUpdate(embeddedOzoneResourceDefs)) {
+ dbOzoneServiceDef.setResources(embeddedOzoneResourceDefs);
+ }
+
+ // Add new access types
+ embeddedOzoneAccessTypes = embeddedOzoneServiceDef.getAccessTypes();
+ embeddedOzoneAccessTypes.add(new RangerServiceDef.RangerAccessTypeDef(101L, "liquibase_java_patch", "liquibase_java_patch", "liquibase_java_patch", new ArrayList<>()));
+
+ if (embeddedOzoneAccessTypes != null) {
+ if (checkAccessTypesPresent(embeddedOzoneAccessTypes)) {
+ if (!embeddedOzoneAccessTypes.toString().equalsIgnoreCase(dbOzoneServiceDef.getAccessTypes().toString())) {
+ dbOzoneServiceDef.setAccessTypes(embeddedOzoneAccessTypes);
+ }
+ }
+ }
+ } else {
+ logger.error("Ozone service-definition does not exist in the db store.");
+ return false;
+ }
+ RangerServiceDefValidator validator = validatorFactory.getServiceDefValidator(svcDBStore);
+ validator.validate(dbOzoneServiceDef, RangerValidator.Action.UPDATE);
+
+ ret = svcDBStore.updateServiceDef(dbOzoneServiceDef);
+ if (ret == null) {
+ throw new RuntimeException("Error while updating " + EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_OZONE_NAME + " service-def");
+ }
+ xXServiceDefObj = daoMgr.getXXServiceDef().findByName(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_OZONE_NAME);
+ if (xXServiceDefObj != null) {
+ String jsonStrPostUpdate = xXServiceDefObj.getDefOptions();
+ Map serviceDefOptionsPostUpdate = jsonStringToMap(jsonStrPostUpdate);
+ if (serviceDefOptionsPostUpdate != null && serviceDefOptionsPostUpdate.containsKey(RangerServiceDef.OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES)) {
+ if (serviceDefOptionsPreUpdate == null || !serviceDefOptionsPreUpdate.containsKey(RangerServiceDef.OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES)) {
+ String preUpdateValue = serviceDefOptionsPreUpdate == null ? null : serviceDefOptionsPreUpdate.get(RangerServiceDef.OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES);
+ if (preUpdateValue == null) {
+ serviceDefOptionsPostUpdate.remove(RangerServiceDef.OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES);
+ } else {
+ serviceDefOptionsPostUpdate.put(RangerServiceDef.OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES, preUpdateValue);
+ }
+ xXServiceDefObj.setDefOptions(mapToJsonString(serviceDefOptionsPostUpdate));
+ daoMgr.getXXServiceDef().update(xXServiceDefObj);
+ }
+ }
+ } else {
+ logger.error("Ozone service-definition does not exist in the Ranger DAO.");
+ return false;
+ }
+ List dbServices = daoMgr.getXXService().findByServiceDefId(embeddedOzoneServiceDef.getId());
+ if (CollectionUtils.isNotEmpty(dbServices)) {
+ for (XXService dbService : dbServices) {
+ SearchFilter filter = new SearchFilter();
+ filter.setParam(SearchFilter.SERVICE_NAME, dbService.getName());
+ updateExisitngOzonePolicies(svcDBStore.getServicePolicies(dbService.getId(), filter));
+ }
+ }
+ } else {
+ logger.error("The embedded Ozone service-definition does not exist.");
+ return false;
+ }
+ return true;
+ }
+
+ private boolean checkNotConfigPresent(List configDefs) {
+ boolean ret = false;
+ List configNames = new ArrayList<>();
+ for (RangerServiceDef.RangerServiceConfigDef configDef : configDefs) {
+ configNames.add(configDef.getName());
+ }
+ for (String delConfig : OZONE_CONFIGS) {
+ if (!configNames.contains(delConfig)) {
+ ret = true;
+ break;
+ }
+ }
+ return ret;
+ }
+
+ private boolean checkVolKeyResUpdate(List embeddedOzoneResDefs) {
+ boolean ret = false;
+ for (RangerServiceDef.RangerResourceDef resDef : embeddedOzoneResDefs) {
+ if ((resDef.getName().equals(OZONE_RESOURCE_VOLUME) && (!resDef.getRecursiveSupported() || resDef.getExcludesSupported())) ||
+ (resDef.getName().equals(OZONE_RESOURCE_KEY) && resDef.getRecursiveSupported())) {
+ ret = true;
+ break;
+ }
+ }
+ return ret;
+ }
+
+ private boolean checkAccessTypesPresent(List embeddedOzoneAccessTypes) {
+ boolean ret = false;
+ for (RangerServiceDef.RangerAccessTypeDef accessDef : embeddedOzoneAccessTypes) {
+ if (ACCESS_TYPE_READ_ACL.equals(accessDef.getName()) || ACCESS_TYPE_WRITE_ACL.equals(accessDef.getName())) {
+ ret = true;
+ break;
+ }
+ }
+ return ret;
+ }
+
+ private void updateExisitngOzonePolicies(List policies) throws Exception {
+ if (CollectionUtils.isNotEmpty(policies)) {
+ for (RangerPolicy policy : policies) {
+ List policyItems = policy.getPolicyItems();
+ if (CollectionUtils.isNotEmpty(policyItems)) {
+ for (RangerPolicy.RangerPolicyItem policyItem : policyItems) {
+ List policyItemAccesses = policyItem.getAccesses();
+ // Add new access types
+ policyItemAccesses.add(new RangerPolicy.RangerPolicyItemAccess("read_acl"));
+ policyItemAccesses.add(new RangerPolicy.RangerPolicyItemAccess("write_acl"));
+ policyItem.setAccesses(policyItemAccesses);
+ }
+ }
+ Map policyResources = policy.getResources();
+ if (MapUtils.isNotEmpty(policyResources)) {
+ if (policyResources.containsKey(OZONE_RESOURCE_VOLUME)) {
+ // Set recursive flag as false for volume resource
+ policyResources.get(OZONE_RESOURCE_VOLUME).setIsRecursive(false);
+ // Set exclude support flag as true for volume resource
+ policyResources.get(OZONE_RESOURCE_VOLUME).setIsExcludes(false);
+ }
+ if (policyResources.containsKey(OZONE_RESOURCE_KEY)) {
+ // Set is recursive flag as true for volume resource
+ policyResources.get(OZONE_RESOURCE_KEY).setIsRecursive(true);
+ }
+ }
+ svcDBStore.updatePolicy(policy);
+ }
+ }
+ }
+
+ private String mapToJsonString(Map map) {
+ String ret = null;
+ if (map != null) {
+ try {
+ ret = jsonUtil.readMapToString(map);
+ } catch (Exception ex) {
+ logger.warn("mapToJsonString() failed to convert map: " + map, ex);
+ }
+ }
+ return ret;
+ }
+}
diff --git a/liquibase-database-upgrade/src/main/resources/kms_log4j.properties b/liquibase-database-upgrade/src/main/resources/kms_log4j.properties
new file mode 100644
index 0000000000..c3c535ba96
--- /dev/null
+++ b/liquibase-database-upgrade/src/main/resources/kms_log4j.properties
@@ -0,0 +1,39 @@
+#
+# 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. See accompanying LICENSE file.
+#
+# Define the File File Appender
+appender.file.type=File
+appender.file.name=FileAppender
+appender.file.fileName=/var/log/ranger/kms/ranger-liquibase-database-upgrade-${date:yyyy-MM-dd_HH-mm-ss}.log
+appender.file.layout.type=PatternLayout
+appender.file.layout.pattern=%d %d{Z} [%t] %-5p (%F:%L) - %m%n
+# Define the Console Appender
+appender.console.type=Console
+appender.console.name=ConsoleAppender
+appender.console.layout.type=PatternLayout
+appender.console.layout.pattern=%d %d{Z} [%t] %-5p (%F:%L) - %m%n
+# Define Loggers
+loggers=console, file
+logger.console.name=ConsoleLogger
+logger.console.level=info
+logger.console.additivity=false
+logger.console.appenderRef.console.ref=ConsoleAppender
+logger.file.name=FileLogger
+logger.file.level=info
+logger.file.additivity=false
+logger.file.appenderRef.file.ref=FileAppender
+# Define Root Logger
+rootLogger.level=info
+rootLogger.appenderRefs=console, file
+rootLogger.appenderRef.console.ref=ConsoleAppender
+rootLogger.appenderRef.file.ref=FileAppender
diff --git a/liquibase-database-upgrade/src/main/resources/ranger_log4j.properties b/liquibase-database-upgrade/src/main/resources/ranger_log4j.properties
new file mode 100644
index 0000000000..0d274ad177
--- /dev/null
+++ b/liquibase-database-upgrade/src/main/resources/ranger_log4j.properties
@@ -0,0 +1,39 @@
+#
+# 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. See accompanying LICENSE file.
+#
+# Define the File File Appender
+appender.file.type=File
+appender.file.name=FileAppender
+appender.file.fileName=/var/log/ranger/admin/ranger-liquibase-database-upgrade-${date:yyyy-MM-dd_HH-mm-ss}.log
+appender.file.layout.type=PatternLayout
+appender.file.layout.pattern=%d %d{Z} [%t] %-5p (%F:%L) - %m%n
+# Define the Console Appender
+appender.console.type=Console
+appender.console.name=ConsoleAppender
+appender.console.layout.type=PatternLayout
+appender.console.layout.pattern=%d %d{Z} [%t] %-5p (%F:%L) - %m%n
+# Define Loggers
+loggers=console, file
+logger.console.name=ConsoleLogger
+logger.console.level=info
+logger.console.additivity=false
+logger.console.appenderRef.console.ref=ConsoleAppender
+logger.file.name=FileLogger
+logger.file.level=info
+logger.file.additivity=false
+logger.file.appenderRef.file.ref=FileAppender
+# Define Root Logger
+rootLogger.level=info
+rootLogger.appenderRefs=console, file
+rootLogger.appenderRef.console.ref=ConsoleAppender
+rootLogger.appenderRef.file.ref=FileAppender
diff --git a/liquibase-database-upgrade/src/test/java/org/apache/ranger/db/upgrade/DummyConfigProviderForTest.java b/liquibase-database-upgrade/src/test/java/org/apache/ranger/db/upgrade/DummyConfigProviderForTest.java
new file mode 100644
index 0000000000..cf8298c3ff
--- /dev/null
+++ b/liquibase-database-upgrade/src/test/java/org/apache/ranger/db/upgrade/DummyConfigProviderForTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.ranger.db.upgrade;
+
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+import java.net.URL;
+
+@Component("test")
+@Lazy
+public class DummyConfigProviderForTest implements IConfigProvider {
+ public static String getResourceFilePath(String resourceRelativePath) {
+ // Get the resource URL
+ ClassLoader classLoader = DummyConfigProviderForTest.class.getClassLoader();
+ URL resourceURL = classLoader.getResource(resourceRelativePath);
+ if (resourceURL == null) {
+ throw new IllegalArgumentException("Resource not found: " + resourceRelativePath);
+ }
+ return resourceURL.getFile();
+ }
+
+ @Override
+ public String getUrl() {
+ return "url";
+ }
+
+ @Override
+ public String getUsername() {
+ return "user";
+ }
+
+ @Override
+ public String getPassword() {
+ return "pwd";
+ }
+
+ @Override
+ public String getDriver() {
+ return "driver";
+ }
+
+ @Override
+ public String getMasterChangelogRelativePath() {
+ return getResourceFilePath("masterChangelogForTest.txt");
+ }
+
+ @Override
+ public String getFinalizeChangelogRelativePath() {
+ return getResourceFilePath("finalizeChangelogForTest.txt");
+ }
+}
diff --git a/liquibase-database-upgrade/src/test/java/org/apache/ranger/db/upgrade/FileDBForTest.java b/liquibase-database-upgrade/src/test/java/org/apache/ranger/db/upgrade/FileDBForTest.java
new file mode 100644
index 0000000000..db719e6c83
--- /dev/null
+++ b/liquibase-database-upgrade/src/test/java/org/apache/ranger/db/upgrade/FileDBForTest.java
@@ -0,0 +1,185 @@
+/*
+ * 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.ranger.db.upgrade;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FileDBForTest {
+ private FileDBForTest() {
+ }
+
+ public static void deleteFileIfExists(String filename) {
+ File file = new File(filename);
+ if (file.exists()) {
+ if (file.delete()) {
+ System.out.println("File deleted successfully: " + filename);
+ } else {
+ System.out.println("Failed to delete the file: " + filename);
+ }
+ } else {
+ System.out.println("File does not exist: " + filename);
+ }
+ }
+
+ public static boolean tagExists(String filePath, String tagName) {
+ createFileIfNotExists(filePath);
+ try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ String[] changeLogRow = line.split(",");
+ if (changeLogRow[changeLogRow.length - 1].equals(tagName)) {
+ return true; // String found
+ }
+ }
+ } catch (IOException e) {
+ System.err.println("Error reading file: " + e.getMessage());
+ }
+ return false; // String not found
+ }
+
+ public static void tag(String filePath, String tagName) {
+ createFileIfNotExists(filePath);
+ if (isFileEmpty(filePath)) {
+ List lines = new ArrayList<>();
+ lines.add(",,," + tagName);
+ writeLines(filePath, lines, true);
+ } else {
+ try {
+ readAndModifyLastLine(filePath, tagName);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ public static List readLines(String filePath) {
+ createFileIfNotExists(filePath);
+ List lines = new ArrayList<>();
+ try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ lines.add(line);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return lines;
+ }
+
+ public static void writeLines(String filePath, List lines, boolean doAppend) {
+ createFileIfNotExists(filePath);
+ try (FileWriter writer = new FileWriter(filePath, doAppend)) { // 'true' enables appending
+ for (String line : lines) {
+ writer.write(line);
+ writer.write("\n");
+ }
+ System.out.println("File=" + filePath + " Lines written to file= " + lines);
+ } catch (IOException e) {
+ System.err.println("Error appending to file: " + e.getMessage());
+ }
+ }
+
+ public static void removeLinesAfterTag(String filePath, String tagName) throws Exception {
+ createFileIfNotExists(filePath);
+ int lastLineWithTag = -1;
+ List lines = readLines(filePath);
+ for (int i = 0; i < lines.size(); i++) {
+ String[] changeLogRow = lines.get(i).split(",");
+ if (changeLogRow[changeLogRow.length - 1].equals(tagName)) {
+ lastLineWithTag = i;
+ }
+ }
+ System.out.println("lastLineWithTag " + tagName + " = " + lastLineWithTag);
+ if (lastLineWithTag == -1) {
+ throw new Exception("Couldn't find Tag " + tagName + " in file, can't remove lines");
+ }
+ lines = lines.subList(0, lastLineWithTag + 1);
+
+ writeLines(filePath, lines, false);
+ }
+
+ private static boolean isFileEmpty(String filePath) {
+ File file = new File(filePath);
+ // Check if file exists and its size is 0
+ return file.exists() && file.isFile() && file.length() == 0;
+ }
+
+ private static void createFileIfNotExists(String filePath) {
+ File file = new File(filePath);
+ try {
+ // Check if the file already exists
+ if (file.exists()) {
+ System.out.println("File already exists: " + file.getAbsolutePath());
+ } else {
+ // Create the file if it does not exist
+ if (file.createNewFile()) {
+ System.out.println("File created successfully: " + file.getAbsolutePath());
+ } else {
+ System.out.println("Failed to create the file.");
+ }
+ }
+ } catch (IOException e) {
+ // Handle exceptions
+ System.out.println("An error occurred while creating the file.");
+ e.printStackTrace();
+ }
+ }
+
+ private static void readAndModifyLastLine(String filePath, String tagName) throws Exception {
+ File file = new File(filePath);
+ if (!file.exists() || !file.isFile()) {
+ throw new FileNotFoundException("File does not exist or is not valid: " + filePath);
+ }
+
+ // Step 1: Read all lines into a list
+ List lines = readLines(filePath);
+
+ if (lines.isEmpty()) {
+ System.out.println("The file is empty. Nothing to modify.");
+ return;
+ }
+
+ // Step 2: Modify the last line
+ int lastIndex = lines.size() - 1;
+ String lastLine = lines.get(lastIndex);
+ String[] lastLineComponents = lastLine.split(",");
+ if (lastLineComponents.length == 4) {
+ //replace existing tag
+ lastLineComponents[3] = tagName;
+ lastLine = String.join(",", lastLineComponents);
+ } else if (lastLineComponents.length == 3) {
+ //add new tag to last line
+ lastLine += ("," + tagName);
+ } else {
+ throw new Exception("Unhandled scenario for test case. Modify the logic if changes made to sample changelog file");
+ }
+
+ lines.set(lastIndex, lastLine);
+
+ // Step 3: Write the modified content back to the file
+ writeLines(filePath, lines, false);
+
+ System.out.println("Successfully modified the last line.");
+ }
+}
diff --git a/liquibase-database-upgrade/src/test/java/org/apache/ranger/db/upgrade/LiquibaseCommandDriverFileBasedForTest.java b/liquibase-database-upgrade/src/test/java/org/apache/ranger/db/upgrade/LiquibaseCommandDriverFileBasedForTest.java
new file mode 100644
index 0000000000..477a7d2273
--- /dev/null
+++ b/liquibase-database-upgrade/src/test/java/org/apache/ranger/db/upgrade/LiquibaseCommandDriverFileBasedForTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.ranger.db.upgrade;
+
+import liquibase.command.CommandResults;
+import liquibase.command.CommandScope;
+import liquibase.exception.CommandExecutionException;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+@Component
+public class LiquibaseCommandDriverFileBasedForTest implements ICommandDriver {
+ @Value("${test_db_file.name:test.txt}")
+ private String filename;
+
+ @Override
+ public CommandResults tag(String serviceName, String tagName) throws CommandExecutionException {
+ FileDBForTest.tag(filename, tagName);
+ return getCommandResultsObject(new TreeMap<>(), new CommandScope("tag"));
+ }
+
+ @Override
+ public CommandResults rollback(String serviceName, String tagName, String changelogFilename) throws CommandExecutionException {
+ try {
+ FileDBForTest.removeLinesAfterTag(filename, tagName);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return getCommandResultsObject(new TreeMap<>(), new CommandScope("rollback"));
+ }
+
+ @Override
+ public CommandResults tagExists(String serviceName, String tagName) throws CommandExecutionException {
+ SortedMap result = new TreeMap<>();
+ if (FileDBForTest.tagExists(filename, tagName)) {
+ result.put("tagExistsResult", true);
+ } else {
+ result.put("tagExistsResult", false);
+ }
+ return getCommandResultsObject(result, new CommandScope("tagExists"));
+ }
+
+ @Override
+ public CommandResults update(String serviceName, String changelogFilename) throws CommandExecutionException {
+ List lines = FileDBForTest.readLines(changelogFilename);
+ System.out.println("lines=" + lines);
+ FileDBForTest.writeLines(filename, lines, true);
+ return getCommandResultsObject(new TreeMap<>(), new CommandScope("update"));
+ }
+
+ @Override
+ public CommandResults status(String serviceName, String changelogFilename) throws CommandExecutionException {
+ SortedMap result = new TreeMap<>();
+ int numRows = FileDBForTest.readLines(filename).size();
+ result.put("numRows", numRows);
+ return getCommandResultsObject(result, new CommandScope("status"));
+ }
+
+ private CommandResults getCommandResultsObject(SortedMap resultValues, CommandScope commandScope) {
+ Class> clazz = CommandResults.class;
+ Constructor> constructor = null;
+ try {
+ constructor = clazz.getDeclaredConstructor(SortedMap.class, CommandScope.class);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ }
+ constructor.setAccessible(true);
+ try {
+ return (CommandResults) constructor.newInstance(resultValues, commandScope);
+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/liquibase-database-upgrade/src/test/java/org/apache/ranger/db/upgrade/TestLiquibaseUpdateDriverMain.java b/liquibase-database-upgrade/src/test/java/org/apache/ranger/db/upgrade/TestLiquibaseUpdateDriverMain.java
new file mode 100644
index 0000000000..6ce33c6fd6
--- /dev/null
+++ b/liquibase-database-upgrade/src/test/java/org/apache/ranger/db/upgrade/TestLiquibaseUpdateDriverMain.java
@@ -0,0 +1,141 @@
+/*
+ * 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.ranger.db.upgrade;
+
+import liquibase.command.CommandResults;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+
+import static org.mockito.Mockito.mockStatic;
+
+@SuppressWarnings("PMD")
+class TestLiquibaseUpdateDriverMain {
+ static String testFile = "test.txt";
+
+ @BeforeAll
+ static void setup() {
+ FileDBForTest.deleteFileIfExists(testFile);
+ }
+
+ @AfterAll
+ static void cleanup() {
+ FileDBForTest.deleteFileIfExists(testFile);
+ }
+
+ @Test
+ void testUpgradeSucceed() {
+ System.out.println("===== Test Update====");
+
+ FileDBForTest.deleteFileIfExists(testFile);
+ try {
+ try (MockedStatic liquibaseCommandResultsUtilMock = mockStatic(LiquibaseCommandResultsUtil.class)) {
+ liquibaseCommandResultsUtilMock.when(() -> LiquibaseCommandResultsUtil.isUpdateSuccessful(Mockito.any(CommandResults.class))).thenReturn(true);
+ System.setProperty("applicationContext.files", "liquibaseTestApplicationContext.xml");
+ System.setProperty("test_db_file.name", testFile);
+ LiquibaseUpdateDriverMain.main(new String[] {"-serviceName", "test", "-tag", "test_3.x", "-op", "update"});
+ ICommandDriver driver = SpringContext.getBean(ICommandDriver.class);
+ IConfigProvider configProvider = SpringContext.getBean("test");
+ Assertions.assertNotNull(configProvider);
+ Assertions.assertNotNull(driver);
+
+ CommandResults result = driver.status("test", configProvider.getMasterChangelogRelativePath());
+ int numRows = (int) result.getResults().get("numRows");
+ Assertions.assertEquals(4, numRows);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ System.out.println("===== Test Update Complete====");
+ }
+
+ @Test
+ void testRollback() {
+ System.out.println("===== Test Rollback====");
+ FileDBForTest.deleteFileIfExists(testFile);
+ try {
+ try (MockedStatic liquibaseCommandResultsUtilMock = mockStatic(LiquibaseCommandResultsUtil.class)) {
+ liquibaseCommandResultsUtilMock.when(() -> LiquibaseCommandResultsUtil.isUpdateSuccessful(Mockito.any(CommandResults.class))).thenReturn(true);
+ liquibaseCommandResultsUtilMock.when(() -> LiquibaseCommandResultsUtil.doesTagExist(Mockito.any(CommandResults.class))).thenCallRealMethod();
+ System.setProperty("applicationContext.files", "liquibaseTestApplicationContext.xml");
+ System.setProperty("test_db_file.name", testFile);
+ LiquibaseUpdateDriverMain.main(new String[] {"-serviceName", "test", "-tag", "test_3.x", "-op", "update"});
+ LiquibaseUpdateDriverMain.main(new String[] {"-serviceName", "test", "-tag", "test_3.x", "-op", "rollback"});
+ ICommandDriver driver = SpringContext.getBean(ICommandDriver.class);
+ IConfigProvider configProvider = SpringContext.getBean("test");
+ CommandResults result = driver.status("test", configProvider.getMasterChangelogRelativePath());
+ int numRows = (int) result.getResults().get("numRows");
+ Assertions.assertEquals(1, numRows);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ System.out.println("===== Test Rollback Complete====");
+ }
+
+ @Test
+ void testFinalize() {
+ System.out.println("===== Test Finalize ======");
+ FileDBForTest.deleteFileIfExists(testFile);
+ try {
+ try (MockedStatic liquibaseCommandResultsUtilMock = mockStatic(LiquibaseCommandResultsUtil.class)) {
+ liquibaseCommandResultsUtilMock.when(() -> LiquibaseCommandResultsUtil.isUpdateSuccessful(Mockito.any(CommandResults.class))).thenReturn(true);
+ liquibaseCommandResultsUtilMock.when(() -> LiquibaseCommandResultsUtil.doesTagExist(Mockito.any(CommandResults.class))).thenCallRealMethod();
+ System.setProperty("applicationContext.files", "liquibaseTestApplicationContext.xml");
+ System.setProperty("test_db_file.name", testFile);
+ LiquibaseUpdateDriverMain.main(new String[] {"-serviceName", "test", "-tag", "test_3.x", "-op", "update"});
+ LiquibaseUpdateDriverMain.main(new String[] {"-serviceName", "test", "-tag", "test_3.x", "-op", "finalize"});
+ ICommandDriver driver = SpringContext.getBean(ICommandDriver.class);
+ IConfigProvider configProvider = SpringContext.getBean("test");
+
+ Assertions.assertNotNull(configProvider, "ConfigProvider should not be null after initialization");
+ Assertions.assertNotNull(driver, "CommandDriver should not be null after initialization");
+
+ CommandResults result = driver.status("test", configProvider.getMasterChangelogRelativePath());
+ int numRows = (int) result.getResults().get("numRows");
+ Assertions.assertEquals(7, numRows);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ System.out.println("===== Test Finalize complete =====");
+ }
+
+ @Test
+ void testFinalizeAfterRollbackShouldFail() {
+ System.out.println("===== Test Finalize after Rollback Should Fail======");
+ FileDBForTest.deleteFileIfExists(testFile);
+ try {
+ try (MockedStatic liquibaseCommandResultsUtilMock = mockStatic(LiquibaseCommandResultsUtil.class)) {
+ liquibaseCommandResultsUtilMock.when(() -> LiquibaseCommandResultsUtil.isUpdateSuccessful(Mockito.any(CommandResults.class))).thenReturn(true);
+ liquibaseCommandResultsUtilMock.when(() -> LiquibaseCommandResultsUtil.doesTagExist(Mockito.any(CommandResults.class))).thenCallRealMethod();
+ System.setProperty("applicationContext.files", "liquibaseTestApplicationContext.xml");
+ System.setProperty("test_db_file.name", testFile);
+ LiquibaseUpdateDriverMain.main(new String[] {"-serviceName", "test", "-tag", "test_3.x", "-op", "update"});
+ LiquibaseUpdateDriverMain.main(new String[] {"-serviceName", "test", "-tag", "test_3.x", "-op", "rollback"});
+ LiquibaseUpdateDriverMain.main(new String[] {"-serviceName", "test", "-tag", "test_3.x", "-op", "finalize"});
+ Assertions.fail("Failed: Code flow should not have reached here. Finalize not allowed after rollback");
+ }
+ } catch (Exception e) {
+ // expected
+ }
+ System.out.println("===== Test Finalize after Rollback Should Fail Complete======");
+ }
+}
diff --git a/liquibase-database-upgrade/src/test/resources/finalizeChangelogForTest.txt b/liquibase-database-upgrade/src/test/resources/finalizeChangelogForTest.txt
new file mode 100644
index 0000000000..d691b6ba02
--- /dev/null
+++ b/liquibase-database-upgrade/src/test/resources/finalizeChangelogForTest.txt
@@ -0,0 +1,3 @@
+id4,author1,changeset4.xml
+id5,author2,changeset5.xml
+id6,author3,changeset6.xml
\ No newline at end of file
diff --git a/liquibase-database-upgrade/src/test/resources/liquibaseTestApplicationContext.xml b/liquibase-database-upgrade/src/test/resources/liquibaseTestApplicationContext.xml
new file mode 100644
index 0000000000..58f15d4d6d
--- /dev/null
+++ b/liquibase-database-upgrade/src/test/resources/liquibaseTestApplicationContext.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/liquibase-database-upgrade/src/test/resources/log4j2-test.xml b/liquibase-database-upgrade/src/test/resources/log4j2-test.xml
new file mode 100644
index 0000000000..b310aced64
--- /dev/null
+++ b/liquibase-database-upgrade/src/test/resources/log4j2-test.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/liquibase-database-upgrade/src/test/resources/masterChangelogForTest.txt b/liquibase-database-upgrade/src/test/resources/masterChangelogForTest.txt
new file mode 100644
index 0000000000..6f10470d9e
--- /dev/null
+++ b/liquibase-database-upgrade/src/test/resources/masterChangelogForTest.txt
@@ -0,0 +1,3 @@
+id1,author1,changeset1.xml
+id2,author2,changeset2.xml
+id3,author3,changeset3.xml
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
old mode 100755
new mode 100644
index f15d440bb8..62f70c3efc
--- a/pom.xml
+++ b/pom.xml
@@ -28,6 +28,9 @@
pom
ranger
Security for Enforcing Enterprise Policies
+
+ liquibase-database-upgrade
+
4.0.3
0.27
@@ -137,6 +140,7 @@
2.0.1.Final
2.3.3
2.3.1
+ 1.82
3.3
1.19.4
2.35
@@ -162,6 +166,7 @@
4.0.4
1.10
0.14.0
+ 4.29.0
${project.basedir}/../lib/local
2.17.2
1.3.14
diff --git a/security-admin/pom.xml b/security-admin/pom.xml
index b317202f5e..8b7a099343 100644
--- a/security-admin/pom.xml
+++ b/security-admin/pom.xml
@@ -875,6 +875,9 @@
org.apache.maven.plugins
maven-war-plugin
3.4.0
+
+ true
+
prepare