mirror of https://github.com/apache/lucene.git
SOLR-13569: AdminUI visual indication of prod/test/dev environment
This commit is contained in:
parent
6751c072ab
commit
b54126169b
|
@ -146,6 +146,8 @@ New Features
|
|||
|
||||
* SOLR-13367: Highlighting: Range queries will now highlight in hl.method=unified mode. (David Smiley)
|
||||
|
||||
* SOLR-13569: AdminUI visual indication of prod/test/dev environment (janhoy)
|
||||
|
||||
Bug Fixes
|
||||
----------------------
|
||||
|
||||
|
|
|
@ -168,3 +168,8 @@ REM list of hosts needs to be whitelisted or Solr will forbid the request. The w
|
|||
REM or if you are using the OOTB solr.xml, can be specified using the system property "solr.shardsWhitelist". Alternatively
|
||||
REM host checking can be disabled by using the system property "solr.disable.shardsWhitelist"
|
||||
REM set SOLR_OPTS="%SOLR_OPTS% -Dsolr.shardsWhitelist=http://localhost:8983,http://localhost:8984"
|
||||
|
||||
REM For a visual indication in the Admin UI of what type of environment this cluster is, configure
|
||||
REM a -Dsolr.environment property below. Valid values are prod, stage, test, dev, with an optional
|
||||
REM label or color, e.g. -Dsolr.environment=test,label=Functional+test,color=brown
|
||||
REM SOLR_OPTS="$SOLR_OPTS -Dsolr.environment=prod"
|
||||
|
|
|
@ -196,3 +196,8 @@ ENABLE_REMOTE_JMX_OPTS="true"
|
|||
# or if you are using the OOTB solr.xml, can be specified using the system property "solr.shardsWhitelist". Alternatively
|
||||
# host checking can be disabled by using the system property "solr.disable.shardsWhitelist"
|
||||
#SOLR_OPTS="$SOLR_OPTS -Dsolr.shardsWhitelist=http://localhost:8983,http://localhost:8984"
|
||||
|
||||
# For a visual indication in the Admin UI of what type of environment this cluster is, configure
|
||||
# a -Dsolr.environment property below. Valid values are prod, stage, test, dev, with an optional
|
||||
# label or color, e.g. -Dsolr.environment=test,label=Functional+test,color=brown
|
||||
#SOLR_OPTS="$SOLR_OPTS -Dsolr.environment=prod"
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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.solr.handler.admin;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.common.cloud.ZkStateReader;
|
||||
|
||||
/**
|
||||
* It is possible to define an environment code when starting Solr, through
|
||||
* -Dsolr.environment=prod|stage|test|dev or by setting the cluster property "environment".
|
||||
* This class checks if any of these are defined, and parses the string, which may also
|
||||
* contain custom overrides for environment name (label) and color to be shown in Admin UI
|
||||
*/
|
||||
public class SolrEnvironment {
|
||||
private String code = "unknown";
|
||||
private String label;
|
||||
private String color;
|
||||
private static Pattern pattern = Pattern.compile("^(prod|stage|test|dev)(,label=([\\w\\d+ _-]+))?(,color=([#\\w\\d]+))?");
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label == null ? null : label.replaceAll("\\+", " ");
|
||||
}
|
||||
|
||||
public String getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public boolean isDefined() {
|
||||
return !"unknown".equals(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an environment string of format <prod|stage|test|dev>
|
||||
* with an optional label and color as arguments
|
||||
* @param environmentString the raw string to parse
|
||||
* @return an instance of this object
|
||||
*/
|
||||
public static SolrEnvironment parse(String environmentString) {
|
||||
SolrEnvironment env = new SolrEnvironment();
|
||||
if (environmentString == null || environmentString.equalsIgnoreCase("unknown")) {
|
||||
return env;
|
||||
}
|
||||
Matcher m = pattern.matcher(environmentString);
|
||||
if (!m.matches()) {
|
||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Bad environment pattern: " + environmentString);
|
||||
}
|
||||
env.code = m.group(1);
|
||||
if (m.group(3) != null) {
|
||||
env.label = m.group(3);
|
||||
}
|
||||
if (m.group(5) != null) {
|
||||
env.color = m.group(5);
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and parses the solr environment configuration string from either
|
||||
* System properties "solr.environment" or from Clusterprop "environment"
|
||||
* @param zkStateReader pass in the zkStateReader if in cloud mode
|
||||
* @return an instance of this class
|
||||
*/
|
||||
public static SolrEnvironment getFromSyspropOrClusterprop(ZkStateReader zkStateReader) {
|
||||
String env = "unknown";
|
||||
if (System.getProperty("solr.environment") != null) {
|
||||
env = System.getProperty("solr.environment");
|
||||
} else if (zkStateReader != null) {
|
||||
env = zkStateReader.getClusterProperty("environment", "unknown");
|
||||
}
|
||||
return SolrEnvironment.parse(env);
|
||||
}
|
||||
}
|
|
@ -150,6 +150,17 @@ public class SystemInfoHandler extends RequestHandlerBase
|
|||
if (solrCloudMode) {
|
||||
rsp.add("node", getCoreContainer(req, core).getZkController().getNodeName());
|
||||
}
|
||||
SolrEnvironment env = SolrEnvironment.getFromSyspropOrClusterprop(solrCloudMode ?
|
||||
getCoreContainer(req, core).getZkController().zkStateReader : null);
|
||||
if (env.isDefined()) {
|
||||
rsp.add("environment", env.getCode());
|
||||
if (env.getLabel() != null) {
|
||||
rsp.add("environment_label", env.getLabel());
|
||||
}
|
||||
if (env.getColor() != null) {
|
||||
rsp.add("environment_color", env.getColor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CoreContainer getCoreContainer(SolrQueryRequest req, SolrCore core) {
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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.solr.handler.admin;
|
||||
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class SolrEnvironmentTest {
|
||||
|
||||
@Test(expected = SolrException.class)
|
||||
public void parseWrongKey() {
|
||||
SolrEnvironment.parse("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parsePredefined() {
|
||||
assertEquals("prod", SolrEnvironment.parse("prod").getCode());
|
||||
assertNull(SolrEnvironment.parse("prod").getColor());
|
||||
assertNull(SolrEnvironment.parse("prod").getLabel());
|
||||
|
||||
assertEquals("stage", SolrEnvironment.parse("stage").getCode());
|
||||
assertNull(SolrEnvironment.parse("stage").getColor());
|
||||
assertNull(SolrEnvironment.parse("stage").getLabel());
|
||||
|
||||
assertEquals("test", SolrEnvironment.parse("test").getCode());
|
||||
assertNull(SolrEnvironment.parse("test").getColor());
|
||||
assertNull(SolrEnvironment.parse("test").getLabel());
|
||||
|
||||
assertEquals("dev", SolrEnvironment.parse("dev").getCode());
|
||||
assertNull(SolrEnvironment.parse("dev").getColor());
|
||||
assertNull(SolrEnvironment.parse("dev").getLabel());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCustom() {
|
||||
assertEquals("my Label", SolrEnvironment.parse("prod,label=my+Label,color=blue").getLabel());
|
||||
assertEquals("blue", SolrEnvironment.parse("prod,label=my+Label,color=blue").getColor());
|
||||
assertEquals("my Label", SolrEnvironment.parse("prod,label=my+Label").getLabel());
|
||||
assertEquals("blue", SolrEnvironment.parse("prod,color=blue").getColor());
|
||||
}
|
||||
|
||||
@Test(expected = SolrException.class)
|
||||
public void tryingToHackLabel() {
|
||||
SolrEnvironment.parse("prod,label=alert('hacked')");
|
||||
}
|
||||
|
||||
@Test(expected = SolrException.class)
|
||||
public void tryingToHackColor() {
|
||||
SolrEnvironment.parse("prod,color=alert('hacked')");
|
||||
}
|
||||
|
||||
@Test(expected = SolrException.class)
|
||||
public void illegalParam() {
|
||||
SolrEnvironment.parse("prod,foo=hello");
|
||||
}
|
||||
}
|
|
@ -239,6 +239,15 @@ SOLR_HOST=solr1.example.com
|
|||
|
||||
Setting the hostname of the Solr server is recommended, especially when running in SolrCloud mode, as this determines the address of the node when it registers with ZooKeeper.
|
||||
|
||||
=== Environment banner in Admin UI
|
||||
|
||||
To guard against accidentally doing changes to the wrong cluster, you may configure a visual indication in the Admin UI of whether you currently work with a production environment or not. To do this, edit your `solr.in.sh` or `solr.in.cmd` file with a `-Dsolr.environment=prod` setting, or set the cluster property named `environment`. To specify label and/or color, use a comma delimited format as below. The `+` character can be used instead of space to avoid quoting. Colors may be valid CSS colors or numeric e.g. `#ff0000` for bright red. Examples of valid environment configs:
|
||||
|
||||
* `prod`
|
||||
* `test,label=Functional+test`
|
||||
* `dev,label=MyDev,color=blue`
|
||||
* `dev,color=blue`
|
||||
|
||||
=== Override Settings in solrconfig.xml
|
||||
|
||||
Solr allows configuration properties to be overridden using Java system properties passed at startup using the `-Dproperty=value` syntax. For instance, in `solrconfig.xml`, the default auto soft commit settings are set to:
|
||||
|
|
|
@ -147,6 +147,8 @@ public class ZkStateReader implements SolrCloseable {
|
|||
|
||||
public static final String URL_SCHEME = "urlScheme";
|
||||
|
||||
private static final String SOLR_ENVIRONMENT = "environment";
|
||||
|
||||
public static final String REPLICA_TYPE = "type";
|
||||
|
||||
/**
|
||||
|
@ -277,6 +279,7 @@ public class ZkStateReader implements SolrCloseable {
|
|||
DEFAULT_SHARD_PREFERENCES,
|
||||
MAX_CORES_PER_NODE,
|
||||
SAMPLE_PERCENTAGE,
|
||||
SOLR_ENVIRONMENT,
|
||||
CollectionAdminParams.DEFAULTS)));
|
||||
|
||||
/**
|
||||
|
|
|
@ -291,9 +291,7 @@ ul
|
|||
{
|
||||
background-image: url( ../../img/ico/box.png );
|
||||
background-position: 5px 50%;
|
||||
display: none;
|
||||
font-weight: bold;
|
||||
margin-top: 10px;
|
||||
padding: 5px 10px;
|
||||
padding-left: 26px;
|
||||
}
|
||||
|
@ -309,6 +307,12 @@ ul
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
#environment.stage
|
||||
{
|
||||
background-color: orange;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#environment.test
|
||||
{
|
||||
background-color: #f5f5b2;
|
||||
|
|
|
@ -98,8 +98,6 @@ limitations under the License.
|
|||
|
||||
<a href="#/" id="solr"><span>Apache SOLR</span></a>
|
||||
|
||||
<p id="environment"> </p>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="main" class="clearfix">
|
||||
|
@ -143,6 +141,8 @@ limitations under the License.
|
|||
<div>
|
||||
|
||||
<ul id="menu">
|
||||
<li id="environment" ng-class="environment" ng-show="showEnvironment" ng-style="environment_color !== undefined ? {'background-color': environment_color} : ''">{{ environment_label }}</li>
|
||||
|
||||
<li id="login" class="global" ng-class="{active:page=='login'}" ng-show="http401 || currentUser"><p><a href="#/login">{{http401 ? "Login" : "Logout " + currentUser}}</a></p></li>
|
||||
|
||||
<li id="index" class="global" ng-class="{active:page=='index'}"><p><a href="#/">Dashboard</a></p></li>
|
||||
|
|
|
@ -481,6 +481,18 @@ solrAdminApp.controller('MainController', function($scope, $route, $rootScope, $
|
|||
})
|
||||
}
|
||||
|
||||
$scope.showEnvironment = data.environment !== undefined;
|
||||
if (data.environment) {
|
||||
$scope.environment = data.environment;
|
||||
var env_labels = {'prod': 'Production', 'stage': 'Staging', 'test': 'Test', 'dev': 'Development'};
|
||||
$scope.environment_label = env_labels[data.environment];
|
||||
if (data.environment_label) {
|
||||
$scope.environment_label = data.environment_label;
|
||||
}
|
||||
if (data.environment_color) {
|
||||
$scope.environment_color = data.environment_color;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$scope.showingLogging = page.lastIndexOf("logging", 0) === 0;
|
||||
|
|
Loading…
Reference in New Issue