diff --git a/plugin/src/main/java/org/elasticsearch/xpack/security/support/IndexLifecycleManager.java b/plugin/src/main/java/org/elasticsearch/xpack/security/support/IndexLifecycleManager.java index daa5156d260..6a67fa23754 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/security/support/IndexLifecycleManager.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/security/support/IndexLifecycleManager.java @@ -49,7 +49,7 @@ import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY public class IndexLifecycleManager extends AbstractComponent { public static final String INTERNAL_SECURITY_INDEX = ".security-v6"; - private static final int INTERNAL_INDEX_FORMAT = 6; + public static final int INTERNAL_INDEX_FORMAT = 6; private static final String SECURITY_VERSION_STRING = "security-version"; public static final String TEMPLATE_VERSION_PATTERN = Pattern.quote("${security.template.version}"); @@ -69,10 +69,6 @@ public class IndexLifecycleManager extends AbstractComponent { private volatile boolean mappingIsUpToDate; private volatile Version mappingVersion; - public enum UpgradeState { - NOT_STARTED, IN_PROGRESS, COMPLETE, FAILED - } - public IndexLifecycleManager(Settings settings, InternalClient client, String indexName, String templateName) { super(settings); this.client = client; @@ -124,7 +120,7 @@ public class IndexLifecycleManager extends AbstractComponent { this.templateIsUpToDate = TemplateUtils.checkTemplateExistsAndIsUpToDate(templateName, SECURITY_VERSION_STRING, state, logger); this.mappingIsUpToDate = checkIndexMappingUpToDate(state); - this.canWriteToIndex = templateIsUpToDate && mappingIsUpToDate; + this.canWriteToIndex = templateIsUpToDate && (mappingIsUpToDate || isIndexUpToDate); this.mappingVersion = oldestIndexMappingVersion(state); } diff --git a/qa/full-cluster-restart/build.gradle b/qa/full-cluster-restart/build.gradle index 94d70195460..40fe9e70a48 100644 --- a/qa/full-cluster-restart/build.gradle +++ b/qa/full-cluster-restart/build.gradle @@ -125,12 +125,17 @@ subprojects { dependsOn copyTestNodeKeystore extraConfigFile 'testnode.jks', new File(outputDir + '/testnode.jks') if (withSystemKey) { - if (version.onOrAfter('5.1.0')) { + if (version.onOrAfter('5.1.0') && version.before('6.0.0')) { // The setting didn't exist until 5.1.0 setting 'xpack.security.system_key.required', 'true' } - extraConfigFile 'x-pack/system_key', - "${mainProject.projectDir}/src/test/resources/system_key" + if (version.onOrAfter('6.0.0')) { + setupCommand 'create-elasticsearch-keystore', 'bin/elasticsearch-keystore', 'create' + setupCommand 'add-key-elasticsearch-keystore', 'bin/elasticsearch-keystore', 'add-file', 'xpack.watcher.encryption_key', + "${mainProject.projectDir}/src/test/resources/system_key" + } else { + extraConfigFile 'x-pack/system_key', "${mainProject.projectDir}/src/test/resources/system_key" + } setting 'xpack.watcher.encrypt_sensitive_data', 'true' } } diff --git a/qa/full-cluster-restart/src/test/java/org/elasticsearch/xpack/restart/FullClusterRestartIT.java b/qa/full-cluster-restart/src/test/java/org/elasticsearch/xpack/restart/FullClusterRestartIT.java index 386a289fe27..62f49e25e55 100644 --- a/qa/full-cluster-restart/src/test/java/org/elasticsearch/xpack/restart/FullClusterRestartIT.java +++ b/qa/full-cluster-restart/src/test/java/org/elasticsearch/xpack/restart/FullClusterRestartIT.java @@ -21,6 +21,7 @@ import org.elasticsearch.test.StreamsUtils; import org.elasticsearch.test.rest.ESRestTestCase; import org.elasticsearch.xpack.common.text.TextTemplate; import org.elasticsearch.xpack.security.SecurityClusterClientYamlTestCase; +import org.elasticsearch.xpack.security.support.IndexLifecycleManager; import org.elasticsearch.xpack.test.rest.XPackRestTestCase; import org.elasticsearch.xpack.watcher.actions.logging.LoggingAction; import org.elasticsearch.xpack.watcher.client.WatchSourceBuilder; @@ -107,22 +108,50 @@ public class FullClusterRestartIT extends ESRestTestCase { assertThat(toStr(client().performRequest("GET", docLocation)), containsString(doc)); } + @SuppressWarnings("unchecked") public void testSecurityNativeRealm() throws Exception { if (runningAgainstOldCluster) { createUser("preupgrade_user"); createRole("preupgrade_role"); } else { waitForYellow(".security"); - // without upgrade, an error should be thrown - try { - createUser("postupgrade_user"); - fail("should not be able to add a user when upgrade hasn't taken place"); - } catch (ResponseException e) { - assertThat(e.getMessage(), containsString("Security index is not on the current version - " + - "the native realm will not be operational until the upgrade API is run on the security index")); + Response settingsResponse = client().performRequest("GET", "/.security/_settings/index.format"); + Map settingsResponseMap = toMap(settingsResponse); + logger.info("settings response map {}", settingsResponseMap); + final boolean needsUpgrade; + final String concreteSecurityIndex; + if (settingsResponseMap.isEmpty()) { + needsUpgrade = true; + concreteSecurityIndex = ".security"; + } else { + concreteSecurityIndex = settingsResponseMap.keySet().iterator().next(); + Map indexSettingsMap = + (Map) settingsResponseMap.get(concreteSecurityIndex); + Map settingsMap = (Map) indexSettingsMap.get("settings"); + logger.info("settings map {}", settingsMap); + if (settingsMap.containsKey("index")) { + int format = Integer.parseInt(String.valueOf(((Map)settingsMap.get("index")).get("format"))); + needsUpgrade = format == IndexLifecycleManager.INTERNAL_INDEX_FORMAT ? false : true; + } else { + needsUpgrade = true; + } } - // run upgrade API - client().performRequest("POST", "_xpack/migration/upgrade/.security"); + + if (needsUpgrade) { + logger.info("upgrading security index {}", concreteSecurityIndex); + // without upgrade, an error should be thrown + try { + createUser("postupgrade_user"); + fail("should not be able to add a user when upgrade hasn't taken place"); + } catch (ResponseException e) { + assertThat(e.getMessage(), containsString("Security index is not on the current version - " + + "the native realm will not be operational until the upgrade API is run on the security index")); + } + // run upgrade API + Response upgradeResponse = client().performRequest("POST", "_xpack/migration/upgrade/" + concreteSecurityIndex); + logger.info("upgrade response:\n{}", toStr(upgradeResponse)); + } + // create additional user and role createUser("postupgrade_user"); createRole("postupgrade_role"); @@ -159,28 +188,33 @@ public class FullClusterRestartIT extends ESRestTestCase { logger.info("testing against {}", oldClusterVersion); waitForYellow(".watches,bwc_watch_index,.watcher-history*"); - logger.info("checking that upgrade procedure on the new cluster is required"); + logger.info("checking if the upgrade procedure on the new cluster is required"); Map response = toMap(client().performRequest("GET", "/_xpack/migration/assistance")); logger.info(response); @SuppressWarnings("unchecked") Map indices = (Map) response.get("indices"); - assertThat(indices.entrySet().size(), greaterThanOrEqualTo(1)); - assertThat(indices.get(".watches"), notNullValue()); - @SuppressWarnings("unchecked") Map index = (Map) indices.get(".watches"); - assertThat(index.get("action_required"), equalTo("upgrade")); + if (indices.containsKey(".watches")) { + logger.info("upgrade procedure is required for watcher"); + assertThat(indices.entrySet().size(), greaterThanOrEqualTo(1)); + assertThat(indices.get(".watches"), notNullValue()); + @SuppressWarnings("unchecked") Map index = (Map) indices.get(".watches"); + assertThat(index.get("action_required"), equalTo("upgrade")); - logger.info("starting upgrade procedure on the new cluster"); + logger.info("starting upgrade procedure on the new cluster"); - Map params = Collections.singletonMap("error_trace", "true"); - Map upgradeResponse = toMap(client().performRequest("POST", "_xpack/migration/upgrade/.watches", params)); - assertThat(upgradeResponse.get("timed_out"), equalTo(Boolean.FALSE)); - // we posted 3 watches, but monitoring can post a few more - assertThat((int)upgradeResponse.get("total"), greaterThanOrEqualTo(3)); + Map params = Collections.singletonMap("error_trace", "true"); + Map upgradeResponse = toMap(client().performRequest("POST", "_xpack/migration/upgrade/.watches", params)); + assertThat(upgradeResponse.get("timed_out"), equalTo(Boolean.FALSE)); + // we posted 3 watches, but monitoring can post a few more + assertThat((int) upgradeResponse.get("total"), greaterThanOrEqualTo(3)); - logger.info("checking that upgrade procedure on the new cluster is no longer required"); - Map responseAfter = toMap(client().performRequest("GET", "/_xpack/migration/assistance")); - @SuppressWarnings("unchecked") Map indicesAfter = (Map) responseAfter.get("indices"); - assertNull(indicesAfter.get(".watches")); + logger.info("checking that upgrade procedure on the new cluster is no longer required"); + Map responseAfter = toMap(client().performRequest("GET", "/_xpack/migration/assistance")); + @SuppressWarnings("unchecked") Map indicesAfter = (Map) responseAfter.get("indices"); + assertNull(indicesAfter.get(".watches")); + } else { + logger.info("upgrade procedure is not required for watcher"); + } // Wait for watcher to actually start.... Map startWatchResponse = toMap(client().performRequest("POST", "_xpack/watcher/_start"));