Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notify systemd when Elasticsearch is ready #44673

Merged
merged 13 commits into from
Jul 24, 2019
18 changes: 18 additions & 0 deletions distribution/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ task buildOssNoJdkNotice(type: NoticeTask) {
*****************************************************************************/
String ossOutputs = 'build/outputs/oss'
String defaultOutputs = 'build/outputs/default'
String systemdOutputs = 'build/outputs/systemd'
String transportOutputs = 'build/outputs/transport-only'

task processOssOutputs(type: Sync) {
Expand All @@ -79,6 +80,10 @@ task processDefaultOutputs(type: Sync) {
from processOssOutputs
}

task processSystemdOutputs(type: Sync) {
into systemdOutputs
}

// Integ tests work over the rest http layer, so we need a transport included with the integ test zip.
// All transport modules are included so that they may be randomized for testing
task processTransportOutputs(type: Sync) {
Expand Down Expand Up @@ -110,6 +115,10 @@ task buildDefaultConfig {
dependsOn processDefaultOutputs
outputs.dir "${defaultOutputs}/config"
}
task buildSystemdModule {
dependsOn processSystemdOutputs
outputs.dir "${systemdOutputs}/modules"
}
task buildTransportModules {
dependsOn processTransportOutputs
outputs.dir "${transportOutputs}/modules"
Expand Down Expand Up @@ -186,6 +195,10 @@ ext.restTestExpansions = [
// we create the buildOssModules task above but fill it here so we can do a single
// loop over modules to also setup cross task dependencies and increment our modules counter
project.rootProject.subprojects.findAll { it.parent.path == ':modules' }.each { Project module ->
if (module.name == 'systemd') {
// the systemd module is only included in the package distributions
return
}
File licenses = new File(module.projectDir, 'licenses')
if (licenses.exists()) {
buildDefaultNotice.licensesDir licenses
Expand Down Expand Up @@ -218,6 +231,8 @@ xpack.subprojects.findAll { it.parent == xpack }.each { Project xpackModule ->
copyLog4jProperties(buildDefaultLog4jConfig, xpackModule)
}

copyModule(processSystemdOutputs, project(':modules:systemd'))

// make sure we have a clean task since we aren't a java project, but we have tasks that
// put stuff in the build dir
task clean(type: Delete) {
Expand Down Expand Up @@ -288,6 +303,9 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) {
exclude "**/platform/${excludePlatform}-x86_64/**"
}
}
if (project.path.startsWith(':distribution:packages')) {
from(project(':distribution').buildSystemdModule)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ Wants=network-online.target
After=network-online.target

[Service]
Type=notify
RuntimeDirectory=elasticsearch
PrivateTmp=true
Environment=ES_HOME=/usr/share/elasticsearch
Environment=ES_PATH_CONF=${path.conf}
Environment=PID_DIR=/var/run/elasticsearch
Environment=ES_SD_NOTIFY=true
EnvironmentFile=-${path.env}

WorkingDirectory=/usr/share/elasticsearch
Expand Down
25 changes: 25 additions & 0 deletions modules/systemd/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.
*/

esplugin {
description 'Integrates Elasticsearch with systemd'
classname 'org.elasticsearch.systemd.SystemdPlugin'
}

integTest.enabled = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.elasticsearch.systemd;

import com.sun.jna.Native;

import java.security.AccessController;
import java.security.PrivilegedAction;

/**
* Provides access to the native method sd_notify from libsystemd.
*/
class Libsystemd {

static {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
Native.register(Libsystemd.class, "libsystemd.so.0");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this fail with the packaged distributions on OS-es that don't support systemd, or does everything in our support matrix support it ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don’t try to load the native library if the environment variable ES_SD_NOTIFY is not set to “true” and in this PR we do that only in the systemd service integration (see the unit file).

return null;
});
}

/**
* Notify systemd of state changes.
*
* @param unset_environment if non-zero, the NOTIFY_SOCKET environment variable will be unset before returning and further calls to
* sd_notify will fail
* @param state a new-line separated list of variable assignments; some assignments are understood directly by systemd
* @return a negative error code on failure, and positive if status was successfully sent
*/
static native int sd_notify(int unset_environment, String state);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.elasticsearch.systemd;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.util.Constants;
import org.elasticsearch.Assertions;
import org.elasticsearch.Build;
import org.elasticsearch.plugins.ClusterPlugin;
import org.elasticsearch.plugins.Plugin;

public class SystemdPlugin extends Plugin implements ClusterPlugin {

private static final Logger logger = LogManager.getLogger(SystemdPlugin.class);

private final boolean enabled;

final boolean isEnabled() {
return enabled;
}

@SuppressWarnings("unused")
public SystemdPlugin() {
this(true, Constants.LINUX, System.getenv("ES_SD_NOTIFY"));
}

SystemdPlugin(final boolean assertIsPackageDistribution, final boolean isLinux, final String esSDNotify) {
if (Assertions.ENABLED && assertIsPackageDistribution) {
// our build is configured to only include this module in the package distributions
assert Build.CURRENT.type() == Build.Type.DEB || Build.CURRENT.type() == Build.Type.RPM : Build.CURRENT.type();
}
if (isLinux == false || esSDNotify == null) {
enabled = false;
return;
}
if (Boolean.TRUE.toString().equals(esSDNotify) == false && Boolean.FALSE.toString().equals(esSDNotify) == false) {
throw new RuntimeException("ES_SD_NOTIFY set to unexpected value [" + esSDNotify + "]");
}
enabled = Boolean.TRUE.toString().equals(esSDNotify);
}

int sd_notify(@SuppressWarnings("SameParameterValue") final int unset_environment, final String state) {
return Libsystemd.sd_notify(unset_environment, state);
}

@Override
public void onNodeStarted() {
if (enabled == false) {
return;
}
final int rc = sd_notify(0, "READY=1");
logger.trace("sd_notify returned [{}]", rc);
if (rc < 0) {
// treat failure to notify systemd of readiness as a startup failure
throw new RuntimeException("sd_notify returned error [" + rc + "]");
}
}

@Override
public void close() {
if (enabled == false) {
return;
}
final int rc = sd_notify(0, "STOPPING=1");
logger.trace("sd_notify returned [{}]", rc);
if (rc < 0) {
// do not treat failure to notify systemd of stopping as a failure
logger.warn("sd_notify returned error [{}]", rc);
}
}

}
23 changes: 23 additions & 0 deletions modules/systemd/src/main/plugin-metadata/plugin-security.policy
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.
*/

grant codeBase "${codebase.systemd}" {
// for registering native methods
permission java.lang.RuntimePermission "accessDeclaredMembers";
};
Loading