diff --git a/DEVELOPING.md b/DEVELOPING.md
index 97529dfb..daded9bb 100644
--- a/DEVELOPING.md
+++ b/DEVELOPING.md
@@ -56,9 +56,15 @@ $ ./scripts/integration_test.sh $RUNTIME_IMAGE
**Run ONLY Local Docker integration tests:**
```bash
-$ ./scripts/local_integration_test.sh $RUNTIME_IMAGE
+$ ./scripts/local_runtimes_common_integration_test.sh $RUNTIME_IMAGE
```
+**Run ONLY Local shutdown tests:**
+```bash
+$ ./scripts/local_shutdown_test.sh $RUNTIME_IMAGE
+```
+
+
**Run ONLY App Engine flexible environment integration tests:**
```bash
$ ./scripts/ae_integration_test.sh $RUNTIME_IMAGE
diff --git a/java-runtimes-common/test-spring-application/pom.xml b/java-runtimes-common/test-spring-application/pom.xml
index b64e6cce..4a942945 100644
--- a/java-runtimes-common/test-spring-application/pom.xml
+++ b/java-runtimes-common/test-spring-application/pom.xml
@@ -35,12 +35,12 @@
com.google.cloud
google-cloud-monitoring
- 0.20.1-alpha
+ 0.22.0-alpha
- com.google.auth
- google-auth-library-credentials
- 0.7.0
+ com.google.cloud
+ google-cloud-logging
+ 1.4.0
diff --git a/java-runtimes-common/test-spring-application/src/main/java/com/google/cloud/runtimes/ExceptionController.java b/java-runtimes-common/test-spring-application/src/main/java/com/google/cloud/runtimes/ExceptionController.java
index ec81e63c..5b4bfab7 100644
--- a/java-runtimes-common/test-spring-application/src/main/java/com/google/cloud/runtimes/ExceptionController.java
+++ b/java-runtimes-common/test-spring-application/src/main/java/com/google/cloud/runtimes/ExceptionController.java
@@ -1,11 +1,11 @@
package com.google.cloud.runtimes;
-import static org.springframework.web.bind.annotation.RequestMethod.POST;
-
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
+import static org.springframework.web.bind.annotation.RequestMethod.POST;
+
@RestController
public class ExceptionController {
diff --git a/java-runtimes-common/test-spring-application/src/main/java/com/google/cloud/runtimes/HelloController.java b/java-runtimes-common/test-spring-application/src/main/java/com/google/cloud/runtimes/HelloController.java
index 55e6276b..d4779bd4 100644
--- a/java-runtimes-common/test-spring-application/src/main/java/com/google/cloud/runtimes/HelloController.java
+++ b/java-runtimes-common/test-spring-application/src/main/java/com/google/cloud/runtimes/HelloController.java
@@ -1,10 +1,10 @@
package com.google.cloud.runtimes;
-import static org.springframework.web.bind.annotation.RequestMethod.GET;
-
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
+import static org.springframework.web.bind.annotation.RequestMethod.GET;
+
@RestController
public class HelloController {
diff --git a/java-runtimes-common/test-spring-application/src/main/java/com/google/cloud/runtimes/MonitoringTestController.java b/java-runtimes-common/test-spring-application/src/main/java/com/google/cloud/runtimes/MonitoringTestController.java
index 7bf5b55c..2979b92e 100644
--- a/java-runtimes-common/test-spring-application/src/main/java/com/google/cloud/runtimes/MonitoringTestController.java
+++ b/java-runtimes-common/test-spring-application/src/main/java/com/google/cloud/runtimes/MonitoringTestController.java
@@ -2,6 +2,8 @@
import com.google.cloud.runtimes.stackdriver.StackDriverMonitoringService;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -9,15 +11,19 @@
import java.io.IOException;
import java.util.logging.Logger;
-import static com.google.cloud.ServiceOptions.getDefaultProjectId;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
@RestController
public class MonitoringTestController {
@Autowired
+ @Lazy
private StackDriverMonitoringService stackDriverMonitoringService;
+ @Autowired
+ @Qualifier("projectId")
+ private String projectId;
+
private static Logger LOG = Logger.getLogger(MonitoringTestController.class.getName());
public static class MonitoringTestRequest {
@@ -45,7 +51,7 @@ public String toString() {
public String handleMonitoringRequest(@RequestBody MonitoringTestRequest monitoringTestRequest) throws IOException, InterruptedException {
LOG.info(String.valueOf(monitoringTestRequest));
- stackDriverMonitoringService.createMetricAndInsertTestToken(getDefaultProjectId(),
+ stackDriverMonitoringService.createMetricAndInsertTestToken(projectId,
monitoringTestRequest.name,
monitoringTestRequest.token);
diff --git a/java-runtimes-common/test-spring-application/src/main/java/com/google/cloud/runtimes/stackdriver/StackDriverMonitoringService.java b/java-runtimes-common/test-spring-application/src/main/java/com/google/cloud/runtimes/stackdriver/StackDriverMonitoringService.java
index 21e98a77..b104feae 100644
--- a/java-runtimes-common/test-spring-application/src/main/java/com/google/cloud/runtimes/stackdriver/StackDriverMonitoringService.java
+++ b/java-runtimes-common/test-spring-application/src/main/java/com/google/cloud/runtimes/stackdriver/StackDriverMonitoringService.java
@@ -4,9 +4,16 @@
import com.google.api.MetricDescriptor;
import com.google.api.MonitoredResource;
import com.google.cloud.monitoring.v3.MetricServiceClient;
+import com.google.cloud.monitoring.v3.stub.MetricServiceStub;
+import com.google.cloud.runtimes.config.GcpConfiguration;
import com.google.monitoring.v3.*;
import com.google.protobuf.util.Timestamps;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import java.io.IOException;
@@ -15,29 +22,41 @@
@Service
public class StackDriverMonitoringService {
+ @Autowired
+ private ApplicationContext applicationContext;
+
@Value("${monitoring.write.retries}")
private int maxRetries;
private static Logger LOG = Logger.getLogger(StackDriverMonitoringService.class.getName());
+
public void createMetricAndInsertTestToken(String projectId, String metricType, long metricValue) throws IOException {
- MetricServiceClient metricServiceClient = MetricServiceClient.create();
int retries = maxRetries;
while (retries > 0) {
try {
CreateTimeSeriesRequest timeSeriesRequest = createTimeSeriesRequest(projectId, metricType, metricValue);
- metricServiceClient.createTimeSeries(timeSeriesRequest);
+ getClient().createTimeSeries(timeSeriesRequest);
LOG.info("Metric created with timeseries.");
return;
} catch (Exception e) {
LOG.warning("error creating timeseries request, retrying..." + e.getClass() + ": " + e.getMessage());
retries--;
if (retries == 0) {
- throw new IllegalStateException("Failed to store timeseries after "+ maxRetries +" attempts! Last error:", e);
+ throw new IllegalStateException("Failed to store timeseries after " + maxRetries + " attempts! Last error:", e);
}
}
}
}
+ /**
+ * This pairs up with the @Lazy annotation on {@link GcpConfiguration#getMetricServiceClient()}.
+ * As MetricServiceClient methods are all `final` and that breaks the "@Autowire @Lazy" combination,
+ * this is the only way to wire this bean lazily.
+ */
+ private MetricServiceClient getClient() {
+ return (MetricServiceClient) applicationContext.getBean("metricServiceClient");
+ }
+
private CreateTimeSeriesRequest createTimeSeriesRequest(String projectId, String metricType, Long metricValue) {
LOG.info("Creating time series to insert token: " + metricValue);
ProjectName projectName = ProjectName.create(projectId);
@@ -66,4 +85,5 @@ private CreateTimeSeriesRequest createTimeSeriesRequest(String projectId, String
.build();
}
+
}
diff --git a/openjdk-common/pom.xml b/openjdk-common/pom.xml
index 24396fca..860e84d1 100644
--- a/openjdk-common/pom.xml
+++ b/openjdk-common/pom.xml
@@ -31,6 +31,17 @@
+
+ maven-enforcer-plugin
+
+
+
+ enforce
+
+ validate
+
+
+
org.apache.maven.plugins
maven-assembly-plugin
diff --git a/openjdk8/pom.xml b/openjdk8/pom.xml
index bf55316b..deecf261 100644
--- a/openjdk8/pom.xml
+++ b/openjdk8/pom.xml
@@ -93,6 +93,10 @@
common-structure-test
integration-test
+
+ local-shutdown-test
+ integration-test
+
openjdk8-structure-test
diff --git a/openjdk9/pom.xml b/openjdk9/pom.xml
index fcec22a9..fd284bae 100644
--- a/openjdk9/pom.xml
+++ b/openjdk9/pom.xml
@@ -92,6 +92,10 @@
common-structure-test
integration-test
+
+ local-shutdown-test
+ integration-test
+
openjdk9-structure-test
diff --git a/pom.xml b/pom.xml
index 0c695479..00827a4c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,24 +67,16 @@
org.apache.maven.plugins
maven-enforcer-plugin
1.4.1
-
-
- enforce-maven
-
- enforce
-
-
-
-
- [3.0,)
-
-
+
+
+
+ [3.3,)
+
+
[1.8,)
-
-
-
-
-
+
+
+
@@ -191,6 +183,22 @@
+
+ local-shutdown-test
+
+ none
+
+ exec
+
+
+ ${project.parent.basedir}/scripts/local_shutdown_test.sh
+ ${project.build.outputDirectory}
+
+ ${docker.image.name}
+
+
+
diff --git a/scripts/gcloud-init.sh b/scripts/gcloud-init.sh
index 49da4885..c095030c 100755
--- a/scripts/gcloud-init.sh
+++ b/scripts/gcloud-init.sh
@@ -46,3 +46,4 @@ gcloud auth activate-service-account --key-file=$KEYFILE
gcloud config set project $GCP_PROJECT
gcloud config set compute/zone us-east1-b
gcloud components install beta kubectl -q
+gcloud components install beta container-builder-local
diff --git a/scripts/integration_test.sh b/scripts/integration_test.sh
index ae13c695..d0de962f 100755
--- a/scripts/integration_test.sh
+++ b/scripts/integration_test.sh
@@ -32,7 +32,7 @@ fi
# is not recommended
readonly gaeDeploymentVersion=$2
-${dir}/local_integration_test.sh ${imageUnderTest}
+${dir}/local_shutdown_test.sh ${imageUnderTest}
${dir}/ae_integration_test.sh ${imageUnderTest} ${gaeDeploymentVersion}
diff --git a/scripts/integration_test.yaml b/scripts/integration_test.yaml
index 511034fc..0968b816 100755
--- a/scripts/integration_test.yaml
+++ b/scripts/integration_test.yaml
@@ -9,7 +9,6 @@ steps:
- name: 'gcr.io/java-runtime-test/integration-test'
args:
- '--url=${_DEPLOYED_APP_URL}'
- - '--skip-custom-logging-tests' # blocked by b/33415496
- '--skip-standard-logging-tests' # blocked by b/33415496
- '--skip-custom-tests'
diff --git a/scripts/local_runtimes_common_integration_test.sh b/scripts/local_runtimes_common_integration_test.sh
new file mode 100755
index 00000000..953fb3e1
--- /dev/null
+++ b/scripts/local_runtimes_common_integration_test.sh
@@ -0,0 +1,109 @@
+#!/bin/bash
+
+# Copyright 2017 Google Inc. All rights reserved.
+
+# Licensed 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.
+
+
+# exit on command failure
+set -e
+
+readonly dir=$(dirname $0)
+readonly projectRoot="$dir/.."
+readonly testAppDir="$projectRoot/test-application"
+readonly deployDir="$testAppDir/target/deploy"
+
+APP_IMAGE='openjdk-local-integration'
+CONTAINER=${APP_IMAGE}-container
+OUTPUT_FILE=${CONTAINER}-output.txt
+DEPLOYMENT_TOKEN=$(uuidgen)
+
+readonly imageUnderTest=$1
+if [[ -z "$imageUnderTest" ]]; then
+ echo "Usage: ${0} "
+ exit 1
+fi
+
+if [[ ! -f $HOME/.config/gcloud/application_default_credentials.json ]]; then
+ # get default application credentials
+ gcloud auth application-default login
+fi
+
+# build the test app
+pushd ${testAppDir}
+mvn clean package -Ddeployment.token="${DEPLOYMENT_TOKEN}" -DskipTests --batch-mode
+popd
+
+# build app container locally
+pushd $deployDir
+export STAGING_IMAGE=$imageUnderTest
+envsubst < Dockerfile.in > Dockerfile
+echo "Building app container..."
+docker build -t $APP_IMAGE . || docker build -t $APP_IMAGE .
+
+docker rm -f $CONTAINER || echo "Integration-test-app container is not running, ready to start a new instance."
+
+# run app container locally to test shutdown logging
+echo "Starting app container..."
+docker run --rm --name $CONTAINER -p 8080 \
+ -e "SHUTDOWN_LOGGING_THREAD_DUMP=true" \
+ -e "SHUTDOWN_LOGGING_HEAP_INFO=true" \
+ -v "$HOME/.config/gcloud/:/root/.config/gcloud" $APP_IMAGE &> $OUTPUT_FILE &
+
+function waitForOutput() {
+ found_output='false'
+ for run in {1..10}
+ do
+ grep "$1" $OUTPUT_FILE && found_output='true' && break
+ sleep 1
+ done
+
+ if [ "$found_output" == "false" ]; then
+ cat $OUTPUT_FILE
+ echo "did not match '$1' in '$OUTPUT_FILE'"
+ exit 1
+ fi
+}
+
+waitForOutput 'Started Application'
+
+getPort() {
+ docker inspect --format='{{range $p, $conf := .NetworkSettings.Ports}}{{(index $conf 0).HostPort}}{{end}}' ${CONTAINER}
+}
+
+
+PORT=`getPort`
+
+nslookup `hostname` | grep Address | grep -v 127.0 | awk '{print $2}' > /tmp/myip
+MYIP=`cat /tmp/myip`
+
+DEPLOYED_APP_URL=http://$MYIP:$PORT
+
+echo app is deployed to $DEPLOYED_APP_URL, making sure it accepts connections
+
+
+until [[ $(curl --silent --fail "$DEPLOYED_APP_URL/deployment.token" | grep "$DEPLOYMENT_TOKEN") ]]; do
+ sleep 2
+done
+popd
+
+docker rm -f metadata || echo "ready to run local cloud builder"
+
+# run in cloud container builder
+echo "Running integration tests on application that is deployed at $DEPLOYED_APP_URL"
+echo `pwd`
+container-builder-local \
+ --config ${dir}/integration_test.yaml \
+ --substitutions "_DEPLOYED_APP_URL=$DEPLOYED_APP_URL" \
+ --dryrun=false \
+ ${dir}
\ No newline at end of file
diff --git a/scripts/local_integration_test.sh b/scripts/local_shutdown_test.sh
similarity index 100%
rename from scripts/local_integration_test.sh
rename to scripts/local_shutdown_test.sh
diff --git a/test-application/src/main/java/com/google/cloud/runtimes/LoggingTestController.java b/test-application/src/main/java/com/google/cloud/runtimes/LoggingTestController.java
new file mode 100644
index 00000000..98166192
--- /dev/null
+++ b/test-application/src/main/java/com/google/cloud/runtimes/LoggingTestController.java
@@ -0,0 +1,77 @@
+package com.google.cloud.runtimes;
+
+import com.google.cloud.MonitoredResource;
+import com.google.cloud.logging.LogEntry;
+import com.google.cloud.logging.Logging;
+import com.google.cloud.logging.Payload;
+import com.google.cloud.logging.Severity;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+
+import static org.springframework.web.bind.annotation.RequestMethod.POST;
+
+@RestController
+public class LoggingTestController {
+
+ @Autowired
+ @Lazy
+ private Logging logging;
+
+ private static Logger LOG = Logger.getLogger(LoggingTestController.class.getName());
+
+ public static class LoggingTestRequest {
+ private String level;
+ private String log_name;
+ private String token;
+
+ public void setLevel(String level) {
+ this.level = level;
+ }
+
+ public void setLog_name(String log_name) {
+ this.log_name = log_name;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+
+ @Override
+ public String toString() {
+ return "LoggingTestRequest{" +
+ "level='" + level + '\'' +
+ ", log_name='" + log_name + '\'' +
+ ", token='" + token + '\'' +
+ '}';
+ }
+ }
+
+ @RequestMapping(path = "/logging_custom", method = POST)
+ public String handleLoggingTestRequest(@RequestBody LoggingTestRequest loggingTestRequest) throws IOException, InterruptedException {
+ LOG.info(String.valueOf(loggingTestRequest));
+
+ List entries = new ArrayList<>();
+ Payload.StringPayload payload = Payload.StringPayload.of(loggingTestRequest.token);
+ Severity severity = Severity.valueOf(loggingTestRequest.level);
+ LogEntry entry = LogEntry.newBuilder(payload)
+ .setSeverity(severity)
+ .setLogName(loggingTestRequest.log_name)
+ .setResource(MonitoredResource.newBuilder("global").build())
+ .build();
+ entries.add(entry);
+ logging.write(entries);
+ LOG.info("Log written to StackDriver: " + entries);
+ return "OK";
+ }
+
+
+}
diff --git a/test-application/src/main/java/com/google/cloud/runtimes/config/GcpConfiguration.java b/test-application/src/main/java/com/google/cloud/runtimes/config/GcpConfiguration.java
new file mode 100644
index 00000000..4e9bec77
--- /dev/null
+++ b/test-application/src/main/java/com/google/cloud/runtimes/config/GcpConfiguration.java
@@ -0,0 +1,38 @@
+package com.google.cloud.runtimes.config;
+
+import com.google.cloud.logging.Logging;
+import com.google.cloud.logging.LoggingOptions;
+import com.google.cloud.monitoring.v3.MetricServiceClient;
+import com.google.cloud.monitoring.v3.MetricServiceSettings;
+import com.google.cloud.monitoring.v3.stub.MetricServiceStub;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Lazy;
+
+import java.io.IOException;
+
+import static com.google.cloud.ServiceOptions.getDefaultProjectId;
+
+@Configuration
+public class GcpConfiguration {
+
+ @Bean
+ @Lazy
+ public Logging getLogging() {
+ LoggingOptions options = LoggingOptions.getDefaultInstance();
+ return options.getService();
+ }
+
+ @Bean(name = "metricServiceClient")
+ @Lazy
+ public MetricServiceClient getMetricServiceClient() throws IOException {
+ return MetricServiceClient.create();
+ }
+
+ @Qualifier("projectId")
+ @Bean
+ public String getProjectId() {
+ return getDefaultProjectId();
+ }
+}