/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License;
 * you may not use this file except in compliance with the Elastic License.
 */
package org.elasticsearch.xpack.security;

import org.apache.http.HttpEntity;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.Version;
import org.elasticsearch.client.Response;
import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase;
import org.elasticsearch.test.rest.yaml.ObjectPath;
import org.junit.Before;

import java.nio.charset.StandardCharsets;
import java.util.Map;

import static org.elasticsearch.xpack.security.SecurityLifecycleService.SECURITY_TEMPLATE_NAME;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;

/**
 * A base {@link ESClientYamlSuiteTestCase} test class for the security module,
 * which depends on security template and mappings being up to date before any writes
 * to the {@code .security} index can take place.
 */
public abstract class SecurityClusterClientYamlTestCase extends ESClientYamlSuiteTestCase {

    public SecurityClusterClientYamlTestCase(ClientYamlTestCandidate testCandidate) {
        super(testCandidate);
    }

    @Before
    public void waitForSecuritySetup() throws Exception {
        waitForSecurity();
    }

    public static void waitForSecurity() throws Exception {
        String masterNode = null;
        HttpEntity entity = client().performRequest("GET", "/_cat/nodes?h=id,master").getEntity();
        String catNodesResponse = EntityUtils.toString(entity, StandardCharsets.UTF_8);
        for (String line : catNodesResponse.split("\n")) {
            int indexOfStar = line.indexOf('*'); // * in the node's output denotes it is master
            if (indexOfStar != -1) {
                masterNode = line.substring(0, indexOfStar).trim();
                break;
            }
        }
        assertNotNull(masterNode);
        final String masterNodeId = masterNode;

        assertBusy(() -> {
            try {
                Response nodeDetailsResponse = client().performRequest("GET", "/_nodes");
                ObjectPath path = ObjectPath.createFromResponse(nodeDetailsResponse);
                Map<String, Object> nodes = path.evaluate("nodes");
                String masterVersion = null;
                for (String key : nodes.keySet()) {
                    // get the ES version number master is on
                    if (key.startsWith(masterNodeId)) {
                        masterVersion = path.evaluate("nodes." + key + ".version");
                        break;
                    }
                }
                assertNotNull(masterVersion);
                final String masterTemplateVersion = masterVersion;

                Response response = client().performRequest("GET", "/_cluster/state/metadata");
                ObjectPath objectPath = ObjectPath.createFromResponse(response);
                String mappingsPath = "metadata.templates." + SECURITY_TEMPLATE_NAME + ".mappings";
                Map<String, Object> mappings = objectPath.evaluate(mappingsPath);
                assertNotNull(mappings);
                assertThat(mappings.size(), greaterThanOrEqualTo(1));
                for (String key : mappings.keySet()) {
                    String templatePath = mappingsPath + "." + key + "._meta.security-version";
                    String templateVersion = objectPath.evaluate(templatePath);
                    final Version mVersion = Version.fromString(masterTemplateVersion);
                    final Version tVersion = Version.fromString(templateVersion);
                    assertTrue(mVersion.onOrBefore(tVersion));
                }
            } catch (Exception e) {
                throw new AssertionError("failed to get cluster state", e);
            }
        });
    }
}

@@ -282,84 +282,8 @@
 y;%0A%0A
-import org.apache.http.HttpEntity;%0Aimport org.apache.http.util.EntityUtils;%0A
 impo
@@ -580,36 +580,50 @@
 ava.
-nio.charset.StandardChars
+util.Collections;%0Aimport java.util.HashS
 et
-s
 ;%0Aim
@@ -641,16 +641,38 @@
 til.Map;
+%0Aimport java.util.Set;
 %0A%0Aimport
@@ -1428,620 +1428,8 @@
 n %7B%0A
-        String masterNode = null;%0A        HttpEntity entity = client().performRequest(%22GET%22, %22/_cat/nodes?h=id,master%22).getEntity();%0A        String catNodesResponse = EntityUtils.toString(entity, StandardCharsets.UTF_8);%0A        for (String line : catNodesResponse.split(%22%5Cn%22)) %7B%0A            int indexOfStar = line.indexOf('*'); // * in the node's output denotes it is master%0A            if (indexOfStar != -1) %7B%0A                masterNode = line.substring(0, indexOfStar).trim();%0A                break;%0A            %7D%0A        %7D%0A        assertNotNull(masterNode);%0A        final String masterNodeId = masterNode;%0A%0A
     
@@ -1494,30 +1494,24 @@
 esponse node
-Detail
 sResponse = 
@@ -1580,17 +1580,22 @@
 ectPath 
-p
+nodesP
 ath = Ob
@@ -1630,14 +1630,8 @@
 node
-Detail
 sRes
@@ -1682,17 +1682,22 @@
 nodes = 
-p
+nodesP
 ath.eval
@@ -1732,34 +1732,50 @@
    S
-tring master
+et%3CVersion%3E node
 Version
+s
  = n
-ull
+ew HashSet%3C%3E()
 ;%0A  
@@ -1800,19 +1800,22 @@
 (String 
-key
+nodeId
  : nodes
@@ -1851,147 +1851,132 @@
     
-// get the ES version number master is on%0A                    if (key.startsWith(masterNodeId)) %7B%0A                        masterVersion = p
+String nodeVersionPath = %22nodes.%22 + nodeId + %22.version%22;%0A                    Version nodeVersion = Version.fromString(nodesP
 ath.
@@ -1988,35 +1988,24 @@
 ate(
-%22
 node
-s.%22 + key + %22.v
+V
 ersion
-%22
+Path)
 );%0A 
@@ -2027,23 +2027,39 @@
     
-    break;%0A    
+nodeVersions.add(nodeVersion);%0A
     
@@ -2092,122 +2092,66 @@
     
-%7D%0A                assertNotNull(masterVersion);%0A                final String masterTemplateVersion = master
+Version highestNodeVersion = Collections.max(node
 Version
+s)
 ;%0A%0A 
@@ -2782,38 +2782,39 @@
                 
-String
+Version
  templateVersion
@@ -2816,16 +2816,35 @@
 rsion = 
+Version.fromString(
 objectPa
@@ -2864,24 +2864,25 @@
 emplatePath)
+)
 ;%0A          
@@ -2895,218 +2895,56 @@
     
-final Version mVersion = Version.fromString(masterTemplateVersion);%0A                    final Version tVersion = Version.fromString(templateVersion);%0A                    assertTrue(mVersion.onOrBefore(tVersion)
+assertEquals(highestNodeVersion, templateVersion
 );%0A 

fix security template version check in rest tests (elastic/x-pack-elasticsearch#2506)