DRILL-5996: Add ability to re-run queries from Profiles tab with impersonation and without authentication
closes #1061 Project: http://git-wip-us.apache.org/repos/asf/drill/repo Commit: http://git-wip-us.apache.org/repos/asf/drill/commit/e25c58f7 Tree: http://git-wip-us.apache.org/repos/asf/drill/tree/e25c58f7 Diff: http://git-wip-us.apache.org/repos/asf/drill/diff/e25c58f7 Branch: refs/heads/master Commit: e25c58f7bf0ad07d3611b85d6d82d05549a28791 Parents: ef4c63d Author: Arina Ielchiieva <[email protected]> Authored: Mon Dec 4 19:33:03 2017 +0200 Committer: Arina Ielchiieva <[email protected]> Committed: Fri Dec 22 13:36:18 2017 +0200 ---------------------------------------------------------------------- .../server/rest/profile/ProfileResources.java | 12 +++---- .../server/rest/profile/ProfileWrapper.java | 29 ++++++++++++---- .../src/main/resources/rest/profile/profile.ftl | 18 ++++++++-- .../src/main/resources/rest/query/query.ftl | 31 ++--------------- .../resources/rest/static/js/querySubmission.js | 36 ++++++++++++++++++++ 5 files changed, 82 insertions(+), 44 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/drill/blob/e25c58f7/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileResources.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileResources.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileResources.java index fdd391e..8751ee6 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileResources.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileResources.java @@ -123,17 +123,17 @@ public class ProfileResources { public String getLink() { return link; } - @Override - public int compareTo(ProfileInfo other) { - return time.compareTo(other.time); - } - public String getForeman() { return foreman; } public double getTotalCost() { return totalCost; } public String getQueueName() { return queueName; } + @Override + public int compareTo(ProfileInfo other) { + return time.compareTo(other.time); + } + /** * Generates link which will return query profile in json representation. * @@ -370,7 +370,7 @@ public class ProfileResources { @Path("/profiles/{queryid}") @Produces(MediaType.TEXT_HTML) public Viewable getProfile(@PathParam("queryid") String queryId){ - ProfileWrapper wrapper = new ProfileWrapper(getQueryProfile(queryId)); + ProfileWrapper wrapper = new ProfileWrapper(getQueryProfile(queryId), work.getContext().getConfig()); return ViewableWithPermissions.create(authEnabled.get(), "/rest/profile/profile.ftl", sc, wrapper); } http://git-wip-us.apache.org/repos/asf/drill/blob/e25c58f7/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileWrapper.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileWrapper.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileWrapper.java index 3a7d432..9c2b438 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileWrapper.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileWrapper.java @@ -1,4 +1,4 @@ -/** +/* * 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 @@ -26,6 +26,7 @@ import java.util.Map; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Maps; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.drill.common.config.DrillConfig; import org.apache.drill.exec.proto.UserBitShared.CoreOperatorType; import org.apache.drill.exec.proto.UserBitShared.MajorFragmentProfile; import org.apache.drill.exec.proto.UserBitShared.MinorFragmentProfile; @@ -35,6 +36,7 @@ import org.apache.drill.exec.proto.UserBitShared.QueryResult.QueryState; import org.apache.drill.exec.proto.helper.QueryIdHelper; import org.apache.drill.exec.server.options.OptionList; import org.apache.drill.exec.server.options.OptionValue; +import org.apache.drill.exec.server.rest.WebServer; import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT; @@ -47,15 +49,16 @@ public class ProfileWrapper { private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ProfileWrapper.class); private static final ObjectMapper mapper = new ObjectMapper().enable(INDENT_OUTPUT); - private QueryProfile profile; - private String id; + private final QueryProfile profile; + private final String id; private final List<FragmentWrapper> fragmentProfiles; private final List<OperatorWrapper> operatorProfiles; - private OptionList options; private final HashMap<String, Long> majorFragmentTallyMap; - private long majorFragmentTallyTotal; + private final long majorFragmentTallyTotal; + private final OptionList options; + private final boolean onlyImpersonationEnabled; - public ProfileWrapper(final QueryProfile profile) { + public ProfileWrapper(final QueryProfile profile, DrillConfig drillConfig) { this.profile = profile; this.id = QueryIdHelper.getQueryId(profile.getId()); @@ -68,7 +71,7 @@ public class ProfileWrapper { fragmentProfiles.add(new FragmentWrapper(major, profile.getStart())); } this.fragmentProfiles = fragmentProfiles; - majorFragmentTallyMap = new HashMap<String, Long>(majors.size()); + this.majorFragmentTallyMap = new HashMap<>(majors.size()); this.majorFragmentTallyTotal = tallyMajorFragmentCost(majors); final List<OperatorWrapper> ows = new ArrayList<>(); @@ -108,12 +111,16 @@ public class ProfileWrapper { } this.operatorProfiles = ows; + OptionList options; try { options = mapper.readValue(profile.getOptionsJson(), OptionList.class); } catch (Exception e) { logger.error("Unable to deserialize query options", e); options = new OptionList(); } + this.options = options; + + this.onlyImpersonationEnabled = WebServer.isImpersonationOnlyEnabled(drillConfig); } private long tallyMajorFragmentCost(List<MajorFragmentProfile> majorFragments) { @@ -297,4 +304,12 @@ public class ProfileWrapper { } return map; } + + /** + * @return true if impersonation is enabled without authentication, + * is needed to indicated if user name should be included when re-running the query + */ + public boolean isOnlyImpersonationEnabled() { + return onlyImpersonationEnabled; + } } http://git-wip-us.apache.org/repos/asf/drill/blob/e25c58f7/exec/java-exec/src/main/resources/rest/profile/profile.ftl ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/resources/rest/profile/profile.ftl b/exec/java-exec/src/main/resources/rest/profile/profile.ftl index ff78da3..1f38d2e 100644 --- a/exec/java-exec/src/main/resources/rest/profile/profile.ftl +++ b/exec/java-exec/src/main/resources/rest/profile/profile.ftl @@ -15,6 +15,10 @@ <script src="/static/js/dagre-d3.min.js"></script> <script src="/static/js/graph.js"></script> <script src="/static/js/jquery.dataTables-1.10.16.min.js"></script> +<#if model.isOnlyImpersonationEnabled()> + <script src="/static/js/jquery.form.js"></script> + <script src="/static/js/querySubmission.js"></script> +</#if> <script> var globalconfig = { @@ -71,7 +75,15 @@ table.sortable thead .sorting_desc { background-image: url("/static/img/black-de </div> <div id="query-edit" class="tab-pane"> <p> - <form role="form" action="/query" method="POST"> + + <#if model.isOnlyImpersonationEnabled()> + <div class="form-group"> + <label for="userName">User Name</label> + <input type="text" size="30" name="userName" id="userName" placeholder="User Name" value="${model.getProfile().user}"> + </div> + </#if> + + <form role="form" id="queryForm" action="/query" method="POST"> <div class="form-group"> <textarea class="form-control" id="query" name="query" style="font-family: Courier;">${model.getProfile().query}</textarea> </div> @@ -95,7 +107,9 @@ table.sortable thead .sorting_desc { background-image: url("/static/img/black-de </label> </div> </div> - <button type="submit" class="btn btn-default">Re-run query</button> + <button class="btn btn-default" type=<#if model.isOnlyImpersonationEnabled()>"button" onclick="doSubmitQueryWithUserName()"<#else>"submit"</#if>> + Re-run query + </button> </form> </p> <p> http://git-wip-us.apache.org/repos/asf/drill/blob/e25c58f7/exec/java-exec/src/main/resources/rest/query/query.ftl ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/resources/rest/query/query.ftl b/exec/java-exec/src/main/resources/rest/query/query.ftl index f9765eb..93194d0 100644 --- a/exec/java-exec/src/main/resources/rest/query/query.ftl +++ b/exec/java-exec/src/main/resources/rest/query/query.ftl @@ -13,6 +13,7 @@ <#macro page_head> <#if model?? && model> <script src="/static/js/jquery.form.js"></script> + <script src="/static/js/querySubmission.js"></script> </#if> </#macro> @@ -59,38 +60,10 @@ <textarea class="form-control" id="query" rows="5" name="query" style="font-family: Courier;"></textarea> </div> - <button class="btn btn-default" type=<#if model?? && model>"button" onclick="doSubmit()"<#else>"submit"</#if>> + <button class="btn btn-default" type=<#if model?? && model>"button" onclick="doSubmitQueryWithUserName()"<#else>"submit"</#if>> Submit </button> </form> - - <#if model?? && model> - <script> - function doSubmit() { - var userName = document.getElementById("userName").value; - if (!userName.trim()) { - alert("Please fill in User Name field"); - return; - } - $.ajax({ - type: "POST", - beforeSend: function (request) { - request.setRequestHeader("User-Name", userName); - }, - url: "/query", - data: $("#queryForm").serializeArray(), - success: function (response) { - var newDoc = document.open("text/html", "replace"); - newDoc.write(response); - newDoc.close(); - }, - error: function (request, textStatus, errorThrown) { - alert(errorThrown); - } - }); - } - </script> - </#if> </#macro> <@page_html/> http://git-wip-us.apache.org/repos/asf/drill/blob/e25c58f7/exec/java-exec/src/main/resources/rest/static/js/querySubmission.js ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/resources/rest/static/js/querySubmission.js b/exec/java-exec/src/main/resources/rest/static/js/querySubmission.js new file mode 100644 index 0000000..638ddbf --- /dev/null +++ b/exec/java-exec/src/main/resources/rest/static/js/querySubmission.js @@ -0,0 +1,36 @@ +/* + * 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. + */ + +function doSubmitQueryWithUserName() { + var userName = document.getElementById("userName").value; + if (!userName.trim()) { + alert("Please fill in User Name field"); + return; + } + $.ajax({ + type: "POST", + beforeSend: function (request) { + request.setRequestHeader("User-Name", userName); + }, + url: "/query", + data: $("#queryForm").serializeArray(), + success: function (response) { + var newDoc = document.open("text/html", "replace"); + newDoc.write(response); + newDoc.close(); + }, + error: function (request, textStatus, errorThrown) { + alert(errorThrown); + } + }); +} \ No newline at end of file
