Skip to content

Commit 921d86b

Browse files
committed
Fixes issue where environment wasn't applied for nodetool and other C* tools. The fix is to now just globally set JAVA_HOME rather than through the append_to_server_env hook.
Adds support for Debian & Arch Linux as a test.
1 parent cac9b72 commit 921d86b

File tree

10 files changed

+191
-38
lines changed

10 files changed

+191
-38
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
venv
22
dist
33
.idea
4+
build
45
*.egg-info
56

67
*/__pycache__/*

README.md

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,42 @@
11
# ccm-java8
22

3-
_ccm-java8_ is a simple [CCM](https://github.com/riptano/ccm/) extension that explicitly sets the `JAVA_HOME` environment variable for all CCM-managed
4-
Cassandra nodes to an available Java 1.8 installation, which is required to run Cassandra 3.11 and lower.
3+
[![PyPi Badge](https://img.shields.io/pypi/v/ccm-java8)](https://pypi.org/project/ccm-java8/)
54

6-
**Platforms Currently Supported:** macOS
5+
_ccm-java8_ is a [CCM](https://github.com/riptano/ccm/) extension that explicitly sets the `JAVA_HOME` environment variable for all CCM-managed
6+
Cassandra nodes (and related tools) to an available Java 8 installation, which is required to run Cassandra 3.11 and lower.
7+
8+
Platforms Currently Supported:
9+
* macOS
10+
* Linux
11+
* Arch
12+
* Debian-based
13+
* RHEL-based
714

815
## Usage
916

10-
Install it alongside CCM:
17+
_ccm-java8_ is available on [PyPI](https://pypi.org/project/ccm-java8/).
18+
19+
1. Install it alongside CCM:
20+
21+
pip install ccm ccm-java8
1122

12-
`pip install ccm ccm-java8`
23+
1. Run CCM commands as normal.
24+
25+
Cassandra and various tools will launch using the Java 8 VM.
26+
27+
If no Java 8 VM can be found, CCM will refuse to start.
28+
Install Java 8 if this occurs.
1329

1430

1531
## Motivation
1632

17-
While operating systems support the side-by-side installation of multiple Java versions, only one version can be selected as the default
18-
(i.e., what version `java` on `$PATH` points to).
33+
Many operating systems support the side-by-side installation of multiple Java versions, yet only one version can be selected as the default
34+
(i.e., what version of `java` is on `$PATH`).
1935

2036
Cassandra's `bin/cassandra` launch script prefers the `java` binary under `$JAVA_HOME`, and will fallback to using the `java` binary on `$PATH` if `$JAVA_HOME` isn't set.
21-
Hence, unless `$JAVA_HOME` or the platform default is explicitly set to a Java 1.8 installation, Cassandra will try, and fail, to start under an incompatible Java version.
37+
Hence, unless `$JAVA_HOME` or the platform default is explicitly set to a Java 8 installation, Cassandra will try, and fail, to start under an incompatible Java version.
38+
39+
_ccm-java8_ works by registering a CCM extension that when loaded by CCM explicitly sets the `JAVA_HOME` environment variable to a directory containing a Java 8 installation, or throws an exception otherwise.
2240

23-
_ccm-java8_ works by registering a CCM extension, and hooks into the `append_to_server_env` method that allows extensions to provide additional environment variables
24-
for each Cassandra process started by CCM.
25-
On node start, _ccm-java8_ explicitly sets the `JAVA_HOME` environment variable to a directory containing a Java 1.8 installation, or throws an exception otherwise.
41+
Older versions used to register a hook into the `append_to_server_env` function, but this function doesn't get called for tools (`nodetool`, `sstabledump`, etc.).
42+
The current version sets the `JAVA_HOME` environment variable globally in the CCM Python process, which gets inherited by all sub-processes launched by CCM.

ccm_java8.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import os
2+
import platform
3+
import subprocess
4+
import sys
5+
from pathlib import Path
6+
import re
7+
from typing import List
8+
9+
import os_release
10+
11+
12+
def log_debug(msg):
13+
if 'CCM_JAVA8_DEBUG' in os.environ:
14+
print(msg, file=sys.stderr)
15+
16+
17+
def get_java_env():
18+
(java_bin_path, loc) = (f'{os.environ["JAVA_HOME"]}/bin/java', 'in JAVA_HOME') if 'JAVA_HOME' in os.environ else ('java', 'on PATH')
19+
20+
try:
21+
current_version = subprocess.check_output([java_bin_path, '-version'], stderr=subprocess.STDOUT).decode('utf-8').split('\n')[0]
22+
if 'version "1.8.' in current_version:
23+
return {}
24+
25+
log_debug(f'Java {loc} is: {current_version}.')
26+
27+
except (subprocess.CalledProcessError, FileNotFoundError):
28+
log_debug(f'Java not found {loc}')
29+
pass
30+
31+
system = platform.system()
32+
33+
if system == 'Darwin': # ie, macOS (for all intents and purposes)
34+
return {
35+
'JAVA_HOME': subprocess.check_output(['/usr/libexec/java_home', '-v', '1.8']).decode('utf-8').strip()
36+
}
37+
38+
elif system == 'Linux':
39+
current_release = os_release.current_release()
40+
41+
def handle_jvms(jvms: List, type) -> dict:
42+
jvms = [str(jvm) for jvm in jvms]
43+
44+
if len(jvms) > 1:
45+
log_debug(f'Found multiple Java 8 {type}s: {dict(enumerate(jvms, start=1))}. Using the first {type}.')
46+
47+
elif len(jvms) == 0:
48+
raise Exception(f'Java 8 {type} not found.')
49+
50+
return {
51+
'JAVA_HOME': jvms[0]
52+
}
53+
54+
if current_release.is_like('arch'): # Arch Linux (and derivatives)
55+
installs = list(Path('/usr/lib/jvm').glob("java-8*"))
56+
57+
return handle_jvms(installs, 'installation')
58+
59+
elif current_release.is_like('debian'): # Debian-based distros (any that uses `update-alternatives`)
60+
alternatives = subprocess.check_output(['update-alternatives', '--list', 'java']).decode('utf-8').split('\n')
61+
alternatives = [alt.strip('/bin/java') for alt in alternatives if "-8-" in alt]
62+
63+
return handle_jvms(alternatives, 'alternative')
64+
65+
elif current_release.is_like('rhel'): # RHEL-based distros
66+
alternatives = subprocess.check_output(['alternatives', '--display', 'java']).decode('utf-8').split('\n')
67+
68+
regex = re.compile(r'^(?P<path>/usr/lib/jvm/.*(-8-|-1.8.0-).*)/bin/java.*')
69+
70+
alternatives = [match.group('path') for match in filter(None, map(regex.match, alternatives))]
71+
72+
return handle_jvms(alternatives, 'alternative')
73+
74+
else:
75+
raise Exception(f'Automatic Java 8 environment configuration not available for this distribution ({current_release.id}).')
76+
77+
# TODO: implement other platform support here
78+
79+
else:
80+
raise Exception(f'Automatic Java 8 environment configuration not available for this platform ({system}).')
81+
82+
83+
def set_java_env():
84+
env = get_java_env()
85+
log_debug(f'New Java 8 environment: {env}')
86+
os.environ.update(env)
87+
88+
89+
if __name__ == '__main__':
90+
os.environ['CCM_JAVA8_DEBUG'] = 'please'
91+
set_java_env()

ccm_java8/__init__.py

Whitespace-only changes.

ccm_java8/java_home.py

Lines changed: 0 additions & 20 deletions
This file was deleted.

platform-tests/arch/Dockerfile

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Arch Linux test. Install various versions of Java, including the Oracle JDK 8 from AUR.
2+
3+
FROM archlinux:latest
4+
5+
RUN pacman -Sy --noconfirm git sudo fakeroot python python-pip jre-openjdk-headless jre8-openjdk-headless jre7-openjdk-headless
6+
7+
# setup build user for makepkg
8+
RUN useradd --no-create-home --shell=/bin/false build && usermod -L build
9+
RUN echo "build ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
10+
RUN mkdir /jre8 && chown -R build:build jre8
11+
12+
USER build
13+
14+
# install Oracle Jdk8
15+
RUN git clone https://aur.archlinux.org/jre8.git
16+
RUN cd jre8 && makepkg -si --noconfirm
17+
18+
USER root
19+
20+
RUN pip install os-release
21+
22+
COPY ccm_java8.py /
23+
RUN python ccm_java8.py

platform-tests/centos/Dockerfile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# CentOS test. Install various versions of Java.
2+
3+
FROM centos:latest
4+
5+
COPY platform-tests/centos/adoptopenjdk.repo /etc/yum.repos.d/
6+
7+
RUN dnf install -y java-11-openjdk-headless java-1.8.0-openjdk-headless adoptopenjdk-8-hotspot
8+
9+
RUN dnf install -y python36
10+
11+
RUN alternatives --set java java-11-openjdk.x86_64
12+
13+
RUN pip3 install os-release
14+
15+
COPY ccm_java8.py /
16+
RUN python3 ccm_java8.py
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[AdoptOpenJDK]
2+
name=AdoptOpenJDK
3+
baseurl=http://adoptopenjdk.jfrog.io/adoptopenjdk/rpm/centos/$releasever/$basearch
4+
enabled=1
5+
gpgcheck=1
6+
gpgkey=https://adoptopenjdk.jfrog.io/adoptopenjdk/api/gpg/key/public

platform-tests/debian/Dockerfile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Debian test. Install various versions of Java.
2+
3+
FROM debian:latest
4+
5+
RUN apt-get update && apt-get install -y software-properties-common wget gnupg
6+
7+
RUN wget -qO - https://adoptopenjdk.jfrog.io/adoptopenjdk/api/gpg/key/public | apt-key add -
8+
RUN add-apt-repository --yes https://adoptopenjdk.jfrog.io/adoptopenjdk/deb/
9+
10+
RUN apt-get update && apt-get install -y adoptopenjdk-8-hotspot openjdk-11-jre-headless
11+
12+
RUN apt-get install -y python3 python3-pip
13+
14+
RUN pip3 install os-release
15+
16+
COPY ccm_java8.py /
17+
RUN python3 ccm_java8.py

setup.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,32 @@
1-
from setuptools import setup, find_packages
1+
from setuptools import setup
22

33
with open("README.md", "r") as fh:
44
long_description = fh.read()
55

66
setup(
77
name='ccm_java8',
8-
version='0.1',
9-
packages=find_packages(),
8+
version='1.0',
9+
py_modules=['ccm_java8'],
1010

1111
author="Adam Zegelin",
1212
author_email="adam@instaclustr.com",
1313

14-
description="CCM extension that starts nodes under Java 1.8",
14+
description="CCM extension that starts Cassandra (and related tools) under Java 8",
1515
long_description=long_description,
1616
long_description_content_type="text/markdown",
1717

1818
url="https://github.com/instaclustr/ccm-java8",
1919

20-
install_requires=['ccm'],
20+
install_requires=['ccm', 'os-release'],
2121

2222
entry_points={
23-
'ccm_extension': ['java_home = ccm_java8.java_home:register_extensions']
23+
'ccm_extension': ['java_home = ccm_java8:set_java_env']
2424
},
2525

2626
classifiers=[
27-
'Development Status :: 4 - Beta',
27+
'Development Status :: 5 - Production/Stable',
28+
'Environment :: Console',
29+
'Environment :: Plugins',
2830
'Environment :: MacOS X',
2931
'Intended Audience :: Developers',
3032
'License :: OSI Approved :: Apache Software License',

0 commit comments

Comments
 (0)