Skip to content

Commit fa6c165

Browse files
committed
wip
1 parent 626f4a6 commit fa6c165

File tree

5 files changed

+158
-24
lines changed

5 files changed

+158
-24
lines changed

pom.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>com.eficode</groupId>
88
<artifactId>devstack</artifactId>
9-
<version>2.3.12-SNAPSHOT</version>
9+
<version>2.3.13-SNAPSHOT</version>
1010
<packaging>jar</packaging>
1111

1212
<name>DevStack</name>
@@ -228,4 +228,4 @@
228228
</plugins>
229229
</build>
230230

231-
</project>
231+
</project>

src/main/groovy/com/eficode/devstack/container/Container.groovy

+1-1
Original file line numberDiff line numberDiff line change
@@ -974,4 +974,4 @@ trait Container {
974974
return callBack.output
975975
}
976976

977-
}
977+
}

src/main/groovy/com/eficode/devstack/container/impl/JsmContainer.groovy

+15-17
Original file line numberDiff line numberDiff line change
@@ -60,25 +60,23 @@ class JsmContainer implements Container {
6060

6161
@Override
6262
ContainerCreateRequest setupContainerCreateRequest() {
63-
64-
String image = containerImage + ":" + containerImageTag
65-
6663
log.debug("Setting up container create request for JSM container")
67-
if (dockerClient.engineArch != "x86_64") {
68-
log.debug("\tDocker engine is not x86, building custom JSM docker image")
69-
70-
ImageBuilder imageBuilder = new ImageBuilder(dockerClient.host, dockerClient.certPath)
71-
String jsmVersion = containerImageTag
72-
if (jsmVersion == "latest") {
73-
log.debug("\tCurrent image tag is set to \"latest\", need to resolve latest version number from Atlassian Marketplace in order to build custom image")
74-
jsmVersion = getLatestJsmVersion()
75-
}
76-
log.debug("\tStarting building of Docker Image for JSM verion $jsmVersion")
77-
ImageSummary newImage = imageBuilder.buildJsm(jsmVersion)
78-
log.debug("\tFinished building custom image:" + newImage.repoTags.join(","))
7964

80-
image = newImage.repoTags.first()
65+
new ImageBuilder(dockerClient.host, dockerClient.certPath)
66+
String jsmVersion = containerImageTag
67+
if (jsmVersion == "latest") {
68+
log.debug("\tCurrent image tag is set to \"latest\", need to resolve latest version number from Atlassian Marketplace in order to build custom image")
69+
jsmVersion = getLatestJsmVersion()
8170
}
71+
log.debug("\tStarting building of Docker Image for JSM verion $jsmVersion")
72+
ImageSummary jsmImage = new ImageBuilder(dockerClient.host, dockerClient.certPath).buildJsm(jsmVersion)
73+
log.debug("\tFinished building custom image:" + jsmImage.repoTags.join(","))
74+
75+
log.debug("\tStarting building of Docker Image for faketime JSM verion $jsmVersion")
76+
ImageSummary faketimeJsmImage = new ImageBuilder(dockerClient.host, dockerClient.certPath).buildFaketimeJsm(jsmVersion)
77+
log.debug("\tFinished building custom image:" + faketimeJsmImage.repoTags.join(","))
78+
79+
String image = faketimeJsmImage.repoTags.first()
8280

8381
ContainerCreateRequest containerCreateRequest = new ContainerCreateRequest().tap { c ->
8482

@@ -94,7 +92,7 @@ class JsmContainer implements Container {
9492
if (debugPort) {
9593
h.portBindings.put((debugPort + "/tcp"), [new PortBinding("0.0.0.0", (debugPort))])
9694
c.exposedPorts.put((debugPort + "/tcp"), [:])
97-
c.env.add("JVM_SUPPORT_RECOMMENDED_ARGS=-Xdebug -Xrunjdwp:transport=dt_socket,address=*:${debugPort},server=y,suspend=n".toString())
95+
c.env.add("JVM_SUPPORT_RECOMMENDED_ARGS=-XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_currentTimeMillis -XX:CompileCommand=dontinline,java.lang.System::currentTimeMillis -agentpath:/libfaketime.so=+2592000000 -Xdebug -Xrunjdwp:transport=dt_socket,address=*:${debugPort},server=y,suspend=n".toString())
9896
}
9997

10098

src/main/groovy/com/eficode/devstack/util/ImageBuilder.groovy

+48-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import com.eficode.devstack.container.impl.DoodContainer
33
import de.gesellix.docker.remote.api.ImageSummary
44
import java.util.concurrent.TimeoutException
55

6-
76
/**
87
* A utility class intended to build docker images so that they match the docker engines CPU architecture
98
*
@@ -16,7 +15,6 @@ class ImageBuilder extends DoodContainer {
1615
Map<String, ArrayList<String>>builderOut = [:]
1716
long cmdTimeoutS = 800 //Will timeout individual container commands after this many seconds
1817

19-
2018
ImageBuilder(String dockerHost, String dockerCertPath) {
2119
assert setupSecureRemoteConnection(dockerHost, dockerCertPath): "Error setting up secure remote docker connection"
2220
prepareBindMount("/var/run/docker.sock", "/var/run/docker.sock")
@@ -48,7 +46,6 @@ class ImageBuilder extends DoodContainer {
4846
* @return
4947
*/
5048
ImageSummary buildJsm(String jsmVersion, boolean force = false){
51-
5249
String imageName = "atlassian/jira-servicemanagement"
5350
String artifactName = "atlassian-servicedesk"
5451
String archType = dockerClient.engineArch
@@ -76,7 +73,55 @@ class ImageBuilder extends DoodContainer {
7673
ImageSummary newImage = images.find {it.repoTags == [imageTag]}
7774
log.debug("\tFinished building image:" + imageTag + ", ID:" + newImage.id[7..17])
7875
return newImage
76+
}
77+
78+
ImageSummary buildFaketimeJsm(String jsmVersion, boolean force = false){
79+
String imageName = "atlassian/jira-servicemanagement"
80+
String artifactName = "atlassian-servicedesk"
81+
String archType = dockerClient.engineArch
82+
String imageTag = "$imageName:$jsmVersion-$archType"
83+
String faketimeRoot = "/faketimebuild"
84+
String faketimeDockerFilePath = "$faketimeRoot/Dockerfile"
85+
String faketimeAgentFilePath = "$faketimeRoot/faketime.cpp"
86+
String faketimeImageTag = "$imageName-faketime:$jsmVersion-$archType"
87+
String faketimecpp = getClass().getResourceAsStream("/faketime.cpp").text
88+
containerName = faketimeImageTag.replaceAll(/[^a-zA-Z0-9_.-]/, "-").take(128-"-IB".length())
89+
containerName += "-IB"
90+
91+
log.info("my name is now $containerName")
92+
93+
//Check first if an image with the expected tag already exists
94+
if (!force) {
95+
ArrayList<ImageSummary> existingImages = dockerClient.images().content
96+
ImageSummary existingImage = existingImages.find {it.repoTags == [faketimeImageTag]}
97+
if (existingImage) {
98+
return existingImage
99+
}
100+
}
101+
102+
String faketimeDockerFile = """
103+
FROM $imageTag
104+
WORKDIR /
105+
RUN apt-get update && apt-get install -y wget g++ make
106+
# RUN wget https://github.com/odnoklassniki/jvmti-tools/raw/master/faketime/faketime.cpp
107+
COPY faketime.cpp .
108+
RUN g++ -O2 -fPIC -shared -I \$JAVA_HOME/include -I \$JAVA_HOME/include/linux -olibfaketime.so faketime.cpp
109+
110+
ENV JVM_SUPPORT_RECOMMENDED_ARGS="-agentpath:/libfaketime.so=+2592000000"
111+
"""
112+
79113

114+
putBuilderCommand("mkdir -p $faketimeRoot", "")
115+
putBuilderCommand("cat > $faketimeDockerFilePath <<- 'EOF'\n" + faketimeDockerFile + "\nEOF", "")
116+
putBuilderCommand("cat > $faketimeAgentFilePath <<- 'EOF'\n" + faketimecpp + "\nEOF", "")
117+
putBuilderCommand("cd $faketimeRoot && docker build --tag $faketimeImageTag --build-arg JIRA_VERSION=$jsmVersion --build-arg ARTEFACT_NAME=$artifactName . && echo status:\$?", "status:0")
118+
putBuilderCommand("pkill tail", "")
119+
120+
assert build() : "Error building the image."
121+
122+
ArrayList<ImageSummary> images = dockerClient.images().content
123+
ImageSummary newImage = images.find {it.repoTags == [faketimeImageTag]}
124+
return newImage
80125
}
81126

82127

@@ -140,7 +185,6 @@ class ImageBuilder extends DoodContainer {
140185
@Override
141186
boolean runAfterDockerSetup(){
142187

143-
144188
builderCommands.each {cmd, expectedLastOut ->
145189
log.info("Running container command:" + cmd)
146190
log.info("\tExpecting last output from command:" + expectedLastOut)

src/main/resources/faketime.cpp

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright 2020 Odnoklassniki Ltd, Mail.Ru Group
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include <jvmti.h>
18+
#include <stdlib.h>
19+
#include <string.h>
20+
#include <time.h>
21+
22+
static jlong (*real_time_millis)(JNIEnv *, jclass) = NULL;
23+
static jlong (*real_nano_time_adjustment)(JNIEnv *, jclass, jlong) = NULL;
24+
25+
jlong JNICALL fake_time_millis(JNIEnv* env, jclass cls)
26+
{
27+
jclass systemClass = env->FindClass("java/lang/System");
28+
jmethodID getPropertyMethodId = env->GetStaticMethodID(systemClass, "getProperty", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
29+
jstring offsetPropertyName = env->NewStringUTF("faketime.offset.seconds");
30+
jstring offsetPropertyDefault = env->NewStringUTF("0");
31+
jstring offsetValue = (jstring)env->CallStaticObjectMethod(systemClass, getPropertyMethodId, offsetPropertyName, offsetPropertyDefault);
32+
const char *offset = env->GetStringUTFChars(offsetValue, NULL);
33+
jlong result = real_time_millis(env, cls) + atoll(offset);
34+
env->ReleaseStringUTFChars(offsetValue, offset);
35+
return result;
36+
}
37+
38+
jlong JNICALL fake_nano_time_adjustment(JNIEnv *env, jclass cls, jlong offset_seconds)
39+
{
40+
jclass systemClass = env->FindClass("java/lang/System");
41+
jmethodID getPropertyMethodId = env->GetStaticMethodID(systemClass, "getProperty", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
42+
jstring offsetPropertyName = env->NewStringUTF("faketime.offset.seconds");
43+
jstring offsetPropertyDefault = env->NewStringUTF("0");
44+
jstring offsetValue = (jstring)env->CallStaticObjectMethod(systemClass, getPropertyMethodId, offsetPropertyName, offsetPropertyDefault);
45+
const char *offset = env->GetStringUTFChars(offsetValue, NULL);
46+
jlong result = real_nano_time_adjustment(env, cls, offset_seconds) + atoll(offset) * 1000000;
47+
env->ReleaseStringUTFChars(offsetValue, offset);
48+
return result;
49+
}
50+
51+
void JNICALL NativeMethodBind(jvmtiEnv *jvmti, JNIEnv *env, jthread thread, jmethodID method,
52+
void *address, void **new_address_ptr)
53+
{
54+
char *name;
55+
if (jvmti->GetMethodName(method, &name, NULL, NULL) == 0)
56+
{
57+
if (real_time_millis == NULL && strcmp(name, "currentTimeMillis") == 0)
58+
{
59+
real_time_millis = (jlong(*)(JNIEnv *, jclass))address;
60+
*new_address_ptr = (void *)fake_time_millis;
61+
}
62+
else if (real_nano_time_adjustment == NULL && strcmp(name, "getNanoTimeAdjustment") == 0)
63+
{
64+
real_nano_time_adjustment = (jlong(*)(JNIEnv *, jclass, jlong))address;
65+
*new_address_ptr = (void *)fake_nano_time_adjustment;
66+
}
67+
jvmti->Deallocate((unsigned char *)name);
68+
}
69+
}
70+
71+
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
72+
{
73+
jvmtiEnv *jvmti;
74+
vm->GetEnv((void **)&jvmti, JVMTI_VERSION_1_0);
75+
76+
jvmtiCapabilities capabilities = {0};
77+
capabilities.can_generate_native_method_bind_events = 1;
78+
#if JNI_VERSION_9
79+
jvmtiCapabilities potential_capabilities;
80+
jvmti->GetPotentialCapabilities(&potential_capabilities);
81+
capabilities.can_generate_early_vmstart = potential_capabilities.can_generate_early_vmstart;
82+
#endif
83+
jvmti->AddCapabilities(&capabilities);
84+
85+
jvmtiEventCallbacks callbacks = {0};
86+
callbacks.NativeMethodBind = NativeMethodBind;
87+
jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
88+
89+
jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_NATIVE_METHOD_BIND, NULL);
90+
91+
return 0;
92+
}

0 commit comments

Comments
 (0)