diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml index 7c20f065f48..12d9f01d7b7 100644 --- a/.github/sync-repo-settings.yaml +++ b/.github/sync-repo-settings.yaml @@ -7,6 +7,7 @@ branchProtectionRules: requiredStatusCheckContexts: - 'Kokoro CI - Java 11' - 'Kokoro CI - Java 8' + - 'Kokoro CI - Java 17' - 'Kokoro CI - Lint' - 'cla/google' requiredApprovingReviewCount: 1 diff --git a/.kokoro/java17/common.cfg b/.kokoro/java17/common.cfg new file mode 100644 index 00000000000..0ee5c4bf978 --- /dev/null +++ b/.kokoro/java17/common.cfg @@ -0,0 +1,45 @@ +# Copyright 2022 Google LLC +# +# 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. + +# Format: //devtools/kokoro/config/proto/build.proto + +# Build timeout of 5 hours +timeout_mins: 360 + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "java-docs-samples/.kokoro/trampoline.sh" + +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Set the JAVA VERSION env var. +env_vars: { + key: "JAVA_VERSION" + value: "1.8,11,17" +} + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/java17" +} + +# Access btlr binaries used in the tests +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/btlr" diff --git a/.kokoro/java17/continuous.cfg b/.kokoro/java17/continuous.cfg new file mode 100644 index 00000000000..d9a8c1bffdc --- /dev/null +++ b/.kokoro/java17/continuous.cfg @@ -0,0 +1,27 @@ +# Copyright 2022 Google LLC +# +# 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. + +# Format: //devtools/kokoro/config/proto/build.proto + +# Tell trampoline which tests to run. +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/java-docs-samples/.kokoro/tests/run_tests.sh" +} + +# Only diff from previous commit +env_vars: { + key: "GIT_DIFF" + value: "HEAD~.. ." +} diff --git a/.kokoro/java17/periodic.cfg b/.kokoro/java17/periodic.cfg new file mode 100644 index 00000000000..65cbde07819 --- /dev/null +++ b/.kokoro/java17/periodic.cfg @@ -0,0 +1,22 @@ +# Copyright 2022 Google LLC +# +# 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. + +# Format: //devtools/kokoro/config/proto/build.proto + +# Tell the trampoline which build file to use. +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/java-docs-samples/.kokoro/tests/run_tests.sh" +} + diff --git a/.kokoro/java17/presubmit.cfg b/.kokoro/java17/presubmit.cfg new file mode 100644 index 00000000000..f642ed9fdf6 --- /dev/null +++ b/.kokoro/java17/presubmit.cfg @@ -0,0 +1,26 @@ +# Copyright 2022 Google LLC +# +# 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. + +# Format: //devtools/kokoro/config/proto/build.proto + +# Tell the trampoline which build file to use. +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/java-docs-samples/.kokoro/tests/run_tests.sh" +} +# Only diff from main +env_vars: { + key: "GIT_DIFF" + value: "origin/main... ." +} diff --git a/.kokoro/tests/run_test_java.sh b/.kokoro/tests/run_test_java.sh index 39cf1f78775..f645e881171 100755 --- a/.kokoro/tests/run_test_java.sh +++ b/.kokoro/tests/run_test_java.sh @@ -18,7 +18,7 @@ SCRIPT_DIR="$(dirname $0)/" # Fail the tests if no Java version was found. POM_JAVA=$(grep -oP '(?<=).*?(?=)' pom.xml) -ALLOWED_VERSIONS=("1.8" "11") +ALLOWED_VERSIONS=("1.8" "11" "17") # shellcheck disable=SC2199 # shellcheck disable=SC2076 if [[ "$POM_JAVA" = "" ]] || [[ ! "${ALLOWED_VERSIONS[@]}" =~ "${POM_JAVA}" ]]; then diff --git a/.kokoro/tests/run_tests.sh b/.kokoro/tests/run_tests.sh index 73f3690fbf8..a7d7071d0d3 100755 --- a/.kokoro/tests/run_tests.sh +++ b/.kokoro/tests/run_tests.sh @@ -45,6 +45,8 @@ if [[ "$SCRIPT_DEBUG" != "true" ]]; then # Update `gcloud` and log versioning for debugging apt update && apt -y upgrade google-cloud-sdk + echo "********** GIT INFO ***********" + git version echo "********** GCLOUD INFO ***********" gcloud -v echo "********** MAVEN INFO ***********" @@ -115,6 +117,8 @@ fi echo -e "\n******************** TESTING PROJECTS ********************" test_prog="$PWD/.kokoro/tests/run_test_java.sh" +git config --global --add safe.directory $PWD + # Use btlr to run all the tests in each folder echo "btlr" "${btlr_args[@]}" -- "${test_prog}" btlr "${btlr_args[@]}" -- "${test_prog}" diff --git a/README.md b/README.md index 9f8c938af38..e8a06cd1398 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,10 @@ This repository holds sample code written in Java that demonstrates the Some samples have accompanying guides on . See respective README files for details. +## Google Cloud Samples + +To browse ready to use code samples check [Google Cloud Samples](https://cloud.google.com/docs/samples?l=java). + ## Set Up 1. [Set up your Java Development Environment](https://cloud.google.com/java/docs/setup) diff --git a/appengine-java11-bundled-services/README.md b/appengine-java11-bundled-services/README.md index e07e46db7c9..700f8fb1de2 100644 --- a/appengine-java11-bundled-services/README.md +++ b/appengine-java11-bundled-services/README.md @@ -39,9 +39,10 @@ access control, billing, and services. ## Development differences between App Engine Java8 and Java11 Bundled Services -The only difference between a Java8 application and a Java11 application is in the `appengine-web.xml` file +The only differences between a Java8 application and a Java11 application are the addition of the bundled services JAR, and an added line in the `appengine-web.xml` file where you need to define the Java11 runtime and declare you need the App Engine APIs: +In `appengine-web.xml`: ```XML java11 @@ -49,10 +50,18 @@ where you need to define the Java11 runtime and declare you need the App Engine ``` -While the Java11 runtime is in Beta, in order to deploy the application, you can use the `beta` value for the `gcloudMode` Cloud SDK parameter like: +In your `pom.xml`'s ``: +```XML + + com.google.appengine + appengine-api-1.0-sdk + 2.0.4 + +``` + ```shell - mvn appengine:deploy -Dapp.deploy.gcloudMode=beta + mvn appengine:deploy ``` @@ -72,17 +81,17 @@ This sample demonstrates how to use the App Engine Datastore APIs in a Java11 we You can execute the following steps to transform the java8 appengine-web.xml file to a java11 appengine-web.xml file: - ```shell - git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git - cd java-docs-samples - cp -pr appengine-java8 /tmp/java11-samples - cd /tmp/java11-samples - # On Linux: - shopt -s globstar dotglob - for f in **/appengine-web.xml; do sed -i 's.java8.java11true.' ${f}; done - # on MacOS - for f in **/appengine-web.xml; do sed -i'' -e 's.java8.java11true.' ${f}; done - ``` +```shell +git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git +cd java-docs-samples +cp -pr appengine-java8 /tmp/java11-samples +cd /tmp/java11-samples +# On Linux: +shopt -s globstar dotglob +for f in **/appengine-web.xml; do sed -i 's.java8.java11true.' ${f}; done +# on MacOS +for f in **/appengine-web.xml; do sed -i'' -e 's.java8.java11true.' ${f}; done + ``` You will see in the `tmp/java11` directory all the correct code samples to compile and deploy to the Java11 AppEngine runtime, with bundled services. Just follow the same documentation as the [Java8 samples][java8-samples]. diff --git a/appengine-java11-bundled-services/datastore/README.md b/appengine-java11-bundled-services/datastore/README.md index 38fe704b9f8..bbf66ef0e81 100644 --- a/appengine-java11-bundled-services/datastore/README.md +++ b/appengine-java11-bundled-services/datastore/README.md @@ -39,10 +39,7 @@ To see the results of the sample application, open ## Deploying -In the following command, replace YOUR-PROJECT-ID with your -[Google Cloud Project ID](https://developers.google.com/console/help/new/#projectnumber) -and SOME-VERSION with a valid version number. - ```sh mvn clean package appengine:deploy +mvn appengine:deployIndex ``` diff --git a/appengine-java11-bundled-services/datastore/pom.xml b/appengine-java11-bundled-services/datastore/pom.xml index 6b05b4830b5..47624da98de 100644 --- a/appengine-java11-bundled-services/datastore/pom.xml +++ b/appengine-java11-bundled-services/datastore/pom.xml @@ -126,10 +126,15 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java11/cloudsql/pom.xml b/appengine-java11/cloudsql/pom.xml index 06be5c6ba5a..40a7f88b63d 100644 --- a/appengine-java11/cloudsql/pom.xml +++ b/appengine-java11/cloudsql/pom.xml @@ -73,13 +73,13 @@ mysql mysql-connector-java - 8.0.28 + 8.0.29 provided com.google.cloud.sql mysql-socket-factory-connector-j-8 - 1.5.0 + 1.6.0 provided @@ -92,7 +92,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test @@ -156,7 +156,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG cloudsql diff --git a/appengine-java11/gaeinfo/pom.xml b/appengine-java11/gaeinfo/pom.xml index 0b05b4ca69c..d73205a3363 100644 --- a/appengine-java11/gaeinfo/pom.xml +++ b/appengine-java11/gaeinfo/pom.xml @@ -84,7 +84,7 @@ Copyright 2019 Google LLC com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG gaeinfo diff --git a/appengine-java11/guestbook-cloud-firestore/pom.xml b/appengine-java11/guestbook-cloud-firestore/pom.xml index cc659d64be1..12cc6bdab0c 100644 --- a/appengine-java11/guestbook-cloud-firestore/pom.xml +++ b/appengine-java11/guestbook-cloud-firestore/pom.xml @@ -91,10 +91,15 @@ guestbook + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG guestbook diff --git a/appengine-java11/helloworld-servlet/pom.xml b/appengine-java11/helloworld-servlet/pom.xml index 7fd26821f8d..b2050c08b25 100644 --- a/appengine-java11/helloworld-servlet/pom.xml +++ b/appengine-java11/helloworld-servlet/pom.xml @@ -67,10 +67,15 @@ limitations under the License. helloworld + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG helloworld diff --git a/appengine-java11/http-server/pom.xml b/appengine-java11/http-server/pom.xml index 4a8724776d2..53a51c8dcbc 100644 --- a/appengine-java11/http-server/pom.xml +++ b/appengine-java11/http-server/pom.xml @@ -38,7 +38,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG http-server diff --git a/appengine-java11/kotlin-ktor/app.yaml b/appengine-java11/kotlin-ktor/app.yaml index 7d200ea8834..ad7258b8290 100644 --- a/appengine-java11/kotlin-ktor/app.yaml +++ b/appengine-java11/kotlin-ktor/app.yaml @@ -13,4 +13,3 @@ # limitations under the License. runtime: java11 -entrypoint: 'java -jar target/kotlin-ktor-0.0.1-jar-with-dependencies.jar' diff --git a/appengine-java11/kotlin-ktor/pom.xml b/appengine-java11/kotlin-ktor/pom.xml index f459f8ee62d..e480653d73d 100644 --- a/appengine-java11/kotlin-ktor/pom.xml +++ b/appengine-java11/kotlin-ktor/pom.xml @@ -31,156 +31,138 @@ limitations under the License. + 11 + 11 + 2.0.2-eap-389 official + 1.6.21 + 1.2.11 UTF-8 true - io.ktor.server.netty.EngineMain - 11 - 11 + com.example.appengine.ApplicationKt - repo1 - https://jcenter.bintray.com - true - false - - - repo2 - https://kotlin.bintray.com/ktor - true - false + ktor_eap + https://maven.pkg.jetbrains.space/public/p/ktor/eap + + true + + + true + - - - - io.ktor - ktor-bom - 1.6.8 - pom - import - - - - - org.jetbrains.kotlin - kotlin-stdlib-jdk8 - 1.6.20 - - - io.ktor - ktor-server-netty - - - ch.qos.logback - logback-classic - 1.3.0-alpha11 + io.ktor + ktor-server-core-jvm + ${ktor_version} - io.ktor - ktor-server-core + io.ktor + ktor-server-netty-jvm + ${ktor_version} - io.ktor - ktor-server-host-common + ch.qos.logback + logback-classic + ${logback_version} - io.ktor - ktor-server-tests - test + io.ktor + ktor-server-tests-jvm + ${ktor_version} + test - org.jetbrains.kotlin - kotlin-test-junit - 1.6.20 - test + org.jetbrains.kotlin + kotlin-test-junit + ${kotlin_version} + test - - - - - ${project.basedir}/src - ${project.basedir}/test + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin - - ${project.basedir}/resources - + + ${project.basedir}/src/main/resources + + - - org.apache.maven.plugins - maven-surefire-plugin - - - maven-compiler-plugin - 1.81.8 - - - kotlin-maven-plugin - org.jetbrains.kotlin - 1.6.20 - - - compile - compile + + kotlin-maven-plugin + org.jetbrains.kotlin + ${kotlin_version} - enable + 1.8 - - - test-compile - test-compile + + + compile + compile + + compile + + + + test-compile + test-compile + + test-compile + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.0.0 + + + + java + + + - enable + ${main.class} - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.2.2 - - - - true - ${main.class} - - - - - - org.apache.maven.plugins - maven-assembly-plugin - 3.3.0 - - - make-assembly - package - single + + + org.apache.maven.plugins + maven-assembly-plugin + 2.6 - - - ${main.class} - - - - jar-with-dependencies - + + jar-with-dependencies + + + + true + ${main.class} + + - - - + + + assemble-all + package + + single + + + + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG kotlin-ktor diff --git a/appengine-java11/kotlin-ktor/src/Application.kt b/appengine-java11/kotlin-ktor/src/main/kotlin/com/example/appengine/Application.kt similarity index 67% rename from appengine-java11/kotlin-ktor/src/Application.kt rename to appengine-java11/kotlin-ktor/src/main/kotlin/com/example/appengine/Application.kt index c5d623b1cba..3325d31456c 100644 --- a/appengine-java11/kotlin-ktor/src/Application.kt +++ b/appengine-java11/kotlin-ktor/src/main/kotlin/com/example/appengine/Application.kt @@ -14,22 +14,16 @@ package com.example.appengine -import io.ktor.application.Application -import io.ktor.application.call -import io.ktor.application.install import io.ktor.http.ContentType -import io.ktor.response.respondText -import io.ktor.routing.get -import io.ktor.routing.routing -import io.ktor.server.engine.ShutDownUrl -import io.ktor.server.netty.EngineMain +import io.ktor.server.application.* +import io.ktor.server.engine.* +import io.ktor.server.response.* +import io.ktor.server.routing.* -fun main(args: Array): Unit = EngineMain.main(args) +fun main(args: Array): Unit = io.ktor.server.netty.EngineMain.main(args) -@Suppress("unused") // Referenced in application.conf -@kotlin.jvm.JvmOverloads -fun Application.module(testing: Boolean = false) { - install(ShutDownUrl.ApplicationCallFeature) { +fun Application.module() { + install(ShutDownUrl.ApplicationCallPlugin) { // The URL that will be intercepted. You can also use the // application.conf's ktor.deployment.shutdown.url key. shutDownUrl = "/_ah/stop" @@ -37,10 +31,10 @@ fun Application.module(testing: Boolean = false) { // A function that will be executed to get the exit code of the process exitCodeSupplier = { 0 } // ApplicationCall.() -> Int } - routing { get("/") { call.respondText("Hello World!", contentType = ContentType.Text.Plain) } } } + diff --git a/appengine-java11/kotlin-ktor/src/main/resources/application.conf b/appengine-java11/kotlin-ktor/src/main/resources/application.conf new file mode 100644 index 00000000000..4cd0bfa8317 --- /dev/null +++ b/appengine-java11/kotlin-ktor/src/main/resources/application.conf @@ -0,0 +1,26 @@ +# Copyright 2022 Google LLC +# +# 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. + +ktor { + deployment { + port = 8080 + port = ${?PORT} + + shutdown.url = "/_ah/stop" + } + application { + modules = [ com.example.appengine.ApplicationKt.module ] + } +} + diff --git a/appengine-java11/kotlin-ktor/src/main/resources/logback.xml b/appengine-java11/kotlin-ktor/src/main/resources/logback.xml new file mode 100644 index 00000000000..bdbb64ec4ba --- /dev/null +++ b/appengine-java11/kotlin-ktor/src/main/resources/logback.xml @@ -0,0 +1,12 @@ + + + + %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + diff --git a/appengine-java11/kotlin-ktor/src/test/ApplicationTest.kt b/appengine-java11/kotlin-ktor/src/test/ApplicationTest.kt new file mode 100644 index 00000000000..9450ef165bd --- /dev/null +++ b/appengine-java11/kotlin-ktor/src/test/ApplicationTest.kt @@ -0,0 +1,35 @@ +// Copyright 2022 Google LLC +// +// 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. + +package com.example.appengine + +import io.ktor.server.http.HttpMethod +import io.ktor.server.http.HttpStatusCode +import io.ktor.server.testing.handleRequest +import io.ktor.server.testing.withTestApplication +import kotlin.test.Test +import kotlin.test.assertEquals + +class ApplicationTest { + @Test + fun testRoot() { + withTestApplication({ module(testing = true) }) { + handleRequest(HttpMethod.Get, "/").apply { + assertEquals(HttpStatusCode.OK, response.status()) + assertEquals("Hello World!", response.content) + } + } + } +} + diff --git a/appengine-java11/micronaut-helloworld/pom.xml b/appengine-java11/micronaut-helloworld/pom.xml index 700a87ae77f..8faafbed2aa 100644 --- a/appengine-java11/micronaut-helloworld/pom.xml +++ b/appengine-java11/micronaut-helloworld/pom.xml @@ -33,7 +33,7 @@ com.example.appengine.Application 11 11 - 3.4.1 + 3.4.3 @@ -86,7 +86,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG micronaut-helloworld diff --git a/appengine-java11/oauth2/pom.xml b/appengine-java11/oauth2/pom.xml index fd4179456d3..a4688d78266 100644 --- a/appengine-java11/oauth2/pom.xml +++ b/appengine-java11/oauth2/pom.xml @@ -57,14 +57,14 @@ com.google.oauth-client google-oauth-client - 1.33.2 + 1.33.3 provided com.google.oauth-client google-oauth-client-servlet - 1.33.2 + 1.33.3 provided @@ -77,7 +77,7 @@ com.google.http-client google-http-client-jackson2 - 1.41.6 + 1.41.8 @@ -101,10 +101,15 @@ oauth2 + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG oauth2 diff --git a/appengine-java11/quarkus-helloworld/pom.xml b/appengine-java11/quarkus-helloworld/pom.xml index 8841535d0a8..66cbce5ff2c 100644 --- a/appengine-java11/quarkus-helloworld/pom.xml +++ b/appengine-java11/quarkus-helloworld/pom.xml @@ -32,7 +32,7 @@ limitations under the License. 2.22.2 11 11 - 2.8.0.Final + 2.9.0.Final UTF-8 @@ -92,7 +92,7 @@ limitations under the License. com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG quarkus-helloworld diff --git a/appengine-java11/spanner/pom.xml b/appengine-java11/spanner/pom.xml index b908c9b7846..d14382d263e 100644 --- a/appengine-java11/spanner/pom.xml +++ b/appengine-java11/spanner/pom.xml @@ -108,7 +108,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG spanner diff --git a/appengine-java11/sparkjava-helloworld/pom.xml b/appengine-java11/sparkjava-helloworld/pom.xml index 6969e3e012f..6cbfa144f30 100644 --- a/appengine-java11/sparkjava-helloworld/pom.xml +++ b/appengine-java11/sparkjava-helloworld/pom.xml @@ -101,7 +101,7 @@ limitations under the License. com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG ${project.build.directory}/sparkjava-helloworld-1.0-jar-with-dependencies.jar diff --git a/appengine-java11/springboot-helloworld/pom.xml b/appengine-java11/springboot-helloworld/pom.xml index a186992352a..7abe625b9ef 100644 --- a/appengine-java11/springboot-helloworld/pom.xml +++ b/appengine-java11/springboot-helloworld/pom.xml @@ -92,7 +92,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 + + 4.0.0 + war + 1.0-SNAPSHOT + com.example.appengine + appengine-datastore-j17 + + + + com.google.cloud.samples + shared-configuration + 1.2.0 + + + + 11 + 11 + + + + + com.google.appengine + appengine-api-1.0-sdk + 2.0.4 + + + + javax.servlet + javax.servlet-api + 3.1.0 + jar + provided + + + + com.google.auto.value + auto-value + 1.9 + provided + + + + com.google.auto.value + auto-value-annotations + 1.9 + + + + com.google.code.findbugs + jsr305 + 3.0.2 + + + + com.google.guava + guava + 31.0.1-jre + + + + joda-time + joda-time + 2.10.13 + + + + + junit + junit + 4.13.2 + test + + + org.mockito + mockito-all + 1.10.19 + test + + + + com.google.appengine + appengine-testing + 2.0.4 + test + + + com.google.appengine + appengine-api-stubs + 2.0.4 + test + + + com.google.appengine + appengine-tools-sdk + 2.0.4 + test + + + com.google.truth + truth + 1.1.3 + test + + + + + + ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + + maven-war-plugin + 3.3.1 + + + default-war + package + + war + + + + + + com.google.cloud.tools + appengine-maven-plugin + 2.4.1 + + GCLOUD_CONFIG + GCLOUD_CONFIG + beta + true + true + + + + + maven-compiler-plugin + 3.8.1 + + + + com.google.auto.value + auto-value + 1.9 + + + + + + org.eclipse.jetty + jetty-maven-plugin + 9.4.44.v20210927 + + + + diff --git a/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/AbstractGuestbook.java b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/AbstractGuestbook.java new file mode 120000 index 00000000000..59b8bfc0057 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/AbstractGuestbook.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/main/java/com/example/appengine/AbstractGuestbook.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/AbstractGuestbookServlet.java b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/AbstractGuestbookServlet.java new file mode 120000 index 00000000000..a91e7e09658 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/AbstractGuestbookServlet.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/main/java/com/example/appengine/AbstractGuestbookServlet.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/Greeting.java b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/Greeting.java new file mode 120000 index 00000000000..9c59e2047b8 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/Greeting.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/main/java/com/example/appengine/Greeting.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/Guestbook.java b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/Guestbook.java new file mode 120000 index 00000000000..2e3d8e4b664 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/Guestbook.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/main/java/com/example/appengine/Guestbook.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/GuestbookServlet.java b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/GuestbookServlet.java new file mode 120000 index 00000000000..752427a8057 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/GuestbookServlet.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/main/java/com/example/appengine/GuestbookServlet.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/GuestbookStrong.java b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/GuestbookStrong.java new file mode 120000 index 00000000000..fef89e36ba8 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/GuestbookStrong.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/main/java/com/example/appengine/GuestbookStrong.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/GuestbookStrongServlet.java b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/GuestbookStrongServlet.java new file mode 120000 index 00000000000..f2d53752473 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/GuestbookStrongServlet.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/main/java/com/example/appengine/GuestbookStrongServlet.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/ListPeopleServlet.java b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/ListPeopleServlet.java new file mode 120000 index 00000000000..fc42fbc3dcd --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/ListPeopleServlet.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/main/java/com/example/appengine/ListPeopleServlet.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/ProjectionServlet.java b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/ProjectionServlet.java new file mode 120000 index 00000000000..ceb105b6acf --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/ProjectionServlet.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/main/java/com/example/appengine/ProjectionServlet.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/StartupServlet.java b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/StartupServlet.java new file mode 120000 index 00000000000..eae75ba9016 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/StartupServlet.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/main/java/com/example/appengine/StartupServlet.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/StatsServlet.java b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/StatsServlet.java new file mode 120000 index 00000000000..6502647321e --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/main/java/com/example/appengine/StatsServlet.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/main/java/com/example/appengine/StatsServlet.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/main/java/com/example/time/Clock.java b/appengine-java17-bundled-services/datastore/src/main/java/com/example/time/Clock.java new file mode 120000 index 00000000000..2413e2ddc58 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/main/java/com/example/time/Clock.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/main/java/com/example/time/Clock.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/main/java/com/example/time/SystemClock.java b/appengine-java17-bundled-services/datastore/src/main/java/com/example/time/SystemClock.java new file mode 120000 index 00000000000..4193c4824ac --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/main/java/com/example/time/SystemClock.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/main/java/com/example/time/SystemClock.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/main/java/com/example/time/testing/FakeClock.java b/appengine-java17-bundled-services/datastore/src/main/java/com/example/time/testing/FakeClock.java new file mode 120000 index 00000000000..b963230e3c4 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/main/java/com/example/time/testing/FakeClock.java @@ -0,0 +1 @@ +../../../../../../../../../appengine-java8/datastore/src/main/java/com/example/time/testing/FakeClock.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/main/webapp/WEB-INF/appengine-web.xml b/appengine-java17-bundled-services/datastore/src/main/webapp/WEB-INF/appengine-web.xml new file mode 100644 index 00000000000..d934ac71f3a --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/main/webapp/WEB-INF/appengine-web.xml @@ -0,0 +1,17 @@ + + + + java17 + true + diff --git a/appengine-java17-bundled-services/datastore/src/main/webapp/WEB-INF/datastore-indexes.xml b/appengine-java17-bundled-services/datastore/src/main/webapp/WEB-INF/datastore-indexes.xml new file mode 120000 index 00000000000..73f343b36e4 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/main/webapp/WEB-INF/datastore-indexes.xml @@ -0,0 +1 @@ +../../../../../../appengine-java8/datastore/src/main/webapp/WEB-INF/datastore-indexes.xml \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/main/webapp/WEB-INF/web.xml b/appengine-java17-bundled-services/datastore/src/main/webapp/WEB-INF/web.xml new file mode 120000 index 00000000000..6c846cab2a4 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1 @@ +../../../../../../appengine-java8/datastore/src/main/webapp/WEB-INF/web.xml \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/main/webapp/guestbook.jsp b/appengine-java17-bundled-services/datastore/src/main/webapp/guestbook.jsp new file mode 120000 index 00000000000..dc4c9b4a240 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/main/webapp/guestbook.jsp @@ -0,0 +1 @@ +../../../../../appengine-java8/datastore/src/main/webapp/guestbook.jsp \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/EntitiesTest.java b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/EntitiesTest.java new file mode 120000 index 00000000000..6acecc301aa --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/EntitiesTest.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/test/java/com/example/appengine/EntitiesTest.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/GuestbookStrongTest.java b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/GuestbookStrongTest.java new file mode 120000 index 00000000000..3541f8f82bb --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/GuestbookStrongTest.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/test/java/com/example/appengine/GuestbookStrongTest.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/GuestbookTest.java b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/GuestbookTest.java new file mode 120000 index 00000000000..015e621216b --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/GuestbookTest.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/test/java/com/example/appengine/GuestbookTest.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/IndexesTest.java b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/IndexesTest.java new file mode 120000 index 00000000000..2530a9646f2 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/IndexesTest.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/test/java/com/example/appengine/IndexesTest.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/ListPeopleServletTest.java b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/ListPeopleServletTest.java new file mode 120000 index 00000000000..e32bdc25149 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/ListPeopleServletTest.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/test/java/com/example/appengine/ListPeopleServletTest.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/MetadataEntityGroupTest.java b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/MetadataEntityGroupTest.java new file mode 120000 index 00000000000..97be437f7c1 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/MetadataEntityGroupTest.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/test/java/com/example/appengine/MetadataEntityGroupTest.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/MetadataKindsTest.java b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/MetadataKindsTest.java new file mode 120000 index 00000000000..aedd248bc2b --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/MetadataKindsTest.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/test/java/com/example/appengine/MetadataKindsTest.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/MetadataNamespacesTest.java b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/MetadataNamespacesTest.java new file mode 120000 index 00000000000..02eea767d46 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/MetadataNamespacesTest.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/test/java/com/example/appengine/MetadataNamespacesTest.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/MetadataPropertiesTest.java b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/MetadataPropertiesTest.java new file mode 120000 index 00000000000..d94057bf260 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/MetadataPropertiesTest.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/test/java/com/example/appengine/MetadataPropertiesTest.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/ProjectionServletTest.java b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/ProjectionServletTest.java new file mode 120000 index 00000000000..f442793b9c6 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/ProjectionServletTest.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/test/java/com/example/appengine/ProjectionServletTest.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/ProjectionTest.java b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/ProjectionTest.java new file mode 120000 index 00000000000..99cd3ef139a --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/ProjectionTest.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/test/java/com/example/appengine/ProjectionTest.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/QueriesTest.java b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/QueriesTest.java new file mode 120000 index 00000000000..2b4c7b4c7fc --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/QueriesTest.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/test/java/com/example/appengine/QueriesTest.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/ReadPolicyTest.java b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/ReadPolicyTest.java new file mode 120000 index 00000000000..84a38cfa093 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/ReadPolicyTest.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/test/java/com/example/appengine/ReadPolicyTest.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/StartupServletTest.java b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/StartupServletTest.java new file mode 120000 index 00000000000..cc898f78bbb --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/StartupServletTest.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/test/java/com/example/appengine/StartupServletTest.java \ No newline at end of file diff --git a/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/TransactionsTest.java b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/TransactionsTest.java new file mode 120000 index 00000000000..8c3e828ead3 --- /dev/null +++ b/appengine-java17-bundled-services/datastore/src/test/java/com/example/appengine/TransactionsTest.java @@ -0,0 +1 @@ +../../../../../../../../appengine-java8/datastore/src/test/java/com/example/appengine/TransactionsTest.java \ No newline at end of file diff --git a/appengine-java8/analytics/pom.xml b/appengine-java8/analytics/pom.xml index 61d62586712..c7f58045cad 100644 --- a/appengine-java8/analytics/pom.xml +++ b/appengine-java8/analytics/pom.xml @@ -87,7 +87,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test @@ -107,10 +107,15 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG analytics diff --git a/appengine-java8/appidentity/pom.xml b/appengine-java8/appidentity/pom.xml index e9d76a9bb9a..e6a15292f82 100644 --- a/appengine-java8/appidentity/pom.xml +++ b/appengine-java8/appidentity/pom.xml @@ -105,10 +105,15 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/bigquery/pom.xml b/appengine-java8/bigquery/pom.xml index 9ce1b14361a..2a24e6d5022 100644 --- a/appengine-java8/bigquery/pom.xml +++ b/appengine-java8/bigquery/pom.xml @@ -57,7 +57,7 @@ com.google.cloud google-cloud-monitoring - 3.2.7 + 3.2.9 @@ -94,7 +94,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test @@ -117,7 +117,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/bigtable/build.gradle b/appengine-java8/bigtable/build.gradle index 4cfaf44aba1..63332506c3e 100644 --- a/appengine-java8/bigtable/build.gradle +++ b/appengine-java8/bigtable/build.gradle @@ -18,7 +18,7 @@ buildscript { // Configuration for building mavenCentral() } dependencies { - classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.4.2' + classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.4.3' classpath 'org.akhikhl.gretty:gretty:+' } } @@ -46,7 +46,7 @@ repositories { dependencies { compile group: 'com.google.cloud.bigtable', name: 'bigtable-hbase-1.2', version:'1.0.0-pre3' - compile group: 'org.apache.hbase', name: 'hbase-client', version:'2.4.11' + compile group: 'org.apache.hbase', name: 'hbase-client', version:'2.4.12' compile group: 'io.netty', name: 'netty-tcnative-boringssl-static', version:'2.0.50.Final' compile group: 'jstl', name: 'jstl', version:'1.2' diff --git a/appengine-java8/bigtable/pom.xml b/appengine-java8/bigtable/pom.xml index 61fe21a82b9..2ede3685309 100644 --- a/appengine-java8/bigtable/pom.xml +++ b/appengine-java8/bigtable/pom.xml @@ -47,7 +47,7 @@ limitations under the License. com.google.cloud.bigtable bigtable-hbase-1.x-hadoop - 2.1.0 + 2.2.0 @@ -138,7 +138,7 @@ limitations under the License. com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/datastore-indexes-exploding/pom.xml b/appengine-java8/datastore-indexes-exploding/pom.xml index ff5d41082e5..aea3f4099ec 100644 --- a/appengine-java8/datastore-indexes-exploding/pom.xml +++ b/appengine-java8/datastore-indexes-exploding/pom.xml @@ -92,10 +92,15 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/datastore-indexes-perfect/pom.xml b/appengine-java8/datastore-indexes-perfect/pom.xml index d7f1f4671a8..cf5bfdc4200 100644 --- a/appengine-java8/datastore-indexes-perfect/pom.xml +++ b/appengine-java8/datastore-indexes-perfect/pom.xml @@ -92,10 +92,15 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/datastore-indexes/pom.xml b/appengine-java8/datastore-indexes/pom.xml index 3e2471378ec..54ded644380 100644 --- a/appengine-java8/datastore-indexes/pom.xml +++ b/appengine-java8/datastore-indexes/pom.xml @@ -93,10 +93,15 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/datastore-schedule-export/pom.xml b/appengine-java8/datastore-schedule-export/pom.xml index 0d2f7e9c973..eec7b45a92d 100644 --- a/appengine-java8/datastore-schedule-export/pom.xml +++ b/appengine-java8/datastore-schedule-export/pom.xml @@ -82,10 +82,15 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/datastore/pom.xml b/appengine-java8/datastore/pom.xml index 0464f7098c6..a26012c2bf7 100644 --- a/appengine-java8/datastore/pom.xml +++ b/appengine-java8/datastore/pom.xml @@ -125,10 +125,15 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/endpoints-v2-backend/build.gradle b/appengine-java8/endpoints-v2-backend/build.gradle index 8777ff8038c..434d39f0c31 100644 --- a/appengine-java8/endpoints-v2-backend/build.gradle +++ b/appengine-java8/endpoints-v2-backend/build.gradle @@ -21,7 +21,7 @@ buildscript { // [START endpoints_plugin] classpath 'com.google.cloud.tools:endpoints-framework-gradle-plugin:2.1.0' // [END endpoints_plugin] - classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.4.2' + classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.4.3' } } diff --git a/appengine-java8/endpoints-v2-backend/pom.xml b/appengine-java8/endpoints-v2-backend/pom.xml index d03326d3014..1dd03821a3a 100644 --- a/appengine-java8/endpoints-v2-backend/pom.xml +++ b/appengine-java8/endpoints-v2-backend/pom.xml @@ -98,7 +98,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG diff --git a/appengine-java8/endpoints-v2-guice/build.gradle b/appengine-java8/endpoints-v2-guice/build.gradle index bdf9f22b74e..bbb641df97c 100644 --- a/appengine-java8/endpoints-v2-guice/build.gradle +++ b/appengine-java8/endpoints-v2-guice/build.gradle @@ -19,7 +19,7 @@ buildscript { dependencies { classpath 'com.google.cloud.tools:endpoints-framework-gradle-plugin:2.1.0' - classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.4.2' + classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.4.3' } } diff --git a/appengine-java8/endpoints-v2-guice/pom.xml b/appengine-java8/endpoints-v2-guice/pom.xml index abc997c222e..f4a55ac8e2a 100644 --- a/appengine-java8/endpoints-v2-guice/pom.xml +++ b/appengine-java8/endpoints-v2-guice/pom.xml @@ -99,7 +99,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG diff --git a/appengine-java8/endpoints-v2-migration/build.gradle b/appengine-java8/endpoints-v2-migration/build.gradle index 3ff42eddd6e..0058eef6cdb 100644 --- a/appengine-java8/endpoints-v2-migration/build.gradle +++ b/appengine-java8/endpoints-v2-migration/build.gradle @@ -20,7 +20,7 @@ buildscript { // Configuration for building } dependencies { // App Engine Gradle plugin - classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.4.2' + classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.4.3' // Endpoints Frameworks Gradle plugin classpath 'com.google.cloud.tools:endpoints-framework-gradle-plugin:2.1.0' diff --git a/appengine-java8/endpoints-v2-migration/pom.xml b/appengine-java8/endpoints-v2-migration/pom.xml index 4ab47a77d67..4fdf1b61c36 100644 --- a/appengine-java8/endpoints-v2-migration/pom.xml +++ b/appengine-java8/endpoints-v2-migration/pom.xml @@ -68,10 +68,15 @@ limitations under the License. ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG diff --git a/appengine-java8/endpoints-v2-skeleton/build.gradle b/appengine-java8/endpoints-v2-skeleton/build.gradle index 5620dfb4a52..a85dcc35a8b 100644 --- a/appengine-java8/endpoints-v2-skeleton/build.gradle +++ b/appengine-java8/endpoints-v2-skeleton/build.gradle @@ -20,7 +20,7 @@ buildscript { dependencies { classpath 'com.google.cloud.tools:endpoints-framework-gradle-plugin:2.1.0' - classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.4.2' + classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.4.3' } } // [END build_script] diff --git a/appengine-java8/endpoints-v2-skeleton/pom.xml b/appengine-java8/endpoints-v2-skeleton/pom.xml index d22847a5603..3efa3b52ce9 100644 --- a/appengine-java8/endpoints-v2-skeleton/pom.xml +++ b/appengine-java8/endpoints-v2-skeleton/pom.xml @@ -85,7 +85,7 @@ limitations under the License. com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG diff --git a/appengine-java8/firebase-backend/pom.xml b/appengine-java8/firebase-backend/pom.xml index 7c0e8f7335b..73d28403472 100644 --- a/appengine-java8/firebase-backend/pom.xml +++ b/appengine-java8/firebase-backend/pom.xml @@ -116,7 +116,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/firebase-event-proxy/pom.xml b/appengine-java8/firebase-event-proxy/pom.xml index 5e5625d4e4a..e0383e893ff 100644 --- a/appengine-java8/firebase-event-proxy/pom.xml +++ b/appengine-java8/firebase-event-proxy/pom.xml @@ -95,10 +95,15 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/firebase-tictactoe/pom.xml b/appengine-java8/firebase-tictactoe/pom.xml index de27b3b757f..a526a5bb72b 100644 --- a/appengine-java8/firebase-tictactoe/pom.xml +++ b/appengine-java8/firebase-tictactoe/pom.xml @@ -72,7 +72,7 @@ com.google.api-client google-api-client-appengine - 1.33.4 + 1.34.0 @@ -118,10 +118,15 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/gaeinfo/pom.xml b/appengine-java8/gaeinfo/pom.xml index fd32e8fda75..00f1417fb5b 100644 --- a/appengine-java8/gaeinfo/pom.xml +++ b/appengine-java8/gaeinfo/pom.xml @@ -97,7 +97,7 @@ Copyright 2017 Google Inc. com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/guestbook-cloud-datastore/pom.xml b/appengine-java8/guestbook-cloud-datastore/pom.xml index b3771dac67d..48ea294c768 100644 --- a/appengine-java8/guestbook-cloud-datastore/pom.xml +++ b/appengine-java8/guestbook-cloud-datastore/pom.xml @@ -109,10 +109,15 @@ + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/helloworld/build.gradle b/appengine-java8/helloworld/build.gradle index 724d1c98767..2e11c316436 100644 --- a/appengine-java8/helloworld/build.gradle +++ b/appengine-java8/helloworld/build.gradle @@ -18,7 +18,7 @@ buildscript { // Configuration for building mavenCentral() } dependencies { - classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.4.2' // If a newer version is available, use it + classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.4.3' // If a newer version is available, use it } } diff --git a/appengine-java8/helloworld/pom.xml b/appengine-java8/helloworld/pom.xml index 4e205977492..59bfe6b0490 100644 --- a/appengine-java8/helloworld/pom.xml +++ b/appengine-java8/helloworld/pom.xml @@ -87,7 +87,7 @@ limitations under the License. org.mockito mockito-core - 4.4.0 + 4.5.1 test @@ -96,10 +96,15 @@ limitations under the License. ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 myProjectId diff --git a/appengine-java8/iap/pom.xml b/appengine-java8/iap/pom.xml index 5525362fd6c..0389b36bfd1 100644 --- a/appengine-java8/iap/pom.xml +++ b/appengine-java8/iap/pom.xml @@ -48,6 +48,11 @@ Copyright 2017 Google Inc. ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + org.apache.maven.plugins maven-compiler-plugin @@ -56,7 +61,7 @@ Copyright 2017 Google Inc. com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/images/pom.xml b/appengine-java8/images/pom.xml index 28f47c5c7af..5648bc416a5 100644 --- a/appengine-java8/images/pom.xml +++ b/appengine-java8/images/pom.xml @@ -64,7 +64,7 @@ Copyright 2015 Google Inc. com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/mail/pom.xml b/appengine-java8/mail/pom.xml index f1901e67467..37fdb5db769 100644 --- a/appengine-java8/mail/pom.xml +++ b/appengine-java8/mail/pom.xml @@ -59,10 +59,15 @@ Copyright 2016 Google Inc. ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/mailgun/pom.xml b/appengine-java8/mailgun/pom.xml index 6b142ccbae6..f73552b6afb 100644 --- a/appengine-java8/mailgun/pom.xml +++ b/appengine-java8/mailgun/pom.xml @@ -65,10 +65,15 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/mailjet/pom.xml b/appengine-java8/mailjet/pom.xml index fbd00e18fd7..f6f201a8c86 100644 --- a/appengine-java8/mailjet/pom.xml +++ b/appengine-java8/mailjet/pom.xml @@ -71,10 +71,15 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/memcache/pom.xml b/appengine-java8/memcache/pom.xml index 2173c0efc34..42e19904cad 100644 --- a/appengine-java8/memcache/pom.xml +++ b/appengine-java8/memcache/pom.xml @@ -64,7 +64,7 @@ Copyright 2015 Google Inc. com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/metadata/pom.xml b/appengine-java8/metadata/pom.xml index b9d247f8f06..24153495676 100644 --- a/appengine-java8/metadata/pom.xml +++ b/appengine-java8/metadata/pom.xml @@ -92,7 +92,7 @@ Copyright 2017 Google Inc. com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/multitenancy/pom.xml b/appengine-java8/multitenancy/pom.xml index d6031c0c2e2..c4ce2ae33f5 100644 --- a/appengine-java8/multitenancy/pom.xml +++ b/appengine-java8/multitenancy/pom.xml @@ -116,10 +116,15 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/oauth2/pom.xml b/appengine-java8/oauth2/pom.xml index eaf2167fbc0..72e838463e5 100644 --- a/appengine-java8/oauth2/pom.xml +++ b/appengine-java8/oauth2/pom.xml @@ -63,7 +63,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/pubsub/pom.xml b/appengine-java8/pubsub/pom.xml index 1d1a7b19465..31c230b1322 100644 --- a/appengine-java8/pubsub/pom.xml +++ b/appengine-java8/pubsub/pom.xml @@ -81,10 +81,15 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG diff --git a/appengine-java8/remote-server/pom.xml b/appengine-java8/remote-server/pom.xml index 0de916d1bc3..c0ae14d5462 100644 --- a/appengine-java8/remote-server/pom.xml +++ b/appengine-java8/remote-server/pom.xml @@ -60,11 +60,15 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes - + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/requests/pom.xml b/appengine-java8/requests/pom.xml index c39efea3a79..a253467d482 100644 --- a/appengine-java8/requests/pom.xml +++ b/appengine-java8/requests/pom.xml @@ -103,7 +103,7 @@ limitations under the License. com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/search/pom.xml b/appengine-java8/search/pom.xml index 560c9595359..9676590e213 100644 --- a/appengine-java8/search/pom.xml +++ b/appengine-java8/search/pom.xml @@ -93,7 +93,7 @@ Copyright 2015 Google Inc. com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/sendgrid/pom.xml b/appengine-java8/sendgrid/pom.xml index b1086e8dc6f..e083eb17f24 100644 --- a/appengine-java8/sendgrid/pom.xml +++ b/appengine-java8/sendgrid/pom.xml @@ -56,10 +56,15 @@ Copyright 2018 Google LLC ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/spanner/pom.xml b/appengine-java8/spanner/pom.xml index 19e01c23449..a0077228ea9 100644 --- a/appengine-java8/spanner/pom.xml +++ b/appengine-java8/spanner/pom.xml @@ -86,7 +86,11 @@ 9.4.44.v20210927 - + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + org.apache.maven.plugins 3.8.1 @@ -99,7 +103,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/sparkjava-helloworld/pom.xml b/appengine-java8/sparkjava-helloworld/pom.xml index 1ac2d9ccb02..2ec84155392 100644 --- a/appengine-java8/sparkjava-helloworld/pom.xml +++ b/appengine-java8/sparkjava-helloworld/pom.xml @@ -124,7 +124,7 @@ limitations under the License. com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 myProjectId diff --git a/appengine-java8/springboot-helloworld/pom.xml b/appengine-java8/springboot-helloworld/pom.xml index fb6c574db11..1f1d724e5df 100644 --- a/appengine-java8/springboot-helloworld/pom.xml +++ b/appengine-java8/springboot-helloworld/pom.xml @@ -91,7 +91,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/taskqueues-deferred/pom.xml b/appengine-java8/taskqueues-deferred/pom.xml index 90db068e976..6dc18ce4cc8 100644 --- a/appengine-java8/taskqueues-deferred/pom.xml +++ b/appengine-java8/taskqueues-deferred/pom.xml @@ -66,7 +66,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 com.google.appengine @@ -87,7 +87,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/taskqueues-pull/pom.xml b/appengine-java8/taskqueues-pull/pom.xml index 88e5e0d4a31..03c2e04ca9d 100644 --- a/appengine-java8/taskqueues-pull/pom.xml +++ b/appengine-java8/taskqueues-pull/pom.xml @@ -81,7 +81,7 @@ limitations under the License. com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/taskqueues-push/pom.xml b/appengine-java8/taskqueues-push/pom.xml index 3fbf64a6ef7..b58fb8a66e7 100644 --- a/appengine-java8/taskqueues-push/pom.xml +++ b/appengine-java8/taskqueues-push/pom.xml @@ -101,7 +101,7 @@ Copyright 2016 Google Inc. com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/tasks/app/pom.xml b/appengine-java8/tasks/app/pom.xml index 33c73f7bf93..fd63ab96d42 100644 --- a/appengine-java8/tasks/app/pom.xml +++ b/appengine-java8/tasks/app/pom.xml @@ -50,7 +50,7 @@ Copyright 2019 Google LLC com.google.cloud google-cloud-tasks - 2.1.10 + 2.1.11 @@ -80,7 +80,7 @@ Copyright 2019 Google LLC com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/tasks/quickstart/pom.xml b/appengine-java8/tasks/quickstart/pom.xml index 1bc9b3cece7..b240e6d893d 100644 --- a/appengine-java8/tasks/quickstart/pom.xml +++ b/appengine-java8/tasks/quickstart/pom.xml @@ -52,7 +52,7 @@ Copyright 2018 Google LLC com.google.cloud google-cloud-tasks - 2.1.10 + 2.1.11 commons-cli @@ -80,10 +80,15 @@ Copyright 2018 Google LLC ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/tasks/snippets/pom.xml b/appengine-java8/tasks/snippets/pom.xml index 5e0c5d7d402..62fdef30d4f 100644 --- a/appengine-java8/tasks/snippets/pom.xml +++ b/appengine-java8/tasks/snippets/pom.xml @@ -18,7 +18,6 @@ Copyright 2019 Google LLC xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - war 1.0-SNAPSHOT com.example.task cloud-tasks-snippets @@ -44,12 +43,12 @@ Copyright 2019 Google LLC com.google.cloud google-cloud-tasks - 2.1.10 + 2.1.11 com.google.protobuf protobuf-java - 3.20.0 + 3.20.1 diff --git a/appengine-java8/translate-pubsub/pom.xml b/appengine-java8/translate-pubsub/pom.xml index ec22e9e5fab..c167e5cd184 100644 --- a/appengine-java8/translate-pubsub/pom.xml +++ b/appengine-java8/translate-pubsub/pom.xml @@ -82,7 +82,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/twilio/pom.xml b/appengine-java8/twilio/pom.xml index bff80a1032e..a6e686d7c9f 100644 --- a/appengine-java8/twilio/pom.xml +++ b/appengine-java8/twilio/pom.xml @@ -56,10 +56,15 @@ Copyright 2015 Google Inc. ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/urlfetch/pom.xml b/appengine-java8/urlfetch/pom.xml index dcd045edb50..cd0f2c26df6 100644 --- a/appengine-java8/urlfetch/pom.xml +++ b/appengine-java8/urlfetch/pom.xml @@ -55,10 +55,15 @@ Copyright 2015 Google Inc. ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/appengine-java8/users/pom.xml b/appengine-java8/users/pom.xml index 9c12dfb6197..fdf1789b75d 100644 --- a/appengine-java8/users/pom.xml +++ b/appengine-java8/users/pom.xml @@ -105,10 +105,15 @@ Copyright 2015 Google Inc. ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/bigtable/beam/bulk-data-generator/README.md b/bigtable/beam/bulk-data-generator/README.md index 12c1088e647..24b9d868e69 100644 --- a/bigtable/beam/bulk-data-generator/README.md +++ b/bigtable/beam/bulk-data-generator/README.md @@ -20,8 +20,8 @@ REGION=us-central1 3. Run the command ``` -mvn compile exec:java -Dexec.mainClass=BulkWrite \ +mvn compile exec:java -Dexec.mainClass=bigtable.BulkWrite \ "-Dexec.args=--bigtableInstanceId=$INSTANCE_ID \ --runner=dataflow --project=$GOOGLE_CLOUD_PROJECT \ --bigtableSize=$BIGTABLE_SIZE --region=$REGION" -``` \ No newline at end of file +``` diff --git a/bigtable/beam/bulk-data-generator/pom.xml b/bigtable/beam/bulk-data-generator/pom.xml index c19ae8c9496..717d6cf05f8 100644 --- a/bigtable/beam/bulk-data-generator/pom.xml +++ b/bigtable/beam/bulk-data-generator/pom.xml @@ -26,7 +26,7 @@ 8 8 - 2.37.0 + 2.38.0 diff --git a/bigtable/beam/keyviz-art/pom.xml b/bigtable/beam/keyviz-art/pom.xml index 90551be8628..e87239edd2a 100644 --- a/bigtable/beam/keyviz-art/pom.xml +++ b/bigtable/beam/keyviz-art/pom.xml @@ -37,7 +37,7 @@ UTF-8 1.8 1.8 - 2.37.0 + 2.38.0 @@ -61,7 +61,7 @@ com.google.cloud.bigtable bigtable-hbase-beam - 2.1.0 + 2.2.0 diff --git a/bigtable/beam/workload-generator/pom.xml b/bigtable/beam/workload-generator/pom.xml index 45e984fac9f..bc0ffc3037d 100644 --- a/bigtable/beam/workload-generator/pom.xml +++ b/bigtable/beam/workload-generator/pom.xml @@ -26,7 +26,7 @@ 8 8 - 2.37.0 + 2.38.0 diff --git a/bigtable/memorystore/pom.xml b/bigtable/memorystore/pom.xml index 65f86e2dd1b..36b10b71abf 100644 --- a/bigtable/memorystore/pom.xml +++ b/bigtable/memorystore/pom.xml @@ -44,7 +44,7 @@ com.google.cloud google-cloud-bigtable - 2.6.1 + 2.6.2 diff --git a/bigtable/scheduled-backups/pom.xml b/bigtable/scheduled-backups/pom.xml index 1918efaa6ba..190e5417a90 100644 --- a/bigtable/scheduled-backups/pom.xml +++ b/bigtable/scheduled-backups/pom.xml @@ -51,7 +51,7 @@ limitations under the License. com.google.cloud google-cloud-bigtable - 2.6.1 + 2.6.2 com.fasterxml.jackson.core diff --git a/bigtable/spark/build.sbt b/bigtable/spark/build.sbt index fff2e974c37..10a9f936ddd 100644 --- a/bigtable/spark/build.sbt +++ b/bigtable/spark/build.sbt @@ -22,7 +22,7 @@ version := "0.1" // https://cloud.google.com/dataproc/docs/concepts/versioning/dataproc-release-1.4 scalaVersion := "2.11.12" val sparkVersion = "2.4.8" -val bigtableVersion = "2.1.0" +val bigtableVersion = "2.2.0" val hbaseVersion = "2.4.9" libraryDependencies ++= Seq( diff --git a/cloud-sql/mysql/client-side-encryption/pom.xml b/cloud-sql/mysql/client-side-encryption/pom.xml index 2aee127445c..56bcd128fb4 100644 --- a/cloud-sql/mysql/client-side-encryption/pom.xml +++ b/cloud-sql/mysql/client-side-encryption/pom.xml @@ -48,7 +48,7 @@ com.google.api-client google-api-client - 1.33.4 + 1.34.1 @@ -57,17 +57,17 @@ com.google.http-client google-http-client-jackson2 - 1.41.6 + 1.41.8 com.google.cloud.sql mysql-socket-factory-connector-j-8 - 1.5.0 + 1.6.0 mysql mysql-connector-java - 8.0.28 + 8.0.29 com.google.crypto.tink diff --git a/cloud-sql/mysql/servlet/.env.yaml b/cloud-sql/mysql/servlet/.env.yaml new file mode 100644 index 00000000000..7978d8d71b7 --- /dev/null +++ b/cloud-sql/mysql/servlet/.env.yaml @@ -0,0 +1,7 @@ +INSTANCE_CONNECTION_NAME: ::INSTANCE-NAME> +INSTANCE_UNIX_SOCKET: /cloudsql/::INSTANCE-NAME> +INSTANCE_HOST: '127.0.0.1' +DB_PORT: 3306 +DB_USER: +DB_PASS: +DB_NAME: diff --git a/cloud-sql/mysql/servlet/README.md b/cloud-sql/mysql/servlet/README.md index 8b510b28d14..533cf1075cb 100644 --- a/cloud-sql/mysql/servlet/README.md +++ b/cloud-sql/mysql/servlet/README.md @@ -29,6 +29,56 @@ export DB_NAME='my_db' Note: Saving credentials in environment variables is convenient, but not secure - consider a more secure solution such as [Cloud KMS](https://cloud.google.com/kms/) or [Secret Manager](https://cloud.google.com/secret-manager/) to help keep secrets safe. +## Configure SSL Certificates +For deployments that connect directly to a Cloud SQL instance with TCP, +without using the Cloud SQL Proxy, +configuring SSL certificates will ensure the connection is encrypted. +1. Use the gcloud CLI to [download the server certificate](https://cloud.google.com/sql/docs/mysql/configure-ssl-instance#server-certs) for your Cloud SQL instance. + - Get information about the service certificate: + ``` + gcloud beta sql ssl server-ca-certs list --instance=INSTANCE_NAME + ``` + - Create a server certificate: + ``` + gcloud beta sql ssl server-ca-certs create --instance=INSTANCE_NAME + ``` + - Download the certificate information to a local PEM file + ``` + gcloud beta sql ssl server-ca-certs list \ + --format="value(cert)" \ + --instance=INSTANCE_NAME > \ + server-ca.pem + ``` + +1. Use the gcloud CLI to [create and download a client public key certificate and client private key](https://cloud.google.com/sql/docs/mysql/configure-ssl-instance#client-certs) + - Create a client certificate using the ssl client-certs create command: + ``` + gcloud sql ssl client-certs create CERT_NAME client-key.pem --instance=INSTANCE_NAME + ``` + - Retrieve the public key for the certificate you just created and copy it into the client-cert.pem file with the ssl client-certs describe command: + ``` + gcloud sql ssl client-certs describe CERT_NAME \ + --instance=INSTANCE_NAME \ + --format="value(cert)" > client-cert.pem + ``` +1. [Import the server certificate into a custom Java truststore](https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-using-ssl.html) using `keytool`: + ``` + keytool -importcert -alias MySQLCACert -file server-ca.pem \ + -keystore -storepass + ``` +1. Set the `TRUST_CERT_KEYSTORE_PATH` and `TRUST_CERT_KEYSTORE_PASSWD` environment variables to the values used in the previous step. +1. [Import the client certificate and key into a custom Java keystore](https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-using-ssl.html) using `openssl` and `keytool`: + - Convert the client key and certificate files to a PKCS #12 archive: + ``` + openssl pkcs12 -export -in client-cert.pem -inkey client-key.pem \ + -name "mysqlclient" -passout pass:mypassword -out client-keystore.p12 + ``` + - Import the client key and certificate into a Java keystore: + ``` + keytool -importkeystore -srckeystore client-keystore.p12 -srcstoretype pkcs12 \ + -srcstorepass -destkeystore -deststoretype JKS -deststorepass + ``` +1. Set the `CLIENT_CERT_KEYSTORE_PATH` and `CLIENT_CERT_KEYSTORE_PASSWD` environment variables to the values used in the previous step. ## Deploying locally To run this application locally, run the following command inside the project folder: @@ -48,13 +98,19 @@ and verify that has been added in your build section as a plugin. -### Development Server +### App Engine Development Server The following command will run the application locally in the the GAE-development server: ```bash mvn appengine:run ``` +### Cloud Functions Development Server +To run the application locally as a Cloud Function, run the following command: +``` +mvn function:run -Drun.functionTarget=com.example.cloudsql.functions.Main +``` + ### Deploy to Google App Engine First, update `src/main/webapp/WEB-INF/appengine-web.xml` with the correct values to pass the @@ -120,3 +176,14 @@ mvn clean package com.google.cloud.tools:jib-maven-plugin:2.8.0:build \ For more details about using Cloud Run see http://cloud.run. Review other [Java on Cloud Run samples](../../../run/). + +### Deploy to Google Cloud Functions + +To deploy the application to Cloud Functions, first fill in the values for required environment variables in `.env.yaml`. Then run the following command +``` +gcloud functions deploy sql-sample \ + --trigger-http \ + --entry-point com.example.cloudsql.functions.Main \ + --runtime java11 \ + --env-vars-file .env.yaml +``` diff --git a/cloud-sql/mysql/servlet/pom.xml b/cloud-sql/mysql/servlet/pom.xml index cb4115eac0d..b92ed13b1cb 100644 --- a/cloud-sql/mysql/servlet/pom.xml +++ b/cloud-sql/mysql/servlet/pom.xml @@ -52,12 +52,12 @@ mysql mysql-connector-java - 8.0.28 + 8.0.29 com.google.cloud.sql mysql-socket-factory-connector-j-8 - 1.5.0 + 1.6.0 com.zaxxer @@ -77,7 +77,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test @@ -92,10 +92,27 @@ 1.1.3 test + + + com.google.cloud.functions.invoker + java-function-invoker + 1.1.0 + + + com.google.cloud.functions + functions-framework-api + 1.0.4 + provided + + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + org.eclipse.jetty jetty-maven-plugin @@ -108,12 +125,29 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG + + + com.google.cloud.functions + function-maven-plugin + 0.10.0 + + com.example.cloudsql.functions.Main + + diff --git a/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/ConnectionPoolContextListener.java b/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/ConnectionPoolContextListener.java index aa994574207..db3373071a0 100644 --- a/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/ConnectionPoolContextListener.java +++ b/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/ConnectionPoolContextListener.java @@ -16,11 +16,8 @@ package com.example.cloudsql; -import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import java.sql.Connection; -import java.sql.PreparedStatement; import java.sql.SQLException; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; @@ -34,101 +31,6 @@ @WebListener("Creates a connection pool that is stored in the Servlet's context for later use.") public class ConnectionPoolContextListener implements ServletContextListener { - // Saving credentials in environment variables is convenient, but not secure - consider a more - // secure solution such as https://cloud.google.com/kms/ to help keep secrets safe. - private static final String INSTANCE_CONNECTION_NAME = - System.getenv("INSTANCE_CONNECTION_NAME"); - private static final String DB_USER = System.getenv("DB_USER"); - private static final String DB_PASS = System.getenv("DB_PASS"); - private static final String DB_NAME = System.getenv("DB_NAME"); - - @SuppressFBWarnings( - value = "USBR_UNNECESSARY_STORE_BEFORE_RETURN", - justification = "Necessary for sample region tag.") - private DataSource createConnectionPool() { - // [START cloud_sql_mysql_servlet_create] - // Note: For Java users, the Cloud SQL JDBC Socket Factory can provide authenticated connections - // which is preferred to using the Cloud SQL Proxy with Unix sockets. - // See https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory for details. - - // The configuration object specifies behaviors for the connection pool. - HikariConfig config = new HikariConfig(); - - // The following URL is equivalent to setting the config options below: - // jdbc:mysql:///?cloudSqlInstance=& - // socketFactory=com.google.cloud.sql.mysql.SocketFactory&user=&password= - // See the link below for more info on building a JDBC URL for the Cloud SQL JDBC Socket Factory - // https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory#creating-the-jdbc-url - - // Configure which instance and what database user to connect with. - config.setJdbcUrl(String.format("jdbc:mysql:///%s", DB_NAME)); - config.setUsername(DB_USER); // e.g. "root", "mysql" - config.setPassword(DB_PASS); // e.g. "my-password" - - config.addDataSourceProperty("socketFactory", "com.google.cloud.sql.mysql.SocketFactory"); - config.addDataSourceProperty("cloudSqlInstance", INSTANCE_CONNECTION_NAME); - - // The ipTypes argument can be used to specify a comma delimited list of preferred IP types - // for connecting to a Cloud SQL instance. The argument ipTypes=PRIVATE will force the - // SocketFactory to connect with an instance's associated private IP. - config.addDataSourceProperty("ipTypes", "PUBLIC,PRIVATE"); - - // ... Specify additional connection properties here. - // [START_EXCLUDE] - - // [START cloud_sql_mysql_servlet_limit] - // maximumPoolSize limits the total number of concurrent connections this pool will keep. Ideal - // values for this setting are highly variable on app design, infrastructure, and database. - config.setMaximumPoolSize(5); - // minimumIdle is the minimum number of idle connections Hikari maintains in the pool. - // Additional connections will be established to meet this value unless the pool is full. - config.setMinimumIdle(5); - // [END cloud_sql_mysql_servlet_limit] - - // [START cloud_sql_mysql_servlet_timeout] - // setConnectionTimeout is the maximum number of milliseconds to wait for a connection checkout. - // Any attempt to retrieve a connection from this pool that exceeds the set limit will throw an - // SQLException. - config.setConnectionTimeout(10000); // 10 seconds - // idleTimeout is the maximum amount of time a connection can sit in the pool. Connections that - // sit idle for this many milliseconds are retried if minimumIdle is exceeded. - config.setIdleTimeout(600000); // 10 minutes - // [END cloud_sql_mysql_servlet_timeout] - - // [START cloud_sql_mysql_servlet_backoff] - // Hikari automatically delays between failed connection attempts, eventually reaching a - // maximum delay of `connectionTimeout / 2` between attempts. - // [END cloud_sql_mysql_servlet_backoff] - - // [START cloud_sql_mysql_servlet_lifetime] - // maxLifetime is the maximum possible lifetime of a connection in the pool. Connections that - // live longer than this many milliseconds will be closed and reestablished between uses. This - // value should be several minutes shorter than the database's timeout value to avoid unexpected - // terminations. - config.setMaxLifetime(1800000); // 30 minutes - // [END cloud_sql_mysql_servlet_lifetime] - - // [END_EXCLUDE] - - // Initialize the connection pool using the configuration object. - DataSource pool = new HikariDataSource(config); - // [END cloud_sql_mysql_servlet_create] - return pool; - } - - private void createTable(DataSource pool) throws SQLException { - // Safely attempt to create the table schema. - try (Connection conn = pool.getConnection()) { - String stmt = - "CREATE TABLE IF NOT EXISTS votes ( " - + "vote_id SERIAL NOT NULL, time_cast timestamp NOT NULL, candidate CHAR(6) NOT NULL," - + " PRIMARY KEY (vote_id) );"; - try (PreparedStatement createTableStatement = conn.prepareStatement(stmt);) { - createTableStatement.execute(); - } - } - } - @Override public void contextDestroyed(ServletContextEvent event) { // This function is called when the Servlet is destroyed. @@ -145,11 +47,15 @@ public void contextInitialized(ServletContextEvent event) { ServletContext servletContext = event.getServletContext(); DataSource pool = (DataSource) servletContext.getAttribute("my-pool"); if (pool == null) { - pool = createConnectionPool(); + if (System.getenv("INSTANCE_HOST") != null) { + pool = TcpConnectionPoolFactory.createConnectionPool(); + } else { + pool = ConnectorConnectionPoolFactory.createConnectionPool(); + } servletContext.setAttribute("my-pool", pool); } try { - createTable(pool); + Utils.createTable(pool); } catch (SQLException ex) { throw new RuntimeException( "Unable to verify table schema. Please double check the steps" diff --git a/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/ConnectionPoolFactory.java b/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/ConnectionPoolFactory.java new file mode 100644 index 00000000000..a9f51330483 --- /dev/null +++ b/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/ConnectionPoolFactory.java @@ -0,0 +1,57 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.cloudsql; + +import com.zaxxer.hikari.HikariConfig; + +public class ConnectionPoolFactory { + + public static HikariConfig configureConnectionPool(HikariConfig config) { + // [START cloud_sql_mysql_servlet_limit] + // maximumPoolSize limits the total number of concurrent connections this pool will keep. Ideal + // values for this setting are highly variable on app design, infrastructure, and database. + config.setMaximumPoolSize(5); + // minimumIdle is the minimum number of idle connections Hikari maintains in the pool. + // Additional connections will be established to meet this value unless the pool is full. + config.setMinimumIdle(5); + // [END cloud_sql_mysql_servlet_limit] + + // [START cloud_sql_mysql_servlet_timeout] + // setConnectionTimeout is the maximum number of milliseconds to wait for a connection checkout. + // Any attempt to retrieve a connection from this pool that exceeds the set limit will throw an + // SQLException. + config.setConnectionTimeout(10000); // 10 seconds + // idleTimeout is the maximum amount of time a connection can sit in the pool. Connections that + // sit idle for this many milliseconds are retried if minimumIdle is exceeded. + config.setIdleTimeout(600000); // 10 minutes + // [END cloud_sql_mysql_servlet_timeout] + + // [START cloud_sql_mysql_servlet_backoff] + // Hikari automatically delays between failed connection attempts, eventually reaching a + // maximum delay of `connectionTimeout / 2` between attempts. + // [END cloud_sql_mysql_servlet_backoff] + + // [START cloud_sql_mysql_servlet_lifetime] + // maxLifetime is the maximum possible lifetime of a connection in the pool. Connections that + // live longer than this many milliseconds will be closed and reestablished between uses. This + // value should be several minutes shorter than the database's timeout value to avoid unexpected + // terminations. + config.setMaxLifetime(1800000); // 30 minutes + // [END cloud_sql_mysql_servlet_lifetime] + return config; + } +} diff --git a/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/ConnectorConnectionPoolFactory.java b/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/ConnectorConnectionPoolFactory.java new file mode 100644 index 00000000000..bc34c6c06cb --- /dev/null +++ b/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/ConnectorConnectionPoolFactory.java @@ -0,0 +1,86 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.cloudsql; + +// [START cloud_sql_mysql_servlet_connect_connector] +// [START cloud_sql_mysql_servlet_connect_unix] + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import javax.sql.DataSource; + +public class ConnectorConnectionPoolFactory extends ConnectionPoolFactory { + + // Note: Saving credentials in environment variables is convenient, but not + // secure - consider a more secure solution such as + // Cloud Secret Manager (https://cloud.google.com/secret-manager) to help + // keep secrets safe. + private static final String INSTANCE_CONNECTION_NAME = + System.getenv("INSTANCE_CONNECTION_NAME"); + private static final String INSTANCE_UNIX_SOCKET = System.getenv("INSTANCE_UNIX_SOCKET"); + private static final String DB_USER = System.getenv("DB_USER"); + private static final String DB_PASS = System.getenv("DB_PASS"); + private static final String DB_NAME = System.getenv("DB_NAME"); + + public static DataSource createConnectionPool() { + // The configuration object specifies behaviors for the connection pool. + HikariConfig config = new HikariConfig(); + + // The following URL is equivalent to setting the config options below: + // jdbc:mysql:///?cloudSqlInstance=& + // socketFactory=com.google.cloud.sql.mysql.SocketFactory&user=&password= + // See the link below for more info on building a JDBC URL for the Cloud SQL JDBC Socket Factory + // https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory#creating-the-jdbc-url + + // Configure which instance and what database user to connect with. + config.setJdbcUrl(String.format("jdbc:mysql:///%s", DB_NAME)); + config.setUsername(DB_USER); // e.g. "root", "mysql" + config.setPassword(DB_PASS); // e.g. "my-password" + + config.addDataSourceProperty("socketFactory", "com.google.cloud.sql.mysql.SocketFactory"); + config.addDataSourceProperty("cloudSqlInstance", INSTANCE_CONNECTION_NAME); + + // [END cloud_sql_mysql_servlet_connect_connector] + // Unix sockets are not natively supported in Java, so it is necessary to use the Cloud SQL + // Java Connector to connect. When setting INSTANCE_UNIX_SOCKET, the connector will + // call an external package that will enable Unix socket connections. + // Note: For Java users, the Cloud SQL Java Connector can provide authenticated connections + // which is usually preferable to using the Cloud SQL Proxy with Unix sockets. + // See https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory for details. + if (INSTANCE_UNIX_SOCKET != null) { + config.addDataSourceProperty("unixSocketPath", INSTANCE_UNIX_SOCKET); + } + // [START cloud_sql_mysql_servlet_connect_connector] + + // [END cloud_sql_mysql_servlet_connect_unix] + // The ipTypes argument can be used to specify a comma delimited list of preferred IP types + // for connecting to a Cloud SQL instance. The argument ipTypes=PRIVATE will force the + // SocketFactory to connect with an instance's associated private IP. + config.addDataSourceProperty("ipTypes", "PUBLIC,PRIVATE"); + // [START cloud_sql_mysql_servlet_connect_unix] + + // ... Specify additional connection properties here. + // [START_EXCLUDE] + configureConnectionPool(config); + // [END_EXCLUDE] + + // Initialize the connection pool using the configuration object. + return new HikariDataSource(config); + } +} +// [END cloud_sql_mysql_servlet_connect_connector] +// [END cloud_sql_mysql_servlet_connect_unix] diff --git a/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/IndexServlet.java b/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/IndexServlet.java index 7f43bf811cd..6551d57e899 100644 --- a/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/IndexServlet.java +++ b/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/IndexServlet.java @@ -20,16 +20,11 @@ import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; -import java.util.ArrayList; import java.util.Date; -import java.util.List; -import java.util.Locale; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.Nullable; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; @@ -38,7 +33,6 @@ import javax.sql.DataSource; - @SuppressFBWarnings( value = {"SE_NO_SERIALVERSIONID", "WEM_WEAK_EXCEPTION_MESSAGING"}, justification = "Not needed for IndexServlet, Exception adds context") @@ -47,67 +41,12 @@ public class IndexServlet extends HttpServlet { private static final Logger LOGGER = Logger.getLogger(IndexServlet.class.getName()); - class TemplateData { - - public int tabCount; - public int spaceCount; - public List recentVotes; - - public TemplateData(int tabCount, int spaceCount, List recentVotes) { - this.tabCount = tabCount; - this.spaceCount = spaceCount; - this.recentVotes = recentVotes; - } - } - - public TemplateData getTemplateData(DataSource pool) throws ServletException { - - int tabCount = 0; - int spaceCount = 0; - List recentVotes = new ArrayList<>(); - try (Connection conn = pool.getConnection()) { - // PreparedStatements are compiled by the database immediately and executed at a later date. - // Most databases cache previously compiled queries, which improves efficiency. - String stmt1 = "SELECT candidate, time_cast FROM votes ORDER BY time_cast DESC LIMIT 5"; - try (PreparedStatement voteStmt = conn.prepareStatement(stmt1);) { - // Execute the statement - ResultSet voteResults = voteStmt.executeQuery(); - // Convert a ResultSet into Vote objects - while (voteResults.next()) { - String candidate = voteResults.getString(1); - Timestamp timeCast = voteResults.getTimestamp(2); - recentVotes.add(new Vote(candidate, timeCast)); - } - } - - // PreparedStatements can also be executed multiple times with different arguments. This can - // improve efficiency, and project a query from being vulnerable to an SQL injection. - String stmt2 = "SELECT COUNT(vote_id) FROM votes WHERE candidate=?"; - try (PreparedStatement voteCountStmt = conn.prepareStatement(stmt2);) { - voteCountStmt.setString(1, "TABS"); - ResultSet tabResult = voteCountStmt.executeQuery(); - if (tabResult.next()) { // Move to the first result - tabCount = tabResult.getInt(1); - } - - voteCountStmt.setString(1, "SPACES"); - ResultSet spaceResult = voteCountStmt.executeQuery(); - if (spaceResult.next()) { // Move to the first result - spaceCount = spaceResult.getInt(1); - } - } + TemplateData getTemplateData(DataSource pool) throws ServletException { + try { + return TemplateData.getTemplateData(pool); } catch (SQLException ex) { - // If something goes wrong, the application needs to react appropriately. This might mean - // getting a new connection and executing the query again, or it might mean redirecting the - // user to a different page to let them know something went wrong. - throw new ServletException( - "Unable to successfully connect to the database. Please check the " - + "steps in the README and try again.", - ex); + throw new ServletException(ex); } - TemplateData templateData = new TemplateData(tabCount, spaceCount, recentVotes); - - return templateData; } @Override @@ -126,27 +65,13 @@ public void doGet(HttpServletRequest req, HttpServletResponse resp) req.getRequestDispatcher("/index.jsp").forward(req, resp); } - // Used to validate user input. All user provided data should be validated and sanitized before - // being used something like a SQL query. Returns null if invalid. - @Nullable - private String validateTeam(String input) { - if (input != null) { - input = input.toUpperCase(Locale.ENGLISH); - // Must be either "TABS" or "SPACES" - if (!"TABS".equals(input) && !"SPACES".equals(input)) { - return null; - } - } - return input; - } - @SuppressFBWarnings( value = {"SERVLET_PARAMETER", "XSS_SERVLET"}, justification = "Input is validated and sanitized.") @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { // Get the team from the request and record the time of the vote. - String team = validateTeam(req.getParameter("team")); + String team = Utils.validateTeam(req.getParameter("team")); Timestamp now = new Timestamp(new Date().getTime()); if (team == null) { resp.setStatus(400); diff --git a/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/TcpConnectionPoolFactory.java b/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/TcpConnectionPoolFactory.java new file mode 100644 index 00000000000..fec50bb8d8b --- /dev/null +++ b/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/TcpConnectionPoolFactory.java @@ -0,0 +1,91 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.cloudsql; + +// [START cloud_sql_mysql_servlet_connect_tcp] +// [START cloud_sql_mysql_servlet_connect_tcp_sslcerts] + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import javax.sql.DataSource; + +public class TcpConnectionPoolFactory extends ConnectionPoolFactory { + + // Saving credentials in environment variables is convenient, but not secure - consider a more + // secure solution such as https://cloud.google.com/secret-manager/ to help keep secrets safe. + private static final String DB_USER = System.getenv("DB_USER"); + private static final String DB_PASS = System.getenv("DB_PASS"); + private static final String DB_NAME = System.getenv("DB_NAME"); + + private static final String INSTANCE_HOST = System.getenv("INSTANCE_HOST"); + private static final String DB_PORT = System.getenv("DB_PORT"); + + // [END cloud_sql_mysql_servlet_connect_tcp] + private static final String TRUST_CERT_KEYSTORE_PATH = System.getenv( + "TRUST_CERT_KEYSTORE_PATH"); + private static final String TRUST_CERT_KEYSTORE_PASSWD = System.getenv( + "TRUST_CERT_KEYSTORE_PASSWD"); + private static final String CLIENT_CERT_KEYSTORE_PATH = System.getenv( + "CLIENT_CERT_KEYSTORE_PATH"); + private static final String CLIENT_CERT_KEYSTORE_PASSWD = System.getenv( + "CLIENT_CERT_KEYSTORE_PASSWD"); + // [START cloud_sql_mysql_servlet_connect_tcp] + + public static DataSource createConnectionPool() { + // The configuration object specifies behaviors for the connection pool. + HikariConfig config = new HikariConfig(); + + // The following URL is equivalent to setting the config options below: + // jdbc:mysql://:/?user=&password= + // See the link below for more info on building a JDBC URL for the Cloud SQL JDBC Socket Factory + // https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory#creating-the-jdbc-url + + // Configure which instance and what database user to connect with. + config.setJdbcUrl(String.format("jdbc:mysql://%s:%s/%s", INSTANCE_HOST, DB_PORT, DB_NAME)); + config.setUsername(DB_USER); // e.g. "root", "mysql" + config.setPassword(DB_PASS); // e.g. "my-password" + + // [END cloud_sql_mysql_servlet_connect_tcp] + // (OPTIONAL) Configure SSL certificates + // For deployments that connect directly to a Cloud SQL instance without + // using the Cloud SQL Proxy, configuring SSL certificates will ensure the + // connection is encrypted. + // See the link below for more information on how to configure SSL Certificates for use with + // MySQL Connector/J + // https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-using-ssl.html + if (CLIENT_CERT_KEYSTORE_PATH != null && TRUST_CERT_KEYSTORE_PATH != null) { + config.addDataSourceProperty("trustCertificateKeyStoreUrl", + String.format("file:%s", TRUST_CERT_KEYSTORE_PATH)); + config.addDataSourceProperty("trustCertificateKeyStorePassword", TRUST_CERT_KEYSTORE_PASSWD); + config.addDataSourceProperty("clientCertificateKeyStoreUrl", + String.format("file:%s", CLIENT_CERT_KEYSTORE_PATH)); + config.addDataSourceProperty("clientCertificateKeyStorePassword", + CLIENT_CERT_KEYSTORE_PASSWD); + } + // [START cloud_sql_mysql_servlet_connect_tcp] + + // ... Specify additional connection properties here. + // [START_EXCLUDE] + configureConnectionPool(config); + // [END_EXCLUDE] + + // Initialize the connection pool using the configuration object. + return new HikariDataSource(config); + } +} +// [END cloud_sql_mysql_servlet_connect_tcp] +// [END cloud_sql_mysql_servlet_connect_tcp_sslcerts] diff --git a/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/TemplateData.java b/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/TemplateData.java new file mode 100644 index 00000000000..693da207de4 --- /dev/null +++ b/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/TemplateData.java @@ -0,0 +1,88 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.cloudsql; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; +import javax.sql.DataSource; + +public class TemplateData { + + public int tabCount; + public int spaceCount; + public List recentVotes; + + public TemplateData(int tabCount, int spaceCount, List recentVotes) { + this.tabCount = tabCount; + this.spaceCount = spaceCount; + this.recentVotes = recentVotes; + } + + public static TemplateData getTemplateData(DataSource pool) throws SQLException { + int tabCount = 0; + int spaceCount = 0; + List recentVotes = new ArrayList<>(); + try (Connection conn = pool.getConnection()) { + // PreparedStatements are compiled by the database immediately and executed at a later date. + // Most databases cache previously compiled queries, which improves efficiency. + String stmt1 = "SELECT candidate, time_cast FROM votes ORDER BY time_cast DESC LIMIT 5"; + try (PreparedStatement voteStmt = conn.prepareStatement(stmt1);) { + // Execute the statement + ResultSet voteResults = voteStmt.executeQuery(); + // Convert a ResultSet into Vote objects + while (voteResults.next()) { + String candidate = voteResults.getString(1); + Timestamp timeCast = voteResults.getTimestamp(2); + recentVotes.add(new Vote(candidate, timeCast)); + } + } + + // PreparedStatements can also be executed multiple times with different arguments. This can + // improve efficiency, and project a query from being vulnerable to an SQL injection. + String stmt2 = "SELECT COUNT(vote_id) FROM votes WHERE candidate=?"; + try (PreparedStatement voteCountStmt = conn.prepareStatement(stmt2);) { + voteCountStmt.setString(1, "TABS"); + ResultSet tabResult = voteCountStmt.executeQuery(); + if (tabResult.next()) { // Move to the first result + tabCount = tabResult.getInt(1); + } + + voteCountStmt.setString(1, "SPACES"); + ResultSet spaceResult = voteCountStmt.executeQuery(); + if (spaceResult.next()) { // Move to the first result + spaceCount = spaceResult.getInt(1); + } + } + } catch (SQLException ex) { + // If something goes wrong, the application needs to react appropriately. This might mean + // getting a new connection and executing the query again, or it might mean redirecting the + // user to a different page to let them know something went wrong. + throw new SQLException( + "Unable to successfully connect to the database. Please check the " + + "steps in the README and try again.", + ex); + } + TemplateData templateData = new TemplateData(tabCount, spaceCount, recentVotes); + + return templateData; + } +} diff --git a/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/Utils.java b/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/Utils.java new file mode 100644 index 00000000000..a6da84573dd --- /dev/null +++ b/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/Utils.java @@ -0,0 +1,56 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.cloudsql; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Locale; +import javax.annotation.Nullable; +import javax.sql.DataSource; + +public class Utils { + + // Used to validate user input. All user provided data should be validated and sanitized before + // being used something like a SQL query. Returns null if invalid. + @Nullable + public static String validateTeam(String input) { + if (input != null) { + input = input.toUpperCase(Locale.ENGLISH); + // Must be either "TABS" or "SPACES" + if (!"TABS".equals(input) && !"SPACES".equals(input)) { + return null; + } + } + return input; + } + + public static void createTable(DataSource pool) throws SQLException { + // Safely attempt to create the table schema. + try (Connection conn = pool.getConnection()) { + String stmt = + "CREATE TABLE IF NOT EXISTS votes ( " + + "vote_id SERIAL NOT NULL, time_cast timestamp NOT NULL, candidate CHAR(6) NOT NULL," + + " PRIMARY KEY (vote_id) );"; + try (PreparedStatement createTableStatement = conn.prepareStatement(stmt);) { + createTableStatement.execute(); + } + } + } + + +} diff --git a/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/functions/Main.java b/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/functions/Main.java new file mode 100644 index 00000000000..e42f4ce7128 --- /dev/null +++ b/cloud-sql/mysql/servlet/src/main/java/com/example/cloudsql/functions/Main.java @@ -0,0 +1,142 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.cloudsql.functions; + +import com.example.cloudsql.ConnectorConnectionPoolFactory; +import com.example.cloudsql.TcpConnectionPoolFactory; +import com.example.cloudsql.TemplateData; +import com.example.cloudsql.Utils; +import com.google.cloud.functions.HttpFunction; +import com.google.cloud.functions.HttpRequest; +import com.google.cloud.functions.HttpResponse; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.Date; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.sql.DataSource; + +public class Main implements HttpFunction { + + private Logger logger = Logger.getLogger(Main.class.getName()); + private static final Gson gson = new Gson(); + + // Declared at cold-start, but only initialized if/when the function executes + // Uses the "initialization-on-demand holder" idiom + // More information: https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom + private static class PoolHolder { + + // Making the default constructor private prohibits instantiation of this class + private PoolHolder() { + } + + // This value is initialized only if (and when) the getInstance() function below is called + private static final DataSource INSTANCE = setupPool(); + + private static DataSource setupPool() { + DataSource pool; + if (System.getenv("INSTANCE_HOST") != null) { + pool = TcpConnectionPoolFactory.createConnectionPool(); + } else { + pool = ConnectorConnectionPoolFactory.createConnectionPool(); + } + try { + Utils.createTable(pool); + } catch (SQLException ex) { + throw new RuntimeException( + "Unable to verify table schema. Please double check the steps" + + "in the README and try again.", + ex); + } + return pool; + } + + private static DataSource getInstance() { + return PoolHolder.INSTANCE; + } + } + + private void returnVoteCounts(HttpRequest req, HttpResponse resp) + throws SQLException, IOException { + DataSource pool = PoolHolder.getInstance(); + TemplateData templateData = TemplateData.getTemplateData(pool); + JsonObject respContent = new JsonObject(); + + // Return JSON Data + respContent.addProperty("tabCount", templateData.tabCount); + respContent.addProperty("spaceCount", templateData.spaceCount); + respContent.addProperty("recentVotes", gson.toJson(templateData.recentVotes)); + resp.getWriter().write(respContent.toString()); + resp.setStatusCode(HttpURLConnection.HTTP_OK); + } + + private void submitVote(HttpRequest req, HttpResponse resp) throws IOException { + DataSource pool = PoolHolder.getInstance(); + Timestamp now = new Timestamp(new Date().getTime()); + JsonObject body = gson.fromJson(req.getReader(), JsonObject.class); + String team = Utils.validateTeam(body.get("team").getAsString()); + if (team == null) { + resp.setStatusCode(400); + resp.getWriter().append("Invalid team specified."); + return; + } + try (Connection conn = pool.getConnection()) { + // PreparedStatements can be more efficient and project against injections. + String stmt = "INSERT INTO votes (time_cast, candidate) VALUES (?, ?);"; + try (PreparedStatement voteStmt = conn.prepareStatement(stmt);) { + voteStmt.setTimestamp(1, now); + voteStmt.setString(2, team); + + // Finally, execute the statement. If it fails, an error will be thrown. + voteStmt.execute(); + } + } catch (SQLException ex) { + // If something goes wrong, handle the error in this section. This might involve retrying or + // adjusting parameters depending on the situation. + logger.log(Level.WARNING, "Error while attempting to submit vote.", ex); + resp.setStatusCode(500); + resp.getWriter() + .write( + "Unable to successfully cast vote! Please check the application " + + "logs for more details."); + } + } + + @Override + public void service(HttpRequest req, HttpResponse resp) throws IOException, SQLException { + + String method = req.getMethod(); + switch (method) { + case "GET": + returnVoteCounts(req, resp); + break; + case "POST": + submitVote(req, resp); + break; + default: + resp.setStatusCode(HttpURLConnection.HTTP_BAD_METHOD); + resp.getWriter().write(String.format("HTTP Method %s is not supported", method)); + break; + } + } +} diff --git a/cloud-sql/mysql/servlet/src/test/java/com/TestIndexServletMysql.java b/cloud-sql/mysql/servlet/src/test/java/com/TestIndexServletMysql.java index abc81dfc142..57b624ba526 100644 --- a/cloud-sql/mysql/servlet/src/test/java/com/TestIndexServletMysql.java +++ b/cloud-sql/mysql/servlet/src/test/java/com/TestIndexServletMysql.java @@ -22,7 +22,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.example.cloudsql.IndexServlet.TemplateData; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import java.io.PrintWriter; diff --git a/cloud-sql/postgres/client-side-encryption/pom.xml b/cloud-sql/postgres/client-side-encryption/pom.xml index 7e57de941ea..54e4eab9f3a 100644 --- a/cloud-sql/postgres/client-side-encryption/pom.xml +++ b/cloud-sql/postgres/client-side-encryption/pom.xml @@ -48,7 +48,7 @@ com.google.api-client google-api-client - 1.33.4 + 1.34.1 @@ -57,7 +57,7 @@ com.google.http-client google-http-client-jackson2 - 1.41.6 + 1.41.8 com.google.cloud.sql @@ -67,7 +67,7 @@ org.postgresql postgresql - 42.3.3 + 42.3.5 com.google.crypto.tink diff --git a/cloud-sql/postgres/servlet/.env.yaml b/cloud-sql/postgres/servlet/.env.yaml new file mode 100644 index 00000000000..73e93097662 --- /dev/null +++ b/cloud-sql/postgres/servlet/.env.yaml @@ -0,0 +1,8 @@ +INSTANCE_CONNECTION_NAME: ::INSTANCE-NAME> +INSTANCE_UNIX_SOCKET: /cloudsql/::INSTANCE-NAME> +INSTANCE_HOST: '127.0.0.1' +DB_PORT: 5432 +DB_USER: +DB_IAM_USER: +DB_PASS: +DB_NAME: diff --git a/cloud-sql/postgres/servlet/README.md b/cloud-sql/postgres/servlet/README.md index 3c021a79125..499e0495fb4 100644 --- a/cloud-sql/postgres/servlet/README.md +++ b/cloud-sql/postgres/servlet/README.md @@ -27,10 +27,50 @@ export DB_PASS='my-db-pass' export DB_NAME='my_db' ``` Note: Saving credentials in environment variables is convenient, but not secure - consider a more -secure solution such as [Cloud KMS](https://cloud.google.com/kms/) to help keep secrets safe. +secure solution such as [Secret Manager](https://cloud.google.com/secret-manager/) to help keep secrets safe. + +## Configure SSL Certificates +For deployments that connect directly to a Cloud SQL instance with TCP, +without using the Cloud SQL Proxy, +configuring SSL certificates will ensure the connection is encrypted. +1. Use the gcloud CLI to [download the server certificate](https://cloud.google.com/sql/docs/mysql/configure-ssl-instance#server-certs) for your Cloud SQL instance. + - Get information about the service certificate: + ``` + gcloud beta sql ssl server-ca-certs list --instance=INSTANCE_NAME + ``` + - Create a server certificate: + ``` + gcloud beta sql ssl server-ca-certs create --instance=INSTANCE_NAME + ``` + - Download the certificate information to a local PEM file + ``` + gcloud beta sql ssl server-ca-certs list \ + --format="value(cert)" \ + --instance=INSTANCE_NAME > \ + server-ca.pem + ``` + +2. Use the gcloud CLI to [create and download a client public key certificate and client private key](https://cloud.google.com/sql/docs/postgres/configure-ssl-instance#client-certs) + - Create a client certificate using the ssl client-certs create command: + ``` + gcloud sql ssl client-certs create CERT_NAME client-key.pem --instance=INSTANCE_NAME + ``` + - Retrieve the public key for the certificate you just created and copy it into the client-cert.pem file with the ssl client-certs describe command: + ``` + gcloud sql ssl client-certs describe CERT_NAME \ + --instance=INSTANCE_NAME \ + --format="value(cert)" > client-cert.pem + ``` +3. Convert the downloaded PEM certificate and key to a PKCS12 archive using `openssl`: + ``` + openssl pkcs12 -export -in client-cert.pem -inkey client-key.pem \ + -name "mysqlclient" -passout pass: -out client-keystore.p12 + ``` +4. Set the `SSL_CLIENT_KEY_PATH` and `SSL_CLIENT_KEY_PASSWD` environment variables to the values from the previous step. +The client key path should point to the PKCS12 archive file. +6. Set the `SSL_SERVER_CA_PATH` environment variables to point to the `server-ca.pem` file downloaded earlier ## Deploying locally - To run this application locally, run the following command inside the project folder: ```bash @@ -48,13 +88,19 @@ and verify that has been added in your build section as a plugin. -### Development Server +### App Engine Development Server The following command will run the application locally in the the GAE-development server: ```bash mvn appengine:run ``` +### Cloud Functions Development Server +To run the application locally as a Cloud Function, run the following command: +``` +mvn function:run -Drun.functionTarget=com.example.cloudsql.functions.Main +``` + ### Deploy to Google App Engine First, update `src/main/webapp/WEB-INF/appengine-web.xml` with the correct values to pass the @@ -120,3 +166,14 @@ mvn clean package com.google.cloud.tools:jib-maven-plugin:2.8.0:build \ For more details about using Cloud Run see http://cloud.run. Review other [Java on Cloud Run samples](../../../run/). + +### Deploy to Google Cloud Functions + +To deploy the application to Cloud Functions, first fill in the values for required environment variables in `.env.yaml`. Then run the following command +``` +gcloud functions deploy sql-sample \ + --trigger-http \ + --entry-point com.example.cloudsql.functions.Main \ + --runtime java11 \ + --env-vars-file .env.yaml +``` diff --git a/cloud-sql/postgres/servlet/pom.xml b/cloud-sql/postgres/servlet/pom.xml index c36d134efe7..b900ee22881 100644 --- a/cloud-sql/postgres/servlet/pom.xml +++ b/cloud-sql/postgres/servlet/pom.xml @@ -52,7 +52,7 @@ org.postgresql postgresql - 42.3.3 + 42.3.5 com.google.cloud.sql @@ -67,7 +67,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test @@ -82,10 +82,27 @@ 1.1.3 test + + + com.google.cloud.functions.invoker + java-function-invoker + 1.0.1 + + + com.google.cloud.functions + functions-framework-api + 1.0.4 + provided + + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + org.eclipse.jetty jetty-maven-plugin @@ -98,12 +115,27 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG - + + + + com.google.cloud.functions + function-maven-plugin + 0.10.0 + + com.example.cloudsql.functions.Main + + diff --git a/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/ConnectionPoolContextListener.java b/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/ConnectionPoolContextListener.java index f299927fab9..db3373071a0 100644 --- a/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/ConnectionPoolContextListener.java +++ b/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/ConnectionPoolContextListener.java @@ -16,11 +16,8 @@ package com.example.cloudsql; -import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import java.sql.Connection; -import java.sql.PreparedStatement; import java.sql.SQLException; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; @@ -34,102 +31,6 @@ @WebListener("Creates a connection pool that is stored in the Servlet's context for later use.") public class ConnectionPoolContextListener implements ServletContextListener { - // Saving credentials in environment variables is convenient, but not secure - consider a more - // secure solution such as https://cloud.google.com/kms/ to help keep secrets safe. - private static final String INSTANCE_CONNECTION_NAME = - System.getenv("INSTANCE_CONNECTION_NAME"); - private static final String DB_USER = System.getenv("DB_USER"); - private static final String DB_PASS = System.getenv("DB_PASS"); - private static final String DB_NAME = System.getenv("DB_NAME"); - - @SuppressFBWarnings( - value = "USBR_UNNECESSARY_STORE_BEFORE_RETURN", - justification = "Necessary for sample region tag.") - private DataSource createConnectionPool() { - // [START cloud_sql_postgres_servlet_create] - // Note: For Java users, the Cloud SQL JDBC Socket Factory can provide authenticated connections - // which is preferred to using the Cloud SQL Auth Proxy with Unix sockets. - // See https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory for details. - - // The configuration object specifies behaviors for the connection pool. - HikariConfig config = new HikariConfig(); - - // The following URL is equivalent to setting the config options below: - // jdbc:postgresql:///?cloudSqlInstance=& - // socketFactory=com.google.cloud.sql.postgres.SocketFactory&user=&password= - // See the link below for more info on building a JDBC URL for the Cloud SQL JDBC Socket Factory - // https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory#creating-the-jdbc-url - - // Configure which instance and what database user to connect with. - config.setJdbcUrl(String.format("jdbc:postgresql:///%s", DB_NAME)); - config.setUsername(DB_USER); // e.g. "root", "postgres" - config.setPassword(DB_PASS); // e.g. "my-password" - - config.addDataSourceProperty("socketFactory", "com.google.cloud.sql.postgres.SocketFactory"); - config.addDataSourceProperty("cloudSqlInstance", INSTANCE_CONNECTION_NAME); - - - // The ipTypes argument can be used to specify a comma delimited list of preferred IP types - // for connecting to a Cloud SQL instance. The argument ipTypes=PRIVATE will force the - // SocketFactory to connect with an instance's associated private IP. - config.addDataSourceProperty("ipTypes", "PUBLIC,PRIVATE"); - - // ... Specify additional connection properties here. - // [START_EXCLUDE] - - // [START cloud_sql_postgres_servlet_limit] - // maximumPoolSize limits the total number of concurrent connections this pool will keep. Ideal - // values for this setting are highly variable on app design, infrastructure, and database. - config.setMaximumPoolSize(5); - // minimumIdle is the minimum number of idle connections Hikari maintains in the pool. - // Additional connections will be established to meet this value unless the pool is full. - config.setMinimumIdle(5); - // [END cloud_sql_postgres_servlet_limit] - - // [START cloud_sql_postgres_servlet_timeout] - // setConnectionTimeout is the maximum number of milliseconds to wait for a connection checkout. - // Any attempt to retrieve a connection from this pool that exceeds the set limit will throw an - // SQLException. - config.setConnectionTimeout(10000); // 10 seconds - // idleTimeout is the maximum amount of time a connection can sit in the pool. Connections that - // sit idle for this many milliseconds are retried if minimumIdle is exceeded. - config.setIdleTimeout(600000); // 10 minutes - // [END cloud_sql_postgres_servlet_timeout] - - // [START cloud_sql_postgres_servlet_backoff] - // Hikari automatically delays between failed connection attempts, eventually reaching a - // maximum delay of `connectionTimeout / 2` between attempts. - // [END cloud_sql_postgres_servlet_backoff] - - // [START cloud_sql_postgres_servlet_lifetime] - // maxLifetime is the maximum possible lifetime of a connection in the pool. Connections that - // live longer than this many milliseconds will be closed and reestablished between uses. This - // value should be several minutes shorter than the database's timeout value to avoid unexpected - // terminations. - config.setMaxLifetime(1800000); // 30 minutes - // [END cloud_sql_postgres_servlet_lifetime] - - // [END_EXCLUDE] - - // Initialize the connection pool using the configuration object. - DataSource pool = new HikariDataSource(config); - // [END cloud_sql_postgres_servlet_create] - return pool; - } - - private void createTable(DataSource pool) throws SQLException { - // Safely attempt to create the table schema. - try (Connection conn = pool.getConnection()) { - String stmt = - "CREATE TABLE IF NOT EXISTS votes ( " - + "vote_id SERIAL NOT NULL, time_cast timestamp NOT NULL, candidate CHAR(6) NOT NULL," - + " PRIMARY KEY (vote_id) );"; - try (PreparedStatement createTableStatement = conn.prepareStatement(stmt);) { - createTableStatement.execute(); - } - } - } - @Override public void contextDestroyed(ServletContextEvent event) { // This function is called when the Servlet is destroyed. @@ -146,11 +47,15 @@ public void contextInitialized(ServletContextEvent event) { ServletContext servletContext = event.getServletContext(); DataSource pool = (DataSource) servletContext.getAttribute("my-pool"); if (pool == null) { - pool = createConnectionPool(); + if (System.getenv("INSTANCE_HOST") != null) { + pool = TcpConnectionPoolFactory.createConnectionPool(); + } else { + pool = ConnectorConnectionPoolFactory.createConnectionPool(); + } servletContext.setAttribute("my-pool", pool); } try { - createTable(pool); + Utils.createTable(pool); } catch (SQLException ex) { throw new RuntimeException( "Unable to verify table schema. Please double check the steps" diff --git a/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/ConnectionPoolFactory.java b/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/ConnectionPoolFactory.java new file mode 100644 index 00000000000..62c45366891 --- /dev/null +++ b/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/ConnectionPoolFactory.java @@ -0,0 +1,57 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.cloudsql; + +import com.zaxxer.hikari.HikariConfig; + +public class ConnectionPoolFactory { + + public static HikariConfig configureConnectionPool(HikariConfig config) { + // [START cloud_sql_postgres_servlet_limit] + // maximumPoolSize limits the total number of concurrent connections this pool will keep. Ideal + // values for this setting are highly variable on app design, infrastructure, and database. + config.setMaximumPoolSize(5); + // minimumIdle is the minimum number of idle connections Hikari maintains in the pool. + // Additional connections will be established to meet this value unless the pool is full. + config.setMinimumIdle(5); + // [END cloud_sql_postgres_servlet_limit] + + // [START cloud_sql_postgres_servlet_timeout] + // setConnectionTimeout is the maximum number of milliseconds to wait for a connection checkout. + // Any attempt to retrieve a connection from this pool that exceeds the set limit will throw an + // SQLException. + config.setConnectionTimeout(10000); // 10 seconds + // idleTimeout is the maximum amount of time a connection can sit in the pool. Connections that + // sit idle for this many milliseconds are retried if minimumIdle is exceeded. + config.setIdleTimeout(600000); // 10 minutes + // [END cloud_sql_postgres_servlet_timeout] + + // [START cloud_sql_postgres_servlet_backoff] + // Hikari automatically delays between failed connection attempts, eventually reaching a + // maximum delay of `connectionTimeout / 2` between attempts. + // [END cloud_sql_postgres_servlet_backoff] + + // [START cloud_sql_postgres_servlet_lifetime] + // maxLifetime is the maximum possible lifetime of a connection in the pool. Connections that + // live longer than this many milliseconds will be closed and reestablished between uses. This + // value should be several minutes shorter than the database's timeout value to avoid unexpected + // terminations. + config.setMaxLifetime(1800000); // 30 minutes + // [END cloud_sql_postgres_servlet_lifetime] + return config; + } +} diff --git a/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/ConnectorConnectionPoolFactory.java b/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/ConnectorConnectionPoolFactory.java new file mode 100644 index 00000000000..ca2dc179c15 --- /dev/null +++ b/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/ConnectorConnectionPoolFactory.java @@ -0,0 +1,107 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.cloudsql; + +// [START cloud_sql_postgres_servlet_connect_connector] +// [START cloud_sql_postgres_servlet_connect_unix] +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import javax.sql.DataSource; + +public class ConnectorConnectionPoolFactory extends ConnectionPoolFactory { + + // Note: Saving credentials in environment variables is convenient, but not + // secure - consider a more secure solution such as + // Cloud Secret Manager (https://cloud.google.com/secret-manager) to help + // keep secrets safe. + private static final String INSTANCE_CONNECTION_NAME = + System.getenv("INSTANCE_CONNECTION_NAME"); + private static final String INSTANCE_UNIX_SOCKET = System.getenv("INSTANCE_UNIX_SOCKET"); + private static final String DB_USER = System.getenv("DB_USER"); + private static final String DB_PASS = System.getenv("DB_PASS"); + private static final String DB_NAME = System.getenv("DB_NAME"); + + public static DataSource createConnectionPool() { + // The configuration object specifies behaviors for the connection pool. + HikariConfig config = new HikariConfig(); + + // The following URL is equivalent to setting the config options below: + // jdbc:postgresql:///?cloudSqlInstance=& + // socketFactory=com.google.cloud.sql.postgres.SocketFactory&user=&password= + // See the link below for more info on building a JDBC URL for the Cloud SQL JDBC Socket Factory + // https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory#creating-the-jdbc-url + + // Configure which instance and what database user to connect with. + config.setJdbcUrl(String.format("jdbc:postgresql:///%s", DB_NAME)); + config.setUsername(DB_USER); // e.g. "root", _postgres" + config.setPassword(DB_PASS); // e.g. "my-password" + + config.addDataSourceProperty("socketFactory", "com.google.cloud.sql_postgres.SocketFactory"); + config.addDataSourceProperty("cloudSqlInstance", INSTANCE_CONNECTION_NAME); + + // [END cloud_sql_postgres_servlet_connect_connector] + // Unix sockets are not natively supported in Java, so it is necessary to use the Cloud SQL + // Java Connector to connect. When setting INSTANCE_UNIX_SOCKET, the connector will + // call an external package that will enable Unix socket connections. + // Note: For Java users, the Cloud SQL Java Connector can provide authenticated connections + // which is usually preferable to using the Cloud SQL Proxy with Unix sockets. + // See https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory for details. + if (INSTANCE_UNIX_SOCKET != null) { + config.addDataSourceProperty("unixSocketPath", INSTANCE_UNIX_SOCKET); + } + // [START cloud_sql_postgres_servlet_connect_connector] + + // [END cloud_sql_postgres_servlet_connect_unix] + // The ipTypes argument can be used to specify a comma delimited list of preferred IP types + // for connecting to a Cloud SQL instance. The argument ipTypes=PRIVATE will force the + // SocketFactory to connect with an instance's associated private IP. + config.addDataSourceProperty("ipTypes", "PUBLIC,PRIVATE"); + // [START cloud_sql_postgres_servlet_connect_unix] + + // [END cloud_sql_postgres_servlet_connect_connector] + // [END cloud_sql_postgres_servlet_connect_unix] + // [START cloud_sql_postgres_servlet_auto_iam_authn] + // If connecting using automatic database authentication, follow the instructions for + // connecting using the connector, but set the DB_IAM_USER value to an IAM user or + // service account that has been given access to the database. + // See https://cloud.google.com/sql/docs/postgres/iam-logins for more details. + String dbIamUser = System.getenv("DB_IAM_USER"); + if (dbIamUser != null) { + config.addDataSourceProperty("enableIamAuth", "true"); + config.addDataSourceProperty("user", dbIamUser); + // Password must be set to a nonempty value to bypass driver validation errors. + config.addDataSourceProperty("password", "password"); + // Explicitly set sslmode to disable to prevent driver from hanging. + // The Java Connector will handle SSL so it is unneccesary to enable it at the driver level. + config.addDataSourceProperty("sslmode", "disable"); + } + // [END cloud_sql_postgres_servlet_auto_iam_authn] + // [START cloud_sql_postgres_servlet_connect_connector] + // [START cloud_sql_postgres_servlet_connect_unix] + + + // ... Specify additional connection properties here. + // [START_EXCLUDE] + configureConnectionPool(config); + // [END_EXCLUDE] + + // Initialize the connection pool using the configuration object. + return new HikariDataSource(config); + } +} +// [END cloud_sql_postgres_servlet_connect_connector] +// [END cloud_sql_postgres_servlet_connect_unix] diff --git a/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/IndexServlet.java b/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/IndexServlet.java index 32b1eeb92dc..10e73fd094c 100644 --- a/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/IndexServlet.java +++ b/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/IndexServlet.java @@ -43,69 +43,14 @@ @WebServlet(name = "Index", value = "") public class IndexServlet extends HttpServlet { - class TemplateData { - - public int tabCount; - public int spaceCount; - public List recentVotes; - - public TemplateData(int tabCount, int spaceCount, List recentVotes) { - this.tabCount = tabCount; - this.spaceCount = spaceCount; - this.recentVotes = recentVotes; - } - } - private static final Logger LOGGER = Logger.getLogger(IndexServlet.class.getName()); public TemplateData getTemplateData(DataSource pool) throws ServletException { - - int tabCount = 0; - int spaceCount = 0; - List recentVotes = new ArrayList<>(); - try (Connection conn = pool.getConnection()) { - // PreparedStatements are compiled by the database immediately and executed at a later date. - // Most databases cache previously compiled queries, which improves efficiency. - String stmt1 = "SELECT candidate, time_cast FROM votes ORDER BY time_cast DESC LIMIT 5"; - try (PreparedStatement voteStmt = conn.prepareStatement(stmt1);) { - // Execute the statement - ResultSet voteResults = voteStmt.executeQuery(); - // Convert a ResultSet into Vote objects - while (voteResults.next()) { - String candidate = voteResults.getString(1); - Timestamp timeCast = voteResults.getTimestamp(2); - recentVotes.add(new Vote(candidate.trim(), timeCast)); - } - } - - // PreparedStatements can also be executed multiple times with different arguments. This can - // improve efficiency, and project a query from being vulnerable to an SQL injection. - String stmt2 = "SELECT COUNT(vote_id) FROM votes WHERE candidate=?"; - try (PreparedStatement voteCountStmt = conn.prepareStatement(stmt2);) { - voteCountStmt.setString(1, "TABS"); - ResultSet tabResult = voteCountStmt.executeQuery(); - if (tabResult.next()) { // Move to the first result - tabCount = tabResult.getInt(1); - } - - voteCountStmt.setString(1, "SPACES"); - ResultSet spaceResult = voteCountStmt.executeQuery(); - if (spaceResult.next()) { // Move to the first result - spaceCount = spaceResult.getInt(1); - } - } + try { + return TemplateData.getTemplateData(pool); } catch (SQLException ex) { - // If something goes wrong, the application needs to react appropriately. This might mean - // getting a new connection and executing the query again, or it might mean redirecting the - // user to a different page to let them know something went wrong. - throw new ServletException( - "Unable to successfully connect to the database. Please check the " - + "steps in the README and try again.", - ex); + throw new ServletException(ex); } - TemplateData templateData = new TemplateData(tabCount, spaceCount, recentVotes); - - return templateData; } @Override diff --git a/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/TcpConnectionPoolFactory.java b/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/TcpConnectionPoolFactory.java new file mode 100644 index 00000000000..7908f2029ff --- /dev/null +++ b/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/TcpConnectionPoolFactory.java @@ -0,0 +1,87 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.cloudsql; + +// [START cloud_sql_postgres_servlet_connect_tcp] +// [START cloud_sql_postgres_servlet_connect_tcp_sslcerts] + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import javax.sql.DataSource; + +public class TcpConnectionPoolFactory extends ConnectionPoolFactory { + + // Note: Saving credentials in environment variables is convenient, but not + // secure - consider a more secure solution such as + // Cloud Secret Manager (https://cloud.google.com/secret-manager) to help + // keep secrets safe. + private static final String DB_USER = System.getenv("DB_USER"); + private static final String DB_PASS = System.getenv("DB_PASS"); + private static final String DB_NAME = System.getenv("DB_NAME"); + + private static final String INSTANCE_HOST = System.getenv("INSTANCE_HOST"); + private static final String DB_PORT = System.getenv("DB_PORT"); + + // [END cloud_sql_postgres_servlet_connect_tcp] + private static final String SSL_CLIENT_KEY_PATH = System.getenv("SSL_CLIENT_KEY_PATH"); + private static final String SSL_CLIENT_KEY_PASSWD = System.getenv("SSL_CLIENT_KEY_PASSWD"); + private static final String SSL_SERVER_CA_PATH = System.getenv("SSL_SERVER_CA_PATH"); + // [START cloud_sql_postgres_servlet_connect_tcp] + + public static DataSource createConnectionPool() { + // The configuration object specifies behaviors for the connection pool. + HikariConfig config = new HikariConfig(); + + // The following URL is equivalent to setting the config options below: + // jdbc:postgresql://:/?user=&password= + // See the link below for more info on building a JDBC URL for the Cloud SQL JDBC Socket Factory + // https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory#creating-the-jdbc-url + + // Configure which instance and what database user to connect with. + config.setJdbcUrl(String.format("jdbc:postgresql://%s:%s/%s", INSTANCE_HOST, DB_PORT, DB_NAME)); + config.setUsername(DB_USER); // e.g. "root", "postgres" + config.setPassword(DB_PASS); // e.g. "my-password" + + // [END cloud_sql_postgres_servlet_connect_tcp] + // (OPTIONAL) Configure SSL certificates + // For deployments that connect directly to a Cloud SQL instance without + // using the Cloud SQL Proxy, configuring SSL certificates will ensure the + // connection is encrypted. + // See the link below for more information on how to configure SSL Certificates for use with + // the Postgres JDBC driver + // https://jdbc.postgresql.org/documentation/head/ssl-client.html + if (SSL_CLIENT_KEY_PATH != null && SSL_SERVER_CA_PATH != null) { + config.addDataSourceProperty("ssl", "true"); + config.addDataSourceProperty("sslmode", "verify-full"); + + config.addDataSourceProperty("sslkey", SSL_CLIENT_KEY_PATH); + config.addDataSourceProperty("sslpassword", SSL_CLIENT_KEY_PASSWD); + config.addDataSourceProperty("sslrootcert", SSL_SERVER_CA_PATH); + } + // [START cloud_sql_postgres_servlet_connect_tcp] + + // ... Specify additional connection properties here. + // [START_EXCLUDE] + configureConnectionPool(config); + // [END_EXCLUDE] + + // Initialize the connection pool using the configuration object. + return new HikariDataSource(config); + } +} +// [END cloud_sql_postgres_servlet_connect_tcp] +// [END cloud_sql_postgres_servlet_connect_tcp_sslcerts] diff --git a/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/TemplateData.java b/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/TemplateData.java new file mode 100644 index 00000000000..10a7f00de27 --- /dev/null +++ b/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/TemplateData.java @@ -0,0 +1,88 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.cloudsql; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; +import javax.sql.DataSource; + +public class TemplateData { + + public int tabCount; + public int spaceCount; + public List recentVotes; + + public TemplateData(int tabCount, int spaceCount, List recentVotes) { + this.tabCount = tabCount; + this.spaceCount = spaceCount; + this.recentVotes = recentVotes; + } + + public static TemplateData getTemplateData(DataSource pool) throws SQLException { + int tabCount = 0; + int spaceCount = 0; + List recentVotes = new ArrayList<>(); + try (Connection conn = pool.getConnection()) { + // PreparedStatements are compiled by the database immediately and executed at a later date. + // Most databases cache previously compiled queries, which improves efficiency. + String stmt1 = "SELECT candidate, time_cast FROM votes ORDER BY time_cast DESC LIMIT 5"; + try (PreparedStatement voteStmt = conn.prepareStatement(stmt1);) { + // Execute the statement + ResultSet voteResults = voteStmt.executeQuery(); + // Convert a ResultSet into Vote objects + while (voteResults.next()) { + String candidate = voteResults.getString(1); + Timestamp timeCast = voteResults.getTimestamp(2); + recentVotes.add(new Vote(candidate.trim(), timeCast)); + } + } + + // PreparedStatements can also be executed multiple times with different arguments. This can + // improve efficiency, and project a query from being vulnerable to an SQL injection. + String stmt2 = "SELECT COUNT(vote_id) FROM votes WHERE candidate=?"; + try (PreparedStatement voteCountStmt = conn.prepareStatement(stmt2);) { + voteCountStmt.setString(1, "TABS"); + ResultSet tabResult = voteCountStmt.executeQuery(); + if (tabResult.next()) { // Move to the first result + tabCount = tabResult.getInt(1); + } + + voteCountStmt.setString(1, "SPACES"); + ResultSet spaceResult = voteCountStmt.executeQuery(); + if (spaceResult.next()) { // Move to the first result + spaceCount = spaceResult.getInt(1); + } + } + } catch (SQLException ex) { + // If something goes wrong, the application needs to react appropriately. This might mean + // getting a new connection and executing the query again, or it might mean redirecting the + // user to a different page to let them know something went wrong. + throw new SQLException( + "Unable to successfully connect to the database. Please check the " + + "steps in the README and try again.", + ex); + } + TemplateData templateData = new TemplateData(tabCount, spaceCount, recentVotes); + + return templateData; + } +} diff --git a/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/Utils.java b/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/Utils.java new file mode 100644 index 00000000000..0c1dcaceb41 --- /dev/null +++ b/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/Utils.java @@ -0,0 +1,54 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.cloudsql; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Locale; +import javax.annotation.Nullable; +import javax.sql.DataSource; + +public class Utils { + + // Used to validate user input. All user provided data should be validated and sanitized before + // being used something like a SQL query. Returns null if invalid. + @Nullable + public static String validateTeam(String input) { + if (input != null) { + input = input.toUpperCase(Locale.ENGLISH); + // Must be either "TABS" or "SPACES" + if (!"TABS".equals(input) && !"SPACES".equals(input)) { + return null; + } + } + return input; + } + + public static void createTable(DataSource pool) throws SQLException { + // Safely attempt to create the table schema. + try (Connection conn = pool.getConnection()) { + String stmt = + "CREATE TABLE IF NOT EXISTS votes ( " + + "vote_id SERIAL NOT NULL, time_cast timestamp NOT NULL, candidate CHAR(6) NOT NULL," + + " PRIMARY KEY (vote_id) );"; + try (PreparedStatement createTableStatement = conn.prepareStatement(stmt);) { + createTableStatement.execute(); + } + } + } +} diff --git a/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/functions/Main.java b/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/functions/Main.java new file mode 100644 index 00000000000..e42f4ce7128 --- /dev/null +++ b/cloud-sql/postgres/servlet/src/main/java/com/example/cloudsql/functions/Main.java @@ -0,0 +1,142 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.cloudsql.functions; + +import com.example.cloudsql.ConnectorConnectionPoolFactory; +import com.example.cloudsql.TcpConnectionPoolFactory; +import com.example.cloudsql.TemplateData; +import com.example.cloudsql.Utils; +import com.google.cloud.functions.HttpFunction; +import com.google.cloud.functions.HttpRequest; +import com.google.cloud.functions.HttpResponse; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.Date; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.sql.DataSource; + +public class Main implements HttpFunction { + + private Logger logger = Logger.getLogger(Main.class.getName()); + private static final Gson gson = new Gson(); + + // Declared at cold-start, but only initialized if/when the function executes + // Uses the "initialization-on-demand holder" idiom + // More information: https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom + private static class PoolHolder { + + // Making the default constructor private prohibits instantiation of this class + private PoolHolder() { + } + + // This value is initialized only if (and when) the getInstance() function below is called + private static final DataSource INSTANCE = setupPool(); + + private static DataSource setupPool() { + DataSource pool; + if (System.getenv("INSTANCE_HOST") != null) { + pool = TcpConnectionPoolFactory.createConnectionPool(); + } else { + pool = ConnectorConnectionPoolFactory.createConnectionPool(); + } + try { + Utils.createTable(pool); + } catch (SQLException ex) { + throw new RuntimeException( + "Unable to verify table schema. Please double check the steps" + + "in the README and try again.", + ex); + } + return pool; + } + + private static DataSource getInstance() { + return PoolHolder.INSTANCE; + } + } + + private void returnVoteCounts(HttpRequest req, HttpResponse resp) + throws SQLException, IOException { + DataSource pool = PoolHolder.getInstance(); + TemplateData templateData = TemplateData.getTemplateData(pool); + JsonObject respContent = new JsonObject(); + + // Return JSON Data + respContent.addProperty("tabCount", templateData.tabCount); + respContent.addProperty("spaceCount", templateData.spaceCount); + respContent.addProperty("recentVotes", gson.toJson(templateData.recentVotes)); + resp.getWriter().write(respContent.toString()); + resp.setStatusCode(HttpURLConnection.HTTP_OK); + } + + private void submitVote(HttpRequest req, HttpResponse resp) throws IOException { + DataSource pool = PoolHolder.getInstance(); + Timestamp now = new Timestamp(new Date().getTime()); + JsonObject body = gson.fromJson(req.getReader(), JsonObject.class); + String team = Utils.validateTeam(body.get("team").getAsString()); + if (team == null) { + resp.setStatusCode(400); + resp.getWriter().append("Invalid team specified."); + return; + } + try (Connection conn = pool.getConnection()) { + // PreparedStatements can be more efficient and project against injections. + String stmt = "INSERT INTO votes (time_cast, candidate) VALUES (?, ?);"; + try (PreparedStatement voteStmt = conn.prepareStatement(stmt);) { + voteStmt.setTimestamp(1, now); + voteStmt.setString(2, team); + + // Finally, execute the statement. If it fails, an error will be thrown. + voteStmt.execute(); + } + } catch (SQLException ex) { + // If something goes wrong, handle the error in this section. This might involve retrying or + // adjusting parameters depending on the situation. + logger.log(Level.WARNING, "Error while attempting to submit vote.", ex); + resp.setStatusCode(500); + resp.getWriter() + .write( + "Unable to successfully cast vote! Please check the application " + + "logs for more details."); + } + } + + @Override + public void service(HttpRequest req, HttpResponse resp) throws IOException, SQLException { + + String method = req.getMethod(); + switch (method) { + case "GET": + returnVoteCounts(req, resp); + break; + case "POST": + submitVote(req, resp); + break; + default: + resp.setStatusCode(HttpURLConnection.HTTP_BAD_METHOD); + resp.getWriter().write(String.format("HTTP Method %s is not supported", method)); + break; + } + } +} diff --git a/cloud-sql/postgres/servlet/src/test/java/com/TestIndexServletPostgres.java b/cloud-sql/postgres/servlet/src/test/java/com/TestIndexServletPostgres.java index d0d6c4e62c6..4cfde1336f4 100644 --- a/cloud-sql/postgres/servlet/src/test/java/com/TestIndexServletPostgres.java +++ b/cloud-sql/postgres/servlet/src/test/java/com/TestIndexServletPostgres.java @@ -22,7 +22,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.example.cloudsql.IndexServlet.TemplateData; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import java.io.PrintWriter; diff --git a/cloud-sql/r2dbc/pom.xml b/cloud-sql/r2dbc/pom.xml index 07429380ba4..5cedae050d2 100644 --- a/cloud-sql/r2dbc/pom.xml +++ b/cloud-sql/r2dbc/pom.xml @@ -48,7 +48,7 @@ com.google.cloud.sql cloud-sql-connector-r2dbc-mysql - 1.5.0 + 1.6.0 @@ -60,7 +60,7 @@ com.google.cloud.sql cloud-sql-connector-r2dbc-postgres - 1.5.0 + 1.6.0 @@ -85,7 +85,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/cloud-sql/sqlserver/client-side-encryption/pom.xml b/cloud-sql/sqlserver/client-side-encryption/pom.xml index bf94bc91e9f..a810a155482 100644 --- a/cloud-sql/sqlserver/client-side-encryption/pom.xml +++ b/cloud-sql/sqlserver/client-side-encryption/pom.xml @@ -48,7 +48,7 @@ com.google.api-client google-api-client - 1.33.4 + 1.34.1 @@ -57,17 +57,17 @@ com.google.http-client google-http-client-jackson2 - 1.41.6 + 1.41.8 com.google.cloud.sql cloud-sql-connector-jdbc-sqlserver - 1.5.0 + 1.6.0 com.microsoft.sqlserver mssql-jdbc - 9.4.1.jre8 + 10.2.0.jre8 com.google.crypto.tink diff --git a/cloud-sql/sqlserver/client-side-encryption/src/main/java/cloudsql/tink/CloudSqlConnectionPool.java b/cloud-sql/sqlserver/client-side-encryption/src/main/java/cloudsql/tink/CloudSqlConnectionPool.java index 23eaa7c6866..a0a9438f943 100644 --- a/cloud-sql/sqlserver/client-side-encryption/src/main/java/cloudsql/tink/CloudSqlConnectionPool.java +++ b/cloud-sql/sqlserver/client-side-encryption/src/main/java/cloudsql/tink/CloudSqlConnectionPool.java @@ -35,6 +35,10 @@ public static DataSource createConnectionPool(String dbUser, String dbPass, Stri config.setPassword(dbPass); // e.g. "my-password" config.addDataSourceProperty("databaseName", dbName); + // The Cloud SQL Java Connector provides SSL encryption so + // it should be disabled at the driver level + config.addDataSourceProperty("encrypt", "false"); + config.addDataSourceProperty("socketFactoryClass", "com.google.cloud.sql.sqlserver.SocketFactory"); config.addDataSourceProperty("socketFactoryConstructorArg", instanceConnectionName); @@ -60,4 +64,4 @@ public static void createTable(DataSource pool, String tableName) throws SQLExce } } } -// [END cloud_sql_sqlserver_cse_db] \ No newline at end of file +// [END cloud_sql_sqlserver_cse_db] diff --git a/cloud-sql/sqlserver/servlet/.env.yaml b/cloud-sql/sqlserver/servlet/.env.yaml new file mode 100644 index 00000000000..807c8b3988f --- /dev/null +++ b/cloud-sql/sqlserver/servlet/.env.yaml @@ -0,0 +1,6 @@ +INSTANCE_CONNECTION_NAME: ::INSTANCE-NAME> +INSTANCE_HOST: '127.0.0.1' +DB_PORT: 1433 +DB_USER: +DB_PASS: +DB_NAME: diff --git a/cloud-sql/sqlserver/servlet/README.md b/cloud-sql/sqlserver/servlet/README.md index cde50bd12b4..b895b145123 100644 --- a/cloud-sql/sqlserver/servlet/README.md +++ b/cloud-sql/sqlserver/servlet/README.md @@ -31,6 +31,34 @@ export DB_NAME='my_db' Note: Saving credentials in environment variables is convenient, but not secure - consider a more secure solution such as [Cloud KMS](https://cloud.google.com/kms/) or [Secret Manager](https://cloud.google.com/secret-manager/) to help keep secrets safe. +## Configure SSL Certificates +For deployments that connect directly to a Cloud SQL instance with TCP, +without using the Cloud SQL Proxy, +configuring SSL certificates will ensure the connection is encrypted. +1. Use the gcloud CLI to [download the server certificate](https://cloud.google.com/sql/docs/mysql/configure-ssl-instance#server-certs) for your Cloud SQL instance. + - Get information about the service certificate: + ``` + gcloud beta sql ssl server-ca-certs list --instance=INSTANCE_NAME + ``` + - Create a server certificate: + ``` + gcloud beta sql ssl server-ca-certs create --instance=INSTANCE_NAME + ``` + - Download the certificate information to a local PEM file + ``` + gcloud beta sql ssl server-ca-certs list \ + --format="value(cert)" \ + --instance=INSTANCE_NAME > \ + server-ca.pem + ``` + +1. [Import the server certificate into a custom Java truststore](https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-using-ssl.html) using `keytool`: + ``` + keytool -importcert -alias MySQLCACert -file server-ca.pem \ + -keystore -storepass + ``` +1. Set the `TRUST_CERT_KEYSTORE_PATH` and `TRUST_CERT_KEYSTORE_PASSWD` environment variables to the values used in the previous step. + ## Deploying locally To run this application locally, run the following command inside the project folder: @@ -50,7 +78,7 @@ and verify that has been added in your build section as a plugin. -### Development Server +### App Engine Development Server The following command will run the application locally in the the GAE-development server: ```bash @@ -59,6 +87,12 @@ mvn clean package appengine:run Note: if the GAE development server fails to start, check that you are using a supported version of Java. Supported versions are Java 8 and Java 11. +### Cloud Functions Development Server +To run the application locally as a Cloud Function, run the following command: +``` +mvn function:run -Drun.functionTarget=com.example.cloudsql.functions.Main +``` + ### Deploy to Google Cloud First, update `src/main/webapp/WEB-INF/appengine-web.xml` with the correct values to pass the @@ -125,5 +159,16 @@ mvn clean package com.google.cloud.tools:jib-maven-plugin:2.8.0:build \ For more details about using Cloud Run see http://cloud.run. Review other [Java on Cloud Run samples](../../../run/). +### Deploy to Google Cloud Functions + +To deploy the application to Cloud Functions, first fill in the values for required environment variables in `.env.yaml`. Then run the following command +``` +gcloud functions deploy sql-sample \ + --trigger-http \ + --entry-point com.example.cloudsql.functions.Main \ + --runtime java11 \ + --env-vars-file .env.yaml +``` + ### Cleanup To avoid incurring any charges, navigate to your project's [App Engine settings](https://console.cloud.google.com/appengine/settings) and click `Disable Application`. Also [delete your Cloud SQL Instance](https://cloud.google.com/sql/docs/mysql/delete-instance) if you no longer need it. diff --git a/cloud-sql/sqlserver/servlet/pom.xml b/cloud-sql/sqlserver/servlet/pom.xml index c69d602fc9e..0dcb41a1304 100644 --- a/cloud-sql/sqlserver/servlet/pom.xml +++ b/cloud-sql/sqlserver/servlet/pom.xml @@ -52,12 +52,12 @@ com.microsoft.sqlserver mssql-jdbc - 9.4.1.jre8 + 10.2.0.jre8 com.google.cloud.sql cloud-sql-connector-jdbc-sqlserver - 1.5.0 + 1.6.0 com.zaxxer @@ -67,7 +67,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test @@ -82,10 +82,27 @@ 1.1.3 test + + + com.google.cloud.functions.invoker + java-function-invoker + 1.0.1 + + + com.google.cloud.functions + functions-framework-api + 1.0.4 + provided + + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + org.eclipse.jetty jetty-maven-plugin @@ -98,7 +115,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/ConnectionPoolContextListener.java b/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/ConnectionPoolContextListener.java index cd25eb98bb2..b7f534bb2fc 100644 --- a/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/ConnectionPoolContextListener.java +++ b/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/ConnectionPoolContextListener.java @@ -16,10 +16,7 @@ package com.example.cloudsql; -import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; -import java.sql.Connection; -import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.logging.Logger; import javax.servlet.ServletContextEvent; @@ -40,94 +37,6 @@ public class ConnectionPoolContextListener implements ServletContextListener { private static final String DB_PASS = System.getenv("DB_PASS"); private static final String DB_NAME = System.getenv("DB_NAME"); - private DataSource createConnectionPool() { - // [START cloud_sql_sqlserver_servlet_create] - // Note: For Java users, the Cloud SQL JDBC Socket Factory can provide authenticated connections - // which is preferred to using the Cloud SQL Proxy with Unix sockets. - // See https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory for details. - - // The configuration object specifies behaviors for the connection pool. - HikariConfig config = new HikariConfig(); - - // The following is equivalent to setting the config options below: - // jdbc:sqlserver://;user=;password=;databaseName=; - // socketFactoryClass=com.google.cloud.sql.sqlserver.SocketFactory; - // socketFactoryConstructorArg= - - // See the link below for more info on building a JDBC URL for the Cloud SQL JDBC Socket Factory - // https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory#creating-the-jdbc-url - - // Configure which instance and what database user to connect with. - config - .setDataSourceClassName("com.microsoft.sqlserver.jdbc.SQLServerDataSource"); - config.setUsername(DB_USER); // e.g. "root", "sqlserver" - config.setPassword(DB_PASS); // e.g. "my-password" - config.addDataSourceProperty("databaseName", DB_NAME); - - config.addDataSourceProperty("socketFactoryClass", - "com.google.cloud.sql.sqlserver.SocketFactory"); - config.addDataSourceProperty("socketFactoryConstructorArg", INSTANCE_CONNECTION_NAME); - - // ... Specify additional connection properties here. - - // [START_EXCLUDE] - - // [START cloud_sql_sqlserver_servlet_limit] - // maximumPoolSize limits the total number of concurrent connections this pool will keep. Ideal - // values for this setting are highly variable on app design, infrastructure, and database. - config.setMaximumPoolSize(5); - // minimumIdle is the minimum number of idle connections Hikari maintains in the pool. - // Additional connections will be established to meet this value unless the pool is full. - config.setMinimumIdle(5); - // [END cloud_sql_sqlserver_servlet_limit] - - // [START cloud_sql_sqlserver_servlet_timeout] - // setConnectionTimeout is the maximum number of milliseconds to wait for a connection checkout. - // Any attempt to retrieve a connection from this pool that exceeds the set limit will throw an - // SQLException. - config.setConnectionTimeout(10000); // 10 seconds - // idleTimeout is the maximum amount of time a connection can sit in the pool. Connections that - // sit idle for this many milliseconds are retried if minimumIdle is exceeded. - config.setIdleTimeout(600000); // 10 minutes - // [END cloud_sql_sqlserver_servlet_timeout] - - // [START cloud_sql_sqlserver_servlet_backoff] - // Hikari automatically delays between failed connection attempts, eventually reaching a - // maximum delay of `connectionTimeout / 2` between attempts. - // [END cloud_sql_sqlserver_servlet_backoff] - - // [START cloud_sql_sqlserver_servlet_lifetime] - // maxLifetime is the maximum possible lifetime of a connection in the pool. Connections that - // live longer than this many milliseconds will be closed and reestablished between uses. This - // value should be several minutes shorter than the database's timeout value to avoid unexpected - // terminations. - config.setMaxLifetime(1800000); // 30 minutes - // [END cloud_sql_sqlserver_servlet_lifetime] - - // [END_EXCLUDE] - - // Initialize the connection pool using the configuration object. - DataSource pool = new HikariDataSource(config); - // [END cloud_sql_sqlserver_servlet_create] - return pool; - } - - private void createTable(DataSource pool) throws SQLException { - // Safely attempt to create the table schema. - try (Connection conn = pool.getConnection()) { - PreparedStatement createTableStatement = conn.prepareStatement( - "IF NOT EXISTS (" - + "SELECT * FROM sysobjects WHERE name='votes' and xtype='U')" - + "CREATE TABLE votes (" - + "vote_id INT NOT NULL IDENTITY," - + "time_cast DATETIME NOT NULL," - + "candidate VARCHAR(6) NOT NULL," - + "PRIMARY KEY (vote_id));" - ); - createTableStatement.execute(); - } - } - @Override public void contextDestroyed(ServletContextEvent event) { // This function is called when the Servlet is destroyed. @@ -143,11 +52,16 @@ public void contextInitialized(ServletContextEvent event) { // that can be used to connect to. DataSource pool = (DataSource) event.getServletContext().getAttribute("my-pool"); if (pool == null) { - pool = createConnectionPool(); + if (System.getenv("INSTANCE_HOST") != null) { + pool = TcpConnectionPoolFactory.createConnectionPool(); + } else { + pool = ConnectorConnectionPoolFactory.createConnectionPool(); + } event.getServletContext().setAttribute("my-pool", pool); } try { - createTable(pool); + // from src/main/java/com/example/cloudsql/Utils.java + Utils.createTable(pool); } catch (SQLException ex) { throw new RuntimeException("Unable to verify table schema. Please double check the steps" + "in the README and try again.", ex); diff --git a/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/ConnectionPoolFactory.java b/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/ConnectionPoolFactory.java new file mode 100644 index 00000000000..e35b731771d --- /dev/null +++ b/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/ConnectionPoolFactory.java @@ -0,0 +1,57 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.cloudsql; + +import com.zaxxer.hikari.HikariConfig; + +public class ConnectionPoolFactory { + + public static HikariConfig configureConnectionPool(HikariConfig config) { + // [START cloud_sql_sqlserver_servlet_limit] + // maximumPoolSize limits the total number of concurrent connections this pool will keep. Ideal + // values for this setting are highly variable on app design, infrastructure, and database. + config.setMaximumPoolSize(5); + // minimumIdle is the minimum number of idle connections Hikari maintains in the pool. + // Additional connections will be established to meet this value unless the pool is full. + config.setMinimumIdle(5); + // [END cloud_sql_sqlserver_servlet_limit] + + // [START cloud_sql_sqlserver_servlet_timeout] + // setConnectionTimeout is the maximum number of milliseconds to wait for a connection checkout. + // Any attempt to retrieve a connection from this pool that exceeds the set limit will throw an + // SQLException. + config.setConnectionTimeout(10000); // 10 seconds + // idleTimeout is the maximum amount of time a connection can sit in the pool. Connections that + // sit idle for this many milliseconds are retried if minimumIdle is exceeded. + config.setIdleTimeout(600000); // 10 minutes + // [END cloud_sql_sqlserver_servlet_timeout] + + // [START cloud_sql_sqlserver_servlet_backoff] + // Hikari automatically delays between failed connection attempts, eventually reaching a + // maximum delay of `connectionTimeout / 2` between attempts. + // [END cloud_sql_sqlserver_servlet_backoff] + + // [START cloud_sql_sqlserver_servlet_lifetime] + // maxLifetime is the maximum possible lifetime of a connection in the pool. Connections that + // live longer than this many milliseconds will be closed and reestablished between uses. This + // value should be several minutes shorter than the database's timeout value to avoid unexpected + // terminations. + config.setMaxLifetime(1800000); // 30 minutes + // [END cloud_sql_sqlserver_servlet_lifetime] + return config; + } +} diff --git a/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/ConnectorConnectionPoolFactory.java b/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/ConnectorConnectionPoolFactory.java new file mode 100644 index 00000000000..b4d738cd846 --- /dev/null +++ b/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/ConnectorConnectionPoolFactory.java @@ -0,0 +1,74 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.cloudsql; + +// [START cloud_sql_sqlserver_servlet_connect_connector] + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import javax.sql.DataSource; + +public class ConnectorConnectionPoolFactory extends ConnectionPoolFactory { + + // Note: Saving credentials in environment variables is convenient, but not + // secure - consider a more secure solution such as + // Cloud Secret Manager (https://cloud.google.com/secret-manager) to help + // keep secrets safe. + private static final String INSTANCE_CONNECTION_NAME = + System.getenv("INSTANCE_CONNECTION_NAME"); + private static final String DB_USER = System.getenv("DB_USER"); + private static final String DB_PASS = System.getenv("DB_PASS"); + private static final String DB_NAME = System.getenv("DB_NAME"); + + public static DataSource createConnectionPool() { + // The configuration object specifies behaviors for the connection pool. + HikariConfig config = new HikariConfig(); + + // The following is equivalent to setting the config options below: + // jdbc:sqlserver://;user=;password=;databaseName=; + // socketFactoryClass=com.google.cloud.sql.sqlserver.SocketFactory; + // socketFactoryConstructorArg= + + // See the link below for more info on building a JDBC URL for the Cloud SQL JDBC Socket Factory + // https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory#creating-the-jdbc-url + + // Configure which instance and what database user to connect with. + config + .setDataSourceClassName("com.microsoft.sqlserver.jdbc.SQLServerDataSource"); + config.setUsername(DB_USER); // e.g. "root", "sqlserver" + config.setPassword(DB_PASS); // e.g. "my-password" + config.addDataSourceProperty("databaseName", DB_NAME); + + config.addDataSourceProperty("socketFactoryClass", + "com.google.cloud.sql.sqlserver.SocketFactory"); + config.addDataSourceProperty("socketFactoryConstructorArg", INSTANCE_CONNECTION_NAME); + + // The Java Connector provides SSL encryption, so it should be disabled + // at the driver level. + config.addDataSourceProperty("encrypt", "false"); + + // ... Specify additional connection properties here. + // [START_EXCLUDE] + configureConnectionPool(config); + // [END_EXCLUDE] + + // Initialize the connection pool using the configuration object. + return new HikariDataSource(config); + } +} +// [END cloud_sql_sqlserver_servlet_connect_connector] + diff --git a/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/IndexServlet.java b/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/IndexServlet.java index 425d358f132..60aa1ba8621 100644 --- a/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/IndexServlet.java +++ b/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/IndexServlet.java @@ -19,12 +19,9 @@ import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; -import java.util.ArrayList; import java.util.Date; -import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletException; @@ -35,71 +32,17 @@ import javax.sql.DataSource; - @WebServlet(name = "Index", value = "") public class IndexServlet extends HttpServlet { private static final Logger LOGGER = Logger.getLogger(IndexServlet.class.getName()); - class TemplateData { - - public int tabCount; - public int spaceCount; - public List recentVotes; - - public TemplateData(int tabCount, int spaceCount, List recentVotes) { - this.tabCount = tabCount; - this.spaceCount = spaceCount; - this.recentVotes = recentVotes; - } - } - public TemplateData getTemplateData(DataSource pool) throws ServletException { - - int tabCount; - int spaceCount; - List recentVotes = new ArrayList<>(); - try (Connection conn = pool.getConnection()) { - // PreparedStatements are compiled by the database immediately and executed at a later date. - // Most databases cache previously compiled queries, which improves efficiency. - PreparedStatement voteStmt = conn.prepareStatement( - "SELECT TOP(5) candidate, time_cast FROM votes ORDER BY time_cast DESC"); - // Execute the statement - ResultSet voteResults = voteStmt.executeQuery(); - // Convert a ResultSet into Vote objects - while (voteResults.next()) { - String candidate = voteResults.getString(1); - Timestamp timeCast = voteResults.getTimestamp(2); - Vote vote = new Vote(candidate.trim(), timeCast); - recentVotes.add(vote); - } - - // PreparedStatements can also be executed multiple times with different arguments. This can - // improve efficiency, and project a query from being vulnerable to an SQL injection. - PreparedStatement voteCountStmt = conn.prepareStatement( - "SELECT COUNT(vote_id) FROM votes WHERE candidate=?"); - - voteCountStmt.setString(1, "TABS"); - ResultSet tabResult = voteCountStmt.executeQuery(); - tabResult.next(); // Move to the first result - tabCount = tabResult.getInt(1); - - voteCountStmt.setString(1, "SPACES"); - ResultSet spaceResult = voteCountStmt.executeQuery(); - spaceResult.next(); // Move to the first result - spaceCount = spaceResult.getInt(1); - + try { + return TemplateData.getTemplateData(pool); } catch (SQLException ex) { - // If something goes wrong, the application needs to react appropriately. This might mean - // getting a new connection and executing the query again, or it might mean redirecting the - // user to a different page to let them know something went wrong. - throw new ServletException("Unable to successfully connect to the database. Please check the " - + "steps in the README and try again.", ex); + throw new ServletException(ex); } - - TemplateData templateData = new TemplateData(tabCount, spaceCount, recentVotes); - - return templateData; } @Override @@ -122,12 +65,9 @@ public void doGet(HttpServletRequest req, HttpServletResponse resp) public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { // Get the team from the request and record the time of the vote. - String team = req.getParameter("team"); - if (team != null) { - team = team.toUpperCase(); - } + String team = Utils.validateTeam(req.getParameter("team")); Timestamp now = new Timestamp(new Date().getTime()); - if (team == null || (!team.equals("TABS") && !team.equals("SPACES"))) { + if (team == null) { resp.setStatus(400); resp.getWriter().append("Invalid team specified."); return; diff --git a/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/TcpConnectionPoolFactory.java b/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/TcpConnectionPoolFactory.java new file mode 100644 index 00000000000..97f64d02286 --- /dev/null +++ b/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/TcpConnectionPoolFactory.java @@ -0,0 +1,81 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.cloudsql; + +// [START cloud_sql_sqlserver_servlet_connect_tcp] +// [START cloud_sql_sqlserver_servlet_connect_tcp_sslcerts] + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import javax.sql.DataSource; + +public class TcpConnectionPoolFactory extends ConnectionPoolFactory { + + // Note: Saving credentials in environment variables is convenient, but not + // secure - consider a more secure solution such as + // Cloud Secret Manager (https://cloud.google.com/secret-manager) to help + // keep secrets safe. + private static final String DB_USER = System.getenv("DB_USER"); + private static final String DB_PASS = System.getenv("DB_PASS"); + private static final String DB_NAME = System.getenv("DB_NAME"); + + private static final String INSTANCE_HOST = System.getenv("INSTANCE_HOST"); + private static final String DB_PORT = System.getenv("DB_PORT"); + + // [END cloud_sql_sqlserver_servlet_connect_tcp] + private static final String TRUST_CERT_KEYSTORE_PATH = System.getenv( + "TRUST_CERT_KEYSTORE_NAME"); + private static final String TRUST_CERT_KEYSTORE_PASSWD = System.getenv( + "TRUST_CERT_KEYSTORE_PASSWD"); + // [START cloud_sql_sqlserver_servlet_connect_tcp] + + public static DataSource createConnectionPool() { + // The configuration object specifies behaviors for the connection pool. + HikariConfig config = new HikariConfig(); + + // Configure which instance and what database user to connect with. + config.setJdbcUrl( + String.format("jdbc:sqlserver://%s:%s;databaseName=%s", INSTANCE_HOST, DB_PORT, DB_NAME)); + config.setUsername(DB_USER); // e.g. "root", "sqlserver" + config.setPassword(DB_PASS); // e.g. "my-password" + + // [END cloud_sql_sqlserver_servlet_connect_tcp] + // (OPTIONAL) Configure SSL certificates + // For deployments that connect directly to a Cloud SQL instance without + // using the Cloud SQL Proxy, configuring SSL certificates will ensure the + // connection is encrypted. + // For details about how the SQL Server JDBC driver handles SSL encryption, see the link below + // https://docs.microsoft.com/en-us/sql/connect/jdbc/understanding-ssl-support?view=sql-server-ver15 + + if (TRUST_CERT_KEYSTORE_PATH != null) { + config.addDataSourceProperty("encrypt", "true"); + config.addDataSourceProperty("trustStore", TRUST_CERT_KEYSTORE_PATH); + config.addDataSourceProperty("trustStorePassword", TRUST_CERT_KEYSTORE_PASSWD); + } + // [START cloud_sql_sqlserver_servlet_connect_tcp] + + // ... Specify additional connection properties here. + // [START_EXCLUDE] + configureConnectionPool(config); + // [END_EXCLUDE] + + // Initialize the connection pool using the configuration object. + return new HikariDataSource(config); + } +} +// [END cloud_sql_sqlserver_servlet_connect_tcp] +// [END cloud_sql_sqlserver_servlet_connect_tcp_sslcerts] diff --git a/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/TemplateData.java b/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/TemplateData.java new file mode 100644 index 00000000000..c2cb79e0a3d --- /dev/null +++ b/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/TemplateData.java @@ -0,0 +1,84 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.cloudsql; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; +import javax.sql.DataSource; + +public class TemplateData { + + public int tabCount; + public int spaceCount; + public List recentVotes; + + public TemplateData(int tabCount, int spaceCount, List recentVotes) { + this.tabCount = tabCount; + this.spaceCount = spaceCount; + this.recentVotes = recentVotes; + } + + public static TemplateData getTemplateData(DataSource pool) throws SQLException { + int tabCount; + int spaceCount; + List recentVotes = new ArrayList<>(); + try (Connection conn = pool.getConnection()) { + // PreparedStatements are compiled by the database immediately and executed at a later date. + // Most databases cache previously compiled queries, which improves efficiency. + PreparedStatement voteStmt = conn.prepareStatement( + "SELECT TOP(5) candidate, time_cast FROM votes ORDER BY time_cast DESC"); + // Execute the statement + ResultSet voteResults = voteStmt.executeQuery(); + // Convert a ResultSet into Vote objects + while (voteResults.next()) { + String candidate = voteResults.getString(1); + Timestamp timeCast = voteResults.getTimestamp(2); + Vote vote = new Vote(candidate.trim(), timeCast); + recentVotes.add(vote); + } + + // PreparedStatements can also be executed multiple times with different arguments. This can + // improve efficiency, and project a query from being vulnerable to an SQL injection. + PreparedStatement voteCountStmt = conn.prepareStatement( + "SELECT COUNT(vote_id) FROM votes WHERE candidate=?"); + + voteCountStmt.setString(1, "TABS"); + ResultSet tabResult = voteCountStmt.executeQuery(); + tabResult.next(); // Move to the first result + tabCount = tabResult.getInt(1); + + voteCountStmt.setString(1, "SPACES"); + ResultSet spaceResult = voteCountStmt.executeQuery(); + spaceResult.next(); // Move to the first result + spaceCount = spaceResult.getInt(1); + + } catch (SQLException ex) { + // If something goes wrong, the application needs to react appropriately. This might mean + // getting a new connection and executing the query again, or it might mean redirecting the + // user to a different page to let them know something went wrong. + throw new SQLException("Unable to successfully connect to the database. Please check the " + + "steps in the README and try again.", ex); + } + + return new TemplateData(tabCount, spaceCount, recentVotes); + } +} diff --git a/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/Utils.java b/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/Utils.java new file mode 100644 index 00000000000..08b6425982e --- /dev/null +++ b/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/Utils.java @@ -0,0 +1,59 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.cloudsql; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Locale; +import javax.annotation.Nullable; +import javax.sql.DataSource; + +public class Utils { + + // Used to validate user input. All user provided data should be validated and sanitized before + // being used something like a SQL query. Returns null if invalid. + @Nullable + public static String validateTeam(String input) { + if (input != null) { + input = input.toUpperCase(Locale.ENGLISH); + // Must be either "TABS" or "SPACES" + if (!"TABS".equals(input) && !"SPACES".equals(input)) { + return null; + } + } + return input; + } + + public static void createTable(DataSource pool) throws SQLException { + // Safely attempt to create the table schema. + try (Connection conn = pool.getConnection()) { + PreparedStatement createTableStatement = conn.prepareStatement( + "IF NOT EXISTS (" + + "SELECT * FROM sysobjects WHERE name='votes' and xtype='U')" + + "CREATE TABLE votes (" + + "vote_id INT NOT NULL IDENTITY," + + "time_cast DATETIME NOT NULL," + + "candidate VARCHAR(6) NOT NULL," + + "PRIMARY KEY (vote_id));" + ); + createTableStatement.execute(); + } + } + + +} diff --git a/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/functions/Main.java b/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/functions/Main.java new file mode 100644 index 00000000000..e42f4ce7128 --- /dev/null +++ b/cloud-sql/sqlserver/servlet/src/main/java/com/example/cloudsql/functions/Main.java @@ -0,0 +1,142 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.cloudsql.functions; + +import com.example.cloudsql.ConnectorConnectionPoolFactory; +import com.example.cloudsql.TcpConnectionPoolFactory; +import com.example.cloudsql.TemplateData; +import com.example.cloudsql.Utils; +import com.google.cloud.functions.HttpFunction; +import com.google.cloud.functions.HttpRequest; +import com.google.cloud.functions.HttpResponse; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.Date; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.sql.DataSource; + +public class Main implements HttpFunction { + + private Logger logger = Logger.getLogger(Main.class.getName()); + private static final Gson gson = new Gson(); + + // Declared at cold-start, but only initialized if/when the function executes + // Uses the "initialization-on-demand holder" idiom + // More information: https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom + private static class PoolHolder { + + // Making the default constructor private prohibits instantiation of this class + private PoolHolder() { + } + + // This value is initialized only if (and when) the getInstance() function below is called + private static final DataSource INSTANCE = setupPool(); + + private static DataSource setupPool() { + DataSource pool; + if (System.getenv("INSTANCE_HOST") != null) { + pool = TcpConnectionPoolFactory.createConnectionPool(); + } else { + pool = ConnectorConnectionPoolFactory.createConnectionPool(); + } + try { + Utils.createTable(pool); + } catch (SQLException ex) { + throw new RuntimeException( + "Unable to verify table schema. Please double check the steps" + + "in the README and try again.", + ex); + } + return pool; + } + + private static DataSource getInstance() { + return PoolHolder.INSTANCE; + } + } + + private void returnVoteCounts(HttpRequest req, HttpResponse resp) + throws SQLException, IOException { + DataSource pool = PoolHolder.getInstance(); + TemplateData templateData = TemplateData.getTemplateData(pool); + JsonObject respContent = new JsonObject(); + + // Return JSON Data + respContent.addProperty("tabCount", templateData.tabCount); + respContent.addProperty("spaceCount", templateData.spaceCount); + respContent.addProperty("recentVotes", gson.toJson(templateData.recentVotes)); + resp.getWriter().write(respContent.toString()); + resp.setStatusCode(HttpURLConnection.HTTP_OK); + } + + private void submitVote(HttpRequest req, HttpResponse resp) throws IOException { + DataSource pool = PoolHolder.getInstance(); + Timestamp now = new Timestamp(new Date().getTime()); + JsonObject body = gson.fromJson(req.getReader(), JsonObject.class); + String team = Utils.validateTeam(body.get("team").getAsString()); + if (team == null) { + resp.setStatusCode(400); + resp.getWriter().append("Invalid team specified."); + return; + } + try (Connection conn = pool.getConnection()) { + // PreparedStatements can be more efficient and project against injections. + String stmt = "INSERT INTO votes (time_cast, candidate) VALUES (?, ?);"; + try (PreparedStatement voteStmt = conn.prepareStatement(stmt);) { + voteStmt.setTimestamp(1, now); + voteStmt.setString(2, team); + + // Finally, execute the statement. If it fails, an error will be thrown. + voteStmt.execute(); + } + } catch (SQLException ex) { + // If something goes wrong, handle the error in this section. This might involve retrying or + // adjusting parameters depending on the situation. + logger.log(Level.WARNING, "Error while attempting to submit vote.", ex); + resp.setStatusCode(500); + resp.getWriter() + .write( + "Unable to successfully cast vote! Please check the application " + + "logs for more details."); + } + } + + @Override + public void service(HttpRequest req, HttpResponse resp) throws IOException, SQLException { + + String method = req.getMethod(); + switch (method) { + case "GET": + returnVoteCounts(req, resp); + break; + case "POST": + submitVote(req, resp); + break; + default: + resp.setStatusCode(HttpURLConnection.HTTP_BAD_METHOD); + resp.getWriter().write(String.format("HTTP Method %s is not supported", method)); + break; + } + } +} diff --git a/cloud-sql/sqlserver/servlet/src/test/java/com/TestIndexServletSqlServer.java b/cloud-sql/sqlserver/servlet/src/test/java/com/TestIndexServletSqlServer.java index b6c930d71bf..c73d91ba42a 100644 --- a/cloud-sql/sqlserver/servlet/src/test/java/com/TestIndexServletSqlServer.java +++ b/cloud-sql/sqlserver/servlet/src/test/java/com/TestIndexServletSqlServer.java @@ -22,7 +22,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.example.cloudsql.IndexServlet.TemplateData; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import java.io.PrintWriter; @@ -94,6 +93,7 @@ public static void createPool() throws SQLException { "com.google.cloud.sql.sqlserver.SocketFactory"); config.addDataSourceProperty("socketFactoryConstructorArg", System.getenv("SQLSERVER_CONNECTION_NAME")); + config.addDataSourceProperty("encrypt", "false"); pool = new HikariDataSource(config); createTable(pool); @@ -138,4 +138,4 @@ public void testServletPost() throws Exception { writer.flush(); assertTrue(stringWriter.toString().contains("Vote successfully cast for")); } -} \ No newline at end of file +} diff --git a/compute/cloud-client/pom.xml b/compute/cloud-client/pom.xml index 93922c625be..511e6201519 100644 --- a/compute/cloud-client/pom.xml +++ b/compute/cloud-client/pom.xml @@ -23,7 +23,7 @@ google-cloud-compute com.google.cloud - 1.7.0 + 1.8.1 google-cloud-storage @@ -37,7 +37,7 @@ com.google.api gax-httpjson - 0.100.0 + 0.99.0 @@ -54,6 +54,25 @@ test 4.13.2 + + + + org.junit.jupiter + junit-jupiter-api + 5.8.2 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.8.2 + test + @@ -65,6 +84,18 @@ pom 25.0.0 + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M6 + maven-plugin + + + **/*IT.java + + + @@ -88,4 +119,33 @@ 1.0-SNAPSHOT + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M6 + + + all + true + 10C + true + + **/*IT.java + + false + + + + org.apache.maven.plugins + maven-failsafe-plugin + 3.0.0-M6 + + true + + + + + diff --git a/compute/cloud-client/src/main/java/compute/CreateEncryptedInstance.java b/compute/cloud-client/src/main/java/compute/CreateEncryptedInstance.java index c1a36efa86b..8829834adbc 100644 --- a/compute/cloud-client/src/main/java/compute/CreateEncryptedInstance.java +++ b/compute/cloud-client/src/main/java/compute/CreateEncryptedInstance.java @@ -30,11 +30,13 @@ import com.google.cloud.compute.v1.Operation; import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class CreateEncryptedInstance { public static void main(String[] args) - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException, ExecutionException, TimeoutException { // TODO(developer): Replace these variables before running the sample. String project = "your-project-id"; String zone = "zone-name"; @@ -48,7 +50,7 @@ public static void main(String[] args) // in the specified project and zone. public static void createEncryptedInstance(String project, String zone, String instanceName, String diskEncryptionKey) - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException, ExecutionException, TimeoutException { /* Below are sample values that can be replaced. machineType: machine type of the VM being created. (This value uses the format zones/{zone}/machineTypes/{type_name}. @@ -111,7 +113,7 @@ public static void createEncryptedInstance(String project, String zone, String i instancesClient.insertAsync(insertInstanceRequest); // Wait for the operation to complete. - Operation response = operation.get(); + Operation response = operation.get(3, TimeUnit.MINUTES); if (response.hasError()) { System.out.println("Instance creation failed ! ! " + response); diff --git a/compute/cloud-client/src/main/java/compute/CreateFirewallRule.java b/compute/cloud-client/src/main/java/compute/CreateFirewallRule.java index dff25241c83..61a72e8ad10 100644 --- a/compute/cloud-client/src/main/java/compute/CreateFirewallRule.java +++ b/compute/cloud-client/src/main/java/compute/CreateFirewallRule.java @@ -26,11 +26,13 @@ import java.io.IOException; import java.util.UUID; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class CreateFirewallRule { public static void main(String[] args) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { // TODO(developer): Replace these variables before running the sample /* project: project ID or project number of the Cloud project you want to use. firewallRuleName: name of the rule that is created. @@ -49,7 +51,7 @@ public static void main(String[] args) // Creates a simple firewall rule allowing for incoming HTTP and // HTTPS access from the entire Internet. public static void createFirewall(String project, String firewallRuleName, String network) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { /* Initialize client that will be used to send requests. This client only needs to be created once, and can be reused for multiple requests. After completing all of your requests, call the `firewallsClient.close()` method on the client to safely @@ -78,7 +80,7 @@ public static void createFirewall(String project, String firewallRuleName, Strin .setFirewallResource(firewallRule) .setProject(project).build(); - firewallsClient.insertAsync(insertFirewallRequest).get(); + firewallsClient.insertAsync(insertFirewallRequest).get(3, TimeUnit.MINUTES); System.out.println("Firewall rule created successfully -> " + firewallRuleName); } diff --git a/compute/cloud-client/src/main/java/compute/CreateInstance.java b/compute/cloud-client/src/main/java/compute/CreateInstance.java index 15ff1196a73..6c012330f1f 100644 --- a/compute/cloud-client/src/main/java/compute/CreateInstance.java +++ b/compute/cloud-client/src/main/java/compute/CreateInstance.java @@ -29,11 +29,13 @@ import com.google.cloud.compute.v1.Operation; import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class CreateInstance { public static void main(String[] args) - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException, ExecutionException, TimeoutException { // TODO(developer): Replace these variables before running the sample. String project = "your-project-id"; String zone = "zone-name"; @@ -44,15 +46,18 @@ public static void main(String[] args) // Create a new instance with the provided "instanceName" value in the specified project and zone. public static void createInstance(String project, String zone, String instanceName) - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException, ExecutionException, TimeoutException { // Below are sample values that can be replaced. - // machineType: machine type of the VM being created. This value uses the format zones/{zone}/machineTypes/{type_name}. For a list of machine types, see https://cloud.google.com/compute/docs/machine-types - // sourceImage: path to the operating system image to mount. For details about images you can mount, see https://cloud.google.com/compute/docs/images + // machineType: machine type of the VM being created. + // * This value uses the format zones/{zone}/machineTypes/{type_name}. + // * For a list of machine types, see https://cloud.google.com/compute/docs/machine-types + // sourceImage: path to the operating system image to mount. + // * For details about images you can mount, see https://cloud.google.com/compute/docs/images // diskSizeGb: storage size of the boot disk to attach to the instance. // networkName: network interface to associate with the instance. String machineType = String.format("zones/%s/machineTypes/n1-standard-1", zone); String sourceImage = String - .format("projects/debian-cloud/global/images/family/%s", "debian-10"); + .format("projects/debian-cloud/global/images/family/%s", "debian-11"); long diskSizeGb = 10L; String networkName = "default"; @@ -69,12 +74,15 @@ public static void createInstance(String project, String zone, String instanceNa .setType(Type.PERSISTENT.toString()) .setDeviceName("disk-1") .setInitializeParams( - AttachedDiskInitializeParams.newBuilder().setSourceImage(sourceImage) - .setDiskSizeGb(diskSizeGb).build()) + AttachedDiskInitializeParams.newBuilder() + .setSourceImage(sourceImage) + .setDiskSizeGb(diskSizeGb) + .build()) .build(); // Use the network interface provided in the networkName argument. - NetworkInterface networkInterface = NetworkInterface.newBuilder().setName(networkName) + NetworkInterface networkInterface = NetworkInterface.newBuilder() + .setName(networkName) .build(); // Bind `instanceName`, `machineType`, `disk`, and `networkInterface` to an instance. @@ -92,13 +100,14 @@ public static void createInstance(String project, String zone, String instanceNa InsertInstanceRequest insertInstanceRequest = InsertInstanceRequest.newBuilder() .setProject(project) .setZone(zone) - .setInstanceResource(instanceResource).build(); + .setInstanceResource(instanceResource) + .build(); OperationFuture operation = instancesClient.insertAsync( insertInstanceRequest); // Wait for the operation to complete. - Operation response = operation.get(); + Operation response = operation.get(3, TimeUnit.MINUTES); if (response.hasError()) { System.out.println("Instance creation failed ! ! " + response); diff --git a/compute/cloud-client/src/main/java/compute/CreateInstanceFromTemplate.java b/compute/cloud-client/src/main/java/compute/CreateInstanceFromTemplate.java index 6bb7e5a216d..3d8e2bea792 100644 --- a/compute/cloud-client/src/main/java/compute/CreateInstanceFromTemplate.java +++ b/compute/cloud-client/src/main/java/compute/CreateInstanceFromTemplate.java @@ -25,11 +25,13 @@ import com.google.cloud.compute.v1.Operation; import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class CreateInstanceFromTemplate { public static void main(String[] args) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { /* TODO(developer): Replace these variables before running the sample. projectId - ID or number of the project you want to use. zone - Name of the zone you want to check, for example: us-west3-b @@ -51,7 +53,7 @@ public static void main(String[] args) // Create a new instance from template in the specified project and zone. public static void createInstanceFromTemplate(String projectId, String zone, String instanceName, String instanceTemplateUrl) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { try (InstancesClient instancesClient = InstancesClient.create()) { @@ -61,7 +63,8 @@ public static void createInstanceFromTemplate(String projectId, String zone, Str .setInstanceResource(Instance.newBuilder().setName(instanceName).build()) .setSourceInstanceTemplate(instanceTemplateUrl).build(); - Operation response = instancesClient.insertAsync(insertInstanceRequest).get(); + Operation response = instancesClient.insertAsync(insertInstanceRequest) + .get(3, TimeUnit.MINUTES); if (response.hasError()) { System.out.println("Instance creation from template failed ! ! " + response); diff --git a/compute/cloud-client/src/main/java/compute/CreateInstanceFromTemplateWithOverrides.java b/compute/cloud-client/src/main/java/compute/CreateInstanceFromTemplateWithOverrides.java index 27bc0f92df4..3ff0e4530cb 100644 --- a/compute/cloud-client/src/main/java/compute/CreateInstanceFromTemplateWithOverrides.java +++ b/compute/cloud-client/src/main/java/compute/CreateInstanceFromTemplateWithOverrides.java @@ -28,11 +28,13 @@ import com.google.cloud.compute.v1.Operation; import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class CreateInstanceFromTemplateWithOverrides { public static void main(String[] args) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { /* TODO(developer): Replace these variables before running the sample. * projectId - ID or number of the project you want to use. * zone - Name of the zone you want to check, for example: us-west3-b @@ -62,7 +64,7 @@ public static void main(String[] args) // but overrides the disk and machine type options in the template. public static void createInstanceFromTemplateWithOverrides(String projectId, String zone, String instanceName, String instanceTemplateName) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { try (InstancesClient instancesClient = InstancesClient.create(); InstanceTemplatesClient instanceTemplatesClient = InstanceTemplatesClient.create()) { @@ -100,7 +102,8 @@ public static void createInstanceFromTemplateWithOverrides(String projectId, Str .setInstanceResource(instance) .setSourceInstanceTemplate(instanceTemplate.getSelfLink()).build(); - Operation response = instancesClient.insertAsync(insertInstanceRequest).get(); + Operation response = instancesClient.insertAsync(insertInstanceRequest) + .get(3, TimeUnit.MINUTES); if (response.hasError()) { System.out.println("Instance creation from template with overrides failed ! ! " + response); diff --git a/compute/cloud-client/src/main/java/compute/CreateInstanceTemplate.java b/compute/cloud-client/src/main/java/compute/CreateInstanceTemplate.java index 9bc0db60222..f0394d13bc3 100644 --- a/compute/cloud-client/src/main/java/compute/CreateInstanceTemplate.java +++ b/compute/cloud-client/src/main/java/compute/CreateInstanceTemplate.java @@ -16,6 +16,7 @@ package compute; +// [START compute_template_create] import com.google.cloud.compute.v1.AccessConfig; import com.google.cloud.compute.v1.AccessConfig.NetworkTier; import com.google.cloud.compute.v1.AttachedDisk; @@ -29,11 +30,13 @@ import com.google.cloud.compute.v1.Operation; import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class CreateInstanceTemplate { public static void main(String[] args) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { // TODO(developer): Replace these variables before running the sample. // projectId: project ID or project number of the Cloud project you use. // templateName: name of the new template to create. @@ -47,7 +50,7 @@ public static void main(String[] args) instance configuration. */ public static void createInstanceTemplate(String projectId, String templateName) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { try (InstanceTemplatesClient instanceTemplatesClient = InstanceTemplatesClient.create()) { String machineType = "e2-standard-4"; @@ -85,7 +88,8 @@ public static void createInstanceTemplate(String projectId, String templateName) .setProperties(instanceProperties).build()).build(); // Create the Instance Template. - Operation response = instanceTemplatesClient.insertAsync(insertInstanceTemplateRequest).get(); + Operation response = instanceTemplatesClient.insertAsync(insertInstanceTemplateRequest) + .get(3, TimeUnit.MINUTES); if (response.hasError()) { System.out.println("Instance Template creation failed ! ! " + response); @@ -97,7 +101,7 @@ public static void createInstanceTemplate(String projectId, String templateName) } public static void createInstanceTemplateWithDiskType(String projectId, String templateName) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { try (InstanceTemplatesClient instanceTemplatesClient = InstanceTemplatesClient.create(); GlobalOperationsClient globalOperationsClient = GlobalOperationsClient.create()) { @@ -122,7 +126,8 @@ public static void createInstanceTemplateWithDiskType(String projectId, String t .setProject(projectId) .setInstanceTemplateResource(instanceTemplate).build(); - Operation response = instanceTemplatesClient.insertAsync(insertInstanceTemplateRequest).get(); + Operation response = instanceTemplatesClient.insertAsync(insertInstanceTemplateRequest) + .get(3, TimeUnit.MINUTES); if (response.hasError()) { System.out.println("Instance Template creation failed ! ! " + response); @@ -132,4 +137,5 @@ public static void createInstanceTemplateWithDiskType(String projectId, String t .printf("Instance Template Operation Status %s: %s", templateName, response.getStatus()); } } -} \ No newline at end of file +} +// [END compute_template_create] \ No newline at end of file diff --git a/compute/cloud-client/src/main/java/compute/CreateInstancesAdvanced.java b/compute/cloud-client/src/main/java/compute/CreateInstancesAdvanced.java index 1c6c2217ded..dad7c6f6372 100644 --- a/compute/cloud-client/src/main/java/compute/CreateInstancesAdvanced.java +++ b/compute/cloud-client/src/main/java/compute/CreateInstancesAdvanced.java @@ -37,6 +37,8 @@ import java.io.IOException; import java.util.Vector; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class CreateInstancesAdvanced { // [END compute_instances_create_from_image] @@ -195,7 +197,7 @@ private static AttachedDisk diskFromSnapshot(String diskType, int diskSizeGb, bo */ private static Instance createWithDisks(String project, String zone, String instanceName, Vector disks, String machineType, String network, String subnetwork) - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException, ExecutionException, TimeoutException { try (InstancesClient instancesClient = InstancesClient.create()) { // Use the network interface provided in the networkName argument. NetworkInterface networkInterface; @@ -231,7 +233,7 @@ private static Instance createWithDisks(String project, String zone, String inst insertInstanceRequest); // Wait for the operation to complete. - Operation response = operation.get(); + Operation response = operation.get(3, TimeUnit.MINUTES); if (response.hasError()) { System.out.println("Instance creation failed ! ! " + response); @@ -260,7 +262,7 @@ private static Instance createWithDisks(String project, String zone, String inst * @return Instance object. */ public static Instance createFromPublicImage(String project, String zone, String instanceName) - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException, ExecutionException, TimeoutException { try (ImagesClient imagesClient = ImagesClient.create()) { // List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details Image image = imagesClient.getFromFamily("debian-cloud", "debian-10"); @@ -287,7 +289,7 @@ public static Instance createFromPublicImage(String project, String zone, String */ public static Instance createFromCustomImage(String project, String zone, String instanceName, String customImage) - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException, ExecutionException, TimeoutException { String diskType = String.format("zones/%s/diskTypes/pd-standard", zone); Vector disks = new Vector<>(); disks.add(diskFromImage(diskType, 10, true, customImage)); @@ -307,7 +309,7 @@ public static Instance createFromCustomImage(String project, String zone, String * @return Instance object. */ public static Instance createWithAdditionalDisk(String project, String zone, String instanceName) - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException, ExecutionException, TimeoutException { try (ImagesClient imagesClient = ImagesClient.create()) { // List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details Image image = imagesClient.getFromFamily("debian-cloud", "debian-10"); @@ -335,7 +337,7 @@ public static Instance createWithAdditionalDisk(String project, String zone, Str */ public static Instance createFromSnapshot(String project, String zone, String instanceName, String snapshotName) - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException, ExecutionException, TimeoutException { String diskType = String.format("zones/%s/diskTypes/pd-standard", zone); Vector disks = new Vector<>(); disks.add(diskFromSnapshot(diskType, 11, true, snapshotName)); @@ -358,7 +360,7 @@ public static Instance createFromSnapshot(String project, String zone, String in */ public static Instance createWithSnapshottedDataDisk(String project, String zone, String instanceName, String snapshotName) - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException, ExecutionException, TimeoutException { try (ImagesClient imagesClient = ImagesClient.create()) { // List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details Image image = imagesClient.getFromFamily("debian-cloud", "debian-10"); @@ -389,7 +391,7 @@ public static Instance createWithSnapshottedDataDisk(String project, String zone */ public static Instance createWithSubnetwork(String project, String zone, String instanceName, String networkLink, String subnetworkLink) - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException, ExecutionException, TimeoutException { try (ImagesClient imagesClient = ImagesClient.create()) { // List of public operating system (OS) images: https://cloud.google.com/compute/docs/images/os-details Image image = imagesClient.getFromFamily("debian-cloud", "debian-10"); diff --git a/compute/cloud-client/src/main/java/compute/CreateTemplateFromInstance.java b/compute/cloud-client/src/main/java/compute/CreateTemplateFromInstance.java index 1e766647931..1d9595972d4 100644 --- a/compute/cloud-client/src/main/java/compute/CreateTemplateFromInstance.java +++ b/compute/cloud-client/src/main/java/compute/CreateTemplateFromInstance.java @@ -28,11 +28,13 @@ import com.google.cloud.compute.v1.SourceInstanceParams; import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class CreateTemplateFromInstance { public static void main(String[] args) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { // TODO(developer): Replace these variables before running the sample. // projectId: project ID or project number of the Cloud project you use. // instance: the instance to base the new template on. This value uses the following format: @@ -49,7 +51,7 @@ public static void main(String[] args) // This new template specifies a different boot disk. public static void createTemplateFromInstance(String projectId, String templateName, String instance) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { try (InstanceTemplatesClient instanceTemplatesClient = InstanceTemplatesClient.create(); GlobalOperationsClient globalOperationsClient = GlobalOperationsClient.create()) { @@ -81,7 +83,7 @@ public static void createTemplateFromInstance(String projectId, String templateN .build(); Operation operation = instanceTemplatesClient.insertCallable() - .futureCall(insertInstanceTemplateRequest).get(); + .futureCall(insertInstanceTemplateRequest).get(3, TimeUnit.MINUTES); Operation response = globalOperationsClient.wait(projectId, operation.getName()); diff --git a/compute/cloud-client/src/main/java/compute/CreateTemplateWithSubnet.java b/compute/cloud-client/src/main/java/compute/CreateTemplateWithSubnet.java index f92ae6f65cd..602db9d2d57 100644 --- a/compute/cloud-client/src/main/java/compute/CreateTemplateWithSubnet.java +++ b/compute/cloud-client/src/main/java/compute/CreateTemplateWithSubnet.java @@ -29,11 +29,13 @@ import com.google.cloud.compute.v1.Operation; import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class CreateTemplateWithSubnet { public static void main(String[] args) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { /* TODO(developer): Replace these variables before running the sample. projectId: project ID or project number of the Cloud project you use. @@ -54,7 +56,7 @@ public static void main(String[] args) // Create an instance template that uses a provided subnet. public static void createTemplateWithSubnet(String projectId, String network, String subnetwork, String templateName) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { try (InstanceTemplatesClient instanceTemplatesClient = InstanceTemplatesClient.create(); GlobalOperationsClient globalOperationsClient = GlobalOperationsClient.create()) { @@ -87,7 +89,7 @@ public static void createTemplateWithSubnet(String projectId, String network, St .build(); Operation operation = instanceTemplatesClient.insertCallable() - .futureCall(insertInstanceTemplateRequest).get(); + .futureCall(insertInstanceTemplateRequest).get(3, TimeUnit.MINUTES); Operation response = globalOperationsClient.wait(projectId, operation.getName()); diff --git a/compute/cloud-client/src/main/java/compute/DeleteFirewallRule.java b/compute/cloud-client/src/main/java/compute/DeleteFirewallRule.java index aa755db0883..0985adc6c3a 100644 --- a/compute/cloud-client/src/main/java/compute/DeleteFirewallRule.java +++ b/compute/cloud-client/src/main/java/compute/DeleteFirewallRule.java @@ -24,11 +24,13 @@ import java.io.IOException; import java.util.UUID; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class DeleteFirewallRule { public static void main(String[] args) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { // TODO(developer): Replace these variables before running the sample // project: project ID or project number of the Cloud project you want to use. // firewallRuleName: name of the firewall rule you want to delete. @@ -40,7 +42,7 @@ public static void main(String[] args) // Deletes a firewall rule from the project. public static void deleteFirewallRule(String project, String firewallRuleName) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { /* Initialize client that will be used to send requests. This client only needs to be created once, and can be reused for multiple requests. After completing all of your requests, call the `firewallsClient.close()` method on the client to safely @@ -49,7 +51,7 @@ public static void deleteFirewallRule(String project, String firewallRuleName) OperationFuture operation = firewallsClient.deleteAsync(project, firewallRuleName); - operation.get(); + operation.get(3, TimeUnit.MINUTES); System.out.println("Deleted firewall rule -> " + firewallRuleName); } diff --git a/compute/cloud-client/src/main/java/compute/DeleteInstance.java b/compute/cloud-client/src/main/java/compute/DeleteInstance.java index f5279aa485b..f3533778ec7 100644 --- a/compute/cloud-client/src/main/java/compute/DeleteInstance.java +++ b/compute/cloud-client/src/main/java/compute/DeleteInstance.java @@ -24,11 +24,13 @@ import com.google.cloud.compute.v1.Operation; import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class DeleteInstance { public static void main(String[] args) - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException, ExecutionException, TimeoutException { // TODO(developer): Replace these variables before running the sample. String project = "your-project-id"; String zone = "zone-name"; @@ -39,7 +41,7 @@ public static void main(String[] args) // Delete the instance specified by `instanceName` // if it's present in the given project and zone. public static void deleteInstance(String project, String zone, String instanceName) - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException, ExecutionException, TimeoutException { // Initialize client that will be used to send requests. This client only needs to be created // once, and can be reused for multiple requests. After completing all of your requests, call // the `instancesClient.close()` method on the client to safely @@ -57,7 +59,7 @@ public static void deleteInstance(String project, String zone, String instanceNa OperationFuture operation = instancesClient.deleteAsync( deleteInstanceRequest); // Wait for the operation to complete. - Operation response = operation.get(); + Operation response = operation.get(3, TimeUnit.MINUTES); if (response.hasError()) { System.out.println("Instance deletion failed ! ! " + response); diff --git a/compute/cloud-client/src/main/java/compute/DeleteInstanceTemplate.java b/compute/cloud-client/src/main/java/compute/DeleteInstanceTemplate.java index 604c4f6d1f6..3c3fa07c024 100644 --- a/compute/cloud-client/src/main/java/compute/DeleteInstanceTemplate.java +++ b/compute/cloud-client/src/main/java/compute/DeleteInstanceTemplate.java @@ -16,16 +16,20 @@ package compute; +// [START compute_template_delete] + import com.google.cloud.compute.v1.DeleteInstanceTemplateRequest; import com.google.cloud.compute.v1.InstanceTemplatesClient; import com.google.cloud.compute.v1.Operation; import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class DeleteInstanceTemplate { public static void main(String[] args) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { // TODO(developer): Replace these variables before running the sample. // projectId: project ID or project number of the Cloud project you use. // templateName: name of the new template to create. @@ -36,7 +40,7 @@ public static void main(String[] args) // Delete an instance template. public static void deleteInstanceTemplate(String projectId, String templateName) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { try (InstanceTemplatesClient instanceTemplatesClient = InstanceTemplatesClient.create()) { DeleteInstanceTemplateRequest deleteInstanceTemplateRequest = DeleteInstanceTemplateRequest @@ -44,7 +48,8 @@ public static void deleteInstanceTemplate(String projectId, String templateName) .setProject(projectId) .setInstanceTemplate(templateName).build(); - Operation response = instanceTemplatesClient.deleteAsync(deleteInstanceTemplateRequest).get(); + Operation response = instanceTemplatesClient.deleteAsync(deleteInstanceTemplateRequest) + .get(3, TimeUnit.MINUTES); if (response.hasError()) { System.out.println("Instance template deletion failed ! ! " + response); @@ -54,4 +59,5 @@ public static void deleteInstanceTemplate(String projectId, String templateName) response.getStatus()); } } -} \ No newline at end of file +} +// [END compute_template_delete] \ No newline at end of file diff --git a/compute/cloud-client/src/main/java/compute/GetInstanceTemplate.java b/compute/cloud-client/src/main/java/compute/GetInstanceTemplate.java index d08f2752b38..f82476cfc2e 100644 --- a/compute/cloud-client/src/main/java/compute/GetInstanceTemplate.java +++ b/compute/cloud-client/src/main/java/compute/GetInstanceTemplate.java @@ -16,6 +16,8 @@ package compute; +// [START compute_template_get] + import com.google.cloud.compute.v1.GetInstanceTemplateRequest; import com.google.cloud.compute.v1.InstanceTemplate; import com.google.cloud.compute.v1.InstanceTemplatesClient; @@ -26,7 +28,7 @@ public class GetInstanceTemplate { public static void main(String[] args) throws IOException { // TODO(developer): Replace these variables before running the sample. // projectId: project ID or project number of the Cloud project you use. - // templateName: name of the new template to retrieve. + // templateName: name of the template to retrieve. String projectId = "your-project-id"; String templateName = "template-name"; getInstanceTemplate(projectId, templateName); @@ -46,4 +48,5 @@ public static void getInstanceTemplate(String projectId, String templateName) th System.out.println("Instance Template retrieved: " + instanceTemplate.getName()); } } -} \ No newline at end of file +} +// [END compute_template_get] diff --git a/compute/cloud-client/src/main/java/compute/ListInstanceTemplates.java b/compute/cloud-client/src/main/java/compute/ListInstanceTemplates.java index c029d8acd4a..917311ac98e 100644 --- a/compute/cloud-client/src/main/java/compute/ListInstanceTemplates.java +++ b/compute/cloud-client/src/main/java/compute/ListInstanceTemplates.java @@ -16,6 +16,8 @@ package compute; +// [START compute_template_list] + import com.google.cloud.compute.v1.InstanceTemplate; import com.google.cloud.compute.v1.InstanceTemplatesClient; import com.google.cloud.compute.v1.InstanceTemplatesClient.ListPagedResponse; @@ -42,4 +44,5 @@ public static ListPagedResponse listInstanceTemplates(String projectId) throws I return templates; } } -} \ No newline at end of file +} +// [END compute_template_list] \ No newline at end of file diff --git a/compute/cloud-client/src/main/java/compute/PatchFirewallRule.java b/compute/cloud-client/src/main/java/compute/PatchFirewallRule.java index d7e7f91f87a..5cbda7d58e4 100644 --- a/compute/cloud-client/src/main/java/compute/PatchFirewallRule.java +++ b/compute/cloud-client/src/main/java/compute/PatchFirewallRule.java @@ -26,11 +26,13 @@ import java.io.IOException; import java.util.UUID; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class PatchFirewallRule { public static void main(String[] args) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { // TODO(developer): Replace these variables before running the sample // project: project ID or project number of the Cloud project you want to use. // firewallRuleName: name of the rule you want to modify. @@ -44,7 +46,7 @@ public static void main(String[] args) // Modifies the priority of a given firewall rule. public static void patchFirewallPriority(String project, String firewallRuleName, int priority) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { /* Initialize client that will be used to send requests. This client only needs to be created once, and can be reused for multiple requests. After completing all of your requests, call the `firewallsClient.close()` method on the client to safely @@ -63,7 +65,7 @@ public static void patchFirewallPriority(String project, String firewallRuleName OperationFuture operation = firewallsClient.patchAsync( patchFirewallRequest); - operation.get(); + operation.get(3, TimeUnit.MINUTES); System.out.println("Firewall Patch applied successfully ! "); } } diff --git a/compute/cloud-client/src/main/java/compute/ResetInstance.java b/compute/cloud-client/src/main/java/compute/ResetInstance.java index 480f412f699..ed6381e5759 100644 --- a/compute/cloud-client/src/main/java/compute/ResetInstance.java +++ b/compute/cloud-client/src/main/java/compute/ResetInstance.java @@ -25,11 +25,13 @@ import com.google.cloud.compute.v1.ResetInstanceRequest; import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class ResetInstance { public static void main(String[] args) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { // TODO(developer): Replace these variables before running the sample. /* project: project ID or project number of the Cloud project your instance belongs to. zone: name of the zone your instance belongs to. @@ -44,7 +46,7 @@ public static void main(String[] args) // Resets a running Google Compute Engine instance (with unencrypted disks). public static void resetInstance(String project, String zone, String instanceName) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { /* Initialize client that will be used to send requests. This client only needs to be created once, and can be reused for multiple requests. After completing all of your requests, call the `instancesClient.close()` method on the client to safely @@ -59,7 +61,7 @@ public static void resetInstance(String project, String zone, String instanceNam OperationFuture operation = instancesClient.resetAsync( resetInstanceRequest); - Operation response = operation.get(); + Operation response = operation.get(3, TimeUnit.MINUTES); if (response.getStatus() == Status.DONE) { System.out.println("Instance reset successfully ! "); diff --git a/compute/cloud-client/src/main/java/compute/ResumeInstance.java b/compute/cloud-client/src/main/java/compute/ResumeInstance.java new file mode 100644 index 00000000000..314e7e0c12f --- /dev/null +++ b/compute/cloud-client/src/main/java/compute/ResumeInstance.java @@ -0,0 +1,75 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package compute; + +// [START compute_resume_instance] + +import com.google.cloud.compute.v1.Instance.Status; +import com.google.cloud.compute.v1.InstancesClient; +import com.google.cloud.compute.v1.Operation; +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class ResumeInstance { + + public static void main(String[] args) + throws IOException, ExecutionException, InterruptedException, TimeoutException { + // TODO(developer): Replace these variables before running the sample. + // project: project ID or project number of the Cloud project your instance belongs to. + // zone: name of the zone your instance belongs to. + // instanceName: name of the instance your want to resume. + + String project = "your-project-id"; + String zone = "zone-name"; + String instanceName = "instance-name"; + + resumeInstance(project, zone, instanceName); + } + + // Resume a suspended Google Compute Engine instance (with unencrypted disks). + // Instance state changes to RUNNING, if successfully resumed. + public static void resumeInstance(String project, String zone, String instanceName) + throws IOException, ExecutionException, InterruptedException, TimeoutException { + // Instantiates a client. + try (InstancesClient instancesClient = InstancesClient.create()) { + + String currentInstanceState = instancesClient.get(project, zone, instanceName).getStatus(); + + // Check if the instance is currently suspended. + if (!currentInstanceState.equalsIgnoreCase(Status.SUSPENDED.toString())) { + throw new RuntimeException( + String.format("Only suspended instances can be resumed. Instance %s is in %s state.", + instanceName, currentInstanceState)); + } + + Operation operation = instancesClient.resumeAsync(project, zone, instanceName) + .get(300, TimeUnit.SECONDS); + + if (operation.hasError() || !instancesClient.get(project, zone, instanceName).getStatus() + .equalsIgnoreCase( + Status.RUNNING.toString())) { + System.out.println("Cannot resume instance. Try again!"); + return; + } + + System.out.printf("Instance resumed successfully ! %s", instanceName); + } + } +} +// [END compute_resume_instance] \ No newline at end of file diff --git a/compute/cloud-client/src/main/java/compute/SetUsageExportBucket.java b/compute/cloud-client/src/main/java/compute/SetUsageExportBucket.java index faf7a3bf9b7..4ffc676aac2 100644 --- a/compute/cloud-client/src/main/java/compute/SetUsageExportBucket.java +++ b/compute/cloud-client/src/main/java/compute/SetUsageExportBucket.java @@ -32,6 +32,7 @@ import java.io.IOException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; // [END compute_usage_report_disable] // [END compute_usage_report_get] @@ -40,7 +41,7 @@ public class SetUsageExportBucket { public static void main(String[] args) - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException, ExecutionException, TimeoutException { // TODO(developer): Replace these variables before running the sample. // TODO(developer): Create a Google Cloud Storage bucket. // bucketName: Cloud Storage Bucket used to store Compute Engine usage reports. @@ -59,7 +60,7 @@ public static void main(String[] args) // This sample presents how to interpret the default value for the report name prefix parameter. public static void setUsageExportBucket(String project, String bucketName, String reportNamePrefix) - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException, ExecutionException, TimeoutException { // bucketName: Cloud Storage Bucket used to store Compute Engine usage reports. // An existing Google Cloud Storage bucket is required. @@ -89,7 +90,7 @@ public static void setUsageExportBucket(String project, String bucketName, .build()); // Wait for the operation to complete. - Operation response = operation.get(); + Operation response = operation.get(3, TimeUnit.MINUTES); if (response.hasError()) { System.out.println("Setting usage export bucket failed ! ! " + response); @@ -143,7 +144,7 @@ public static UsageExportLocation getUsageExportBucket(String project) throws IO // Disable Compute Engine usage export bucket for the Cloud project. public static boolean disableUsageExportBucket(String project) - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException, ExecutionException, TimeoutException { try (ProjectsClient projectsClient = ProjectsClient.create()) { @@ -158,7 +159,8 @@ public static boolean disableUsageExportBucket(String project) .build()); // Wait for the operation to complete. - Operation response = operation.get(); + Operation response = operation.get(3, TimeUnit.MINUTES); + ; if (response.hasError()) { System.out.println("Disable usage export bucket failed ! ! " + response); @@ -166,7 +168,7 @@ public static boolean disableUsageExportBucket(String project) } // Wait for the settings to be effected. - TimeUnit.SECONDS.sleep(5); + TimeUnit.SECONDS.sleep(15); // Return false if the usage reports is disabled. return projectsClient.get(project).getUsageExportLocation().hasBucketName(); } diff --git a/compute/cloud-client/src/main/java/compute/StartEncryptedInstance.java b/compute/cloud-client/src/main/java/compute/StartEncryptedInstance.java index 7dc0eb70102..9e5b2b62704 100644 --- a/compute/cloud-client/src/main/java/compute/StartEncryptedInstance.java +++ b/compute/cloud-client/src/main/java/compute/StartEncryptedInstance.java @@ -30,11 +30,13 @@ import com.google.cloud.compute.v1.StartWithEncryptionKeyInstanceRequest; import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class StartEncryptedInstance { public static void main(String[] args) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { // TODO(developer): Replace these variables before running the sample. /* project: project ID or project number of the Cloud project your instance belongs to. zone: name of the zone your instance belongs to. @@ -54,7 +56,7 @@ public static void main(String[] args) // Starts a stopped Google Compute Engine instance (with encrypted disks). public static void startEncryptedInstance(String project, String zone, String instanceName, String key) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { /* Initialize client that will be used to send requests. This client only needs to be created once, and can be reused for multiple requests. After completing all of your requests, call the `instancesClient.close()` method on the client to safely @@ -94,7 +96,7 @@ public static void startEncryptedInstance(String project, String zone, String in OperationFuture operation = instancesClient.startWithEncryptionKeyAsync( encryptionKeyInstanceRequest); - Operation response = operation.get(); + Operation response = operation.get(3, TimeUnit.MINUTES); if (response.getStatus() == Status.DONE) { System.out.println("Encrypted instance started successfully ! "); diff --git a/compute/cloud-client/src/main/java/compute/StartInstance.java b/compute/cloud-client/src/main/java/compute/StartInstance.java index 4bcf20de89e..3c8ab90bb7e 100644 --- a/compute/cloud-client/src/main/java/compute/StartInstance.java +++ b/compute/cloud-client/src/main/java/compute/StartInstance.java @@ -25,11 +25,13 @@ import com.google.cloud.compute.v1.StartInstanceRequest; import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class StartInstance { public static void main(String[] args) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { // TODO(developer): Replace these variables before running the sample. /* project: project ID or project number of the Cloud project your instance belongs to. zone: name of the zone your instance belongs to. @@ -43,7 +45,7 @@ public static void main(String[] args) // Starts a stopped Google Compute Engine instance (with unencrypted disks). public static void startInstance(String project, String zone, String instanceName) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { /* Initialize client that will be used to send requests. This client only needs to be created once, and can be reused for multiple requests. After completing all of your requests, call the `instancesClient.close()` method on the client to safely @@ -61,7 +63,7 @@ public static void startInstance(String project, String zone, String instanceNam startInstanceRequest); // Wait for the operation to complete. - Operation response = operation.get(); + Operation response = operation.get(3, TimeUnit.MINUTES); if (response.getStatus() == Status.DONE) { System.out.println("Instance started successfully ! "); diff --git a/compute/cloud-client/src/main/java/compute/StopInstance.java b/compute/cloud-client/src/main/java/compute/StopInstance.java index 7fd341b5ee5..ce22aa11458 100644 --- a/compute/cloud-client/src/main/java/compute/StopInstance.java +++ b/compute/cloud-client/src/main/java/compute/StopInstance.java @@ -25,11 +25,13 @@ import com.google.cloud.compute.v1.StopInstanceRequest; import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class StopInstance { public static void main(String[] args) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { // TODO(developer): Replace these variables before running the sample. /* project: project ID or project number of the Cloud project your instance belongs to. zone: name of the zone your instance belongs to. @@ -44,7 +46,7 @@ public static void main(String[] args) // Stops a started Google Compute Engine instance. public static void stopInstance(String project, String zone, String instanceName) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { /* Initialize client that will be used to send requests. This client only needs to be created once, and can be reused for multiple requests. After completing all of your requests, call the `instancesClient.close()` method on the client to safely @@ -59,7 +61,7 @@ public static void stopInstance(String project, String zone, String instanceName OperationFuture operation = instancesClient.stopAsync( stopInstanceRequest); - Operation response = operation.get(); + Operation response = operation.get(3, TimeUnit.MINUTES); if (response.getStatus() == Status.DONE) { System.out.println("Instance stopped successfully ! "); diff --git a/compute/cloud-client/src/main/java/compute/SuspendInstance.java b/compute/cloud-client/src/main/java/compute/SuspendInstance.java new file mode 100644 index 00000000000..458b83680bd --- /dev/null +++ b/compute/cloud-client/src/main/java/compute/SuspendInstance.java @@ -0,0 +1,66 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package compute; + +// [START compute_suspend_instance] + +import com.google.cloud.compute.v1.Instance.Status; +import com.google.cloud.compute.v1.InstancesClient; +import com.google.cloud.compute.v1.Operation; +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class SuspendInstance { + + public static void main(String[] args) + throws IOException, ExecutionException, InterruptedException, TimeoutException { + // TODO(developer): Replace these variables before running the sample. + // project: project ID or project number of the Cloud project your instance belongs to. + // zone: name of the zone your instance belongs to. + // instanceName: name of the instance your want to suspend. + + String project = "your-project-id"; + String zone = "zone-name"; + String instanceName = "instance-name"; + + suspendInstance(project, zone, instanceName); + } + + // Suspend a running Google Compute Engine instance. + // For limitations and compatibility on which instances can be suspended, + // see: https://cloud.google.com/compute/docs/instances/suspend-resume-instance#limitations + public static void suspendInstance(String project, String zone, String instanceName) + throws IOException, ExecutionException, InterruptedException, TimeoutException { + // Instantiates a client. + try (InstancesClient instancesClient = InstancesClient.create()) { + + Operation operation = instancesClient.suspendAsync(project, zone, instanceName) + .get(300, TimeUnit.SECONDS); + + if (operation.hasError() || !instancesClient.get(project, zone, instanceName).getStatus() + .equalsIgnoreCase(Status.SUSPENDED.toString())) { + System.out.println("Cannot suspend instance. Try again!"); + return; + } + + System.out.printf("Instance suspended successfully ! %s", instanceName); + } + } +} +// [END compute_suspend_instance] \ No newline at end of file diff --git a/compute/cloud-client/src/main/java/compute/customhostname/CreateInstanceWithCustomHostname.java b/compute/cloud-client/src/main/java/compute/customhostname/CreateInstanceWithCustomHostname.java index 03238336edc..03f07491a37 100644 --- a/compute/cloud-client/src/main/java/compute/customhostname/CreateInstanceWithCustomHostname.java +++ b/compute/cloud-client/src/main/java/compute/customhostname/CreateInstanceWithCustomHostname.java @@ -27,11 +27,13 @@ import com.google.cloud.compute.v1.Operation; import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class CreateInstanceWithCustomHostname { public static void main(String[] args) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { // TODO(developer): Replace these variables before running the sample. // hostName: Custom hostname of the new VM instance. // * Custom hostnames must conform to RFC 1035 requirements for valid hostnames. @@ -45,7 +47,7 @@ public static void main(String[] args) // Creates an instance with custom hostname. public static void createInstanceWithCustomHostname(String projectId, String zone, String instanceName, String hostName) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { // machineType - Machine type for the VM instance specified in the following format: // * "zones/{zone}/machineTypes/{type_name}". For example: // * "zones/europe-west3-c/machineTypes/f1-micro" @@ -101,7 +103,8 @@ public static void createInstanceWithCustomHostname(String projectId, String zon .setInstanceResource(instanceResource).build(); // Wait for the create operation to complete. - Operation response = instancesClient.insertAsync(request).get(); + Operation response = instancesClient.insertAsync(request).get(3, TimeUnit.MINUTES); + ; if (response.hasError()) { System.out.printf("Instance creation failed for instance: %s ; Response: %s ! ! ", diff --git a/compute/cloud-client/src/main/java/compute/deleteprotection/CreateInstanceDeleteProtection.java b/compute/cloud-client/src/main/java/compute/deleteprotection/CreateInstanceDeleteProtection.java index c8fafb7e5f1..71b880e2301 100644 --- a/compute/cloud-client/src/main/java/compute/deleteprotection/CreateInstanceDeleteProtection.java +++ b/compute/cloud-client/src/main/java/compute/deleteprotection/CreateInstanceDeleteProtection.java @@ -27,11 +27,13 @@ import com.google.cloud.compute.v1.Operation; import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class CreateInstanceDeleteProtection { public static void main(String[] args) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { // TODO(developer): Replace these variables before running the sample. // project: project ID or project number of the Cloud project you want to use. // zone: name of the zone you want to use. For example: “us-west3-b” @@ -48,7 +50,7 @@ public static void main(String[] args) // Send an instance creation request to the Compute Engine API and wait for it to complete. public static void createInstanceDeleteProtection(String projectId, String zone, String instanceName, boolean deleteProtection) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { String machineType = String.format("zones/%s/machineTypes/e2-small", zone); String sourceImage = String @@ -98,7 +100,9 @@ public static void createInstanceDeleteProtection(String projectId, String zone, .build(); // Wait for the create operation to complete. - Operation response = instancesClient.insertAsync(insertInstanceRequest).get(); + Operation response = instancesClient.insertAsync(insertInstanceRequest) + .get(3, TimeUnit.MINUTES); + ; if (response.hasError()) { System.out.println("Instance creation failed ! ! " + response); diff --git a/compute/cloud-client/src/main/java/compute/deleteprotection/SetDeleteProtection.java b/compute/cloud-client/src/main/java/compute/deleteprotection/SetDeleteProtection.java index 13df47926a0..3720261d3a4 100644 --- a/compute/cloud-client/src/main/java/compute/deleteprotection/SetDeleteProtection.java +++ b/compute/cloud-client/src/main/java/compute/deleteprotection/SetDeleteProtection.java @@ -22,11 +22,13 @@ import com.google.cloud.compute.v1.SetDeletionProtectionInstanceRequest; import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class SetDeleteProtection { public static void main(String[] args) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { // TODO(developer): Replace these variables before running the sample. // project: project ID or project number of the Cloud project you want to use. // zone: name of the zone you want to use. For example: “us-west3-b” @@ -43,7 +45,7 @@ public static void main(String[] args) // Updates the "Delete Protection" setting of given instance. public static void setDeleteProtection(String projectId, String zone, String instanceName, boolean deleteProtection) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { try (InstancesClient instancesClient = InstancesClient.create()) { @@ -55,7 +57,8 @@ public static void setDeleteProtection(String projectId, String zone, .setDeletionProtection(deleteProtection) .build(); - instancesClient.setDeletionProtectionAsync(request).get(); + instancesClient.setDeletionProtectionAsync(request).get(3, TimeUnit.MINUTES); + ; // Retrieve the updated setting from the instance. System.out.printf("Updated Delete Protection setting: %s", instancesClient.get(projectId, zone, instanceName).getDeletionProtection()); diff --git a/compute/cloud-client/src/main/java/compute/preemptible/CreatePreemptibleInstance.java b/compute/cloud-client/src/main/java/compute/preemptible/CreatePreemptibleInstance.java index 05c2ce0c6c0..2965512eb4a 100644 --- a/compute/cloud-client/src/main/java/compute/preemptible/CreatePreemptibleInstance.java +++ b/compute/cloud-client/src/main/java/compute/preemptible/CreatePreemptibleInstance.java @@ -28,11 +28,13 @@ import com.google.cloud.compute.v1.Scheduling; import java.io.IOException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class CreatePreemptibleInstance { public static void main(String[] args) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { // TODO(developer): Replace these variables before running the sample. // projectId: project ID or project number of the Cloud project you want to use. // zone: name of the zone you want to use. For example: “us-west3-b” @@ -47,7 +49,7 @@ public static void main(String[] args) // Send an instance creation request with preemptible settings to the Compute Engine API // and wait for it to complete. public static void createPremptibleInstance(String projectId, String zone, String instanceName) - throws IOException, ExecutionException, InterruptedException { + throws IOException, ExecutionException, InterruptedException, TimeoutException { String machineType = String.format("zones/%s/machineTypes/e2-small", zone); String sourceImage = "projects/debian-cloud/global/images/family/debian-11"; @@ -97,7 +99,9 @@ public static void createPremptibleInstance(String projectId, String zone, Strin .build(); // Wait for the create operation to complete. - Operation response = instancesClient.insertAsync(insertInstanceRequest).get(); + Operation response = instancesClient.insertAsync(insertInstanceRequest) + .get(3, TimeUnit.MINUTES); + ; if (response.hasError()) { System.out.println("Instance creation failed ! ! " + response); diff --git a/compute/cloud-client/src/main/java/compute/windows/windowsinstances/CreateFirewallRuleForWindowsActivationHost.java b/compute/cloud-client/src/main/java/compute/windows/windowsinstances/CreateFirewallRuleForWindowsActivationHost.java new file mode 100644 index 00000000000..b741c47fa0b --- /dev/null +++ b/compute/cloud-client/src/main/java/compute/windows/windowsinstances/CreateFirewallRuleForWindowsActivationHost.java @@ -0,0 +1,88 @@ +// Copyright 2022 Google LLC +// +// 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. + +package compute.windows.windowsinstances; + +// [START compute_create_egress_rule_windows_activation] + +import com.google.cloud.compute.v1.Allowed; +import com.google.cloud.compute.v1.Firewall; +import com.google.cloud.compute.v1.FirewallsClient; +import com.google.cloud.compute.v1.InsertFirewallRequest; +import com.google.cloud.compute.v1.Operation; +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class CreateFirewallRuleForWindowsActivationHost { + + public static void main(String[] args) + throws IOException, ExecutionException, InterruptedException, TimeoutException { + // TODO(developer): Replace these variables before running the sample. + // projectId - ID or number of the project you want to use. + String projectId = "your-google-cloud-project-id"; + + // firewallRuleName - Name of the firewall rule you want to create. + String firewallRuleName = "firewall-rule-name"; + + // networkName - Name of the network you want the new instance to use. + // * For example: "global/networks/default" represents the network + // * named "default", which is created automatically for each project. + String networkName = "global/networks/default"; + + createFirewallRuleForWindowsActivationHost(projectId, firewallRuleName, networkName); + } + + // Creates a new allow egress firewall rule with the highest priority for host + // kms.windows.googlecloud.com (35.190.247.13) for Windows activation. + public static void createFirewallRuleForWindowsActivationHost(String projectId, + String firewallRuleName, String networkName) + throws IOException, ExecutionException, InterruptedException, TimeoutException { + // Instantiates a client. + try (FirewallsClient firewallsClient = FirewallsClient.create()) { + + Firewall firewall = Firewall.newBuilder() + .setName(firewallRuleName) + // These are the default values for kms.windows.googlecloud.com + // See, https://cloud.google.com/compute/docs/instances/windows/creating-managing-windows-instances#firewall_rule_requirements + .addAllowed(Allowed.newBuilder() + .setIPProtocol("tcp") + .addPorts("1688") + .build()) + .setDirection("EGRESS") + .setNetwork(networkName) + .addDestinationRanges("35.190.247.13/32") + .setPriority(0) + .build(); + + InsertFirewallRequest request = InsertFirewallRequest.newBuilder() + .setProject(projectId) + .setFirewallResource(firewall) + .build(); + + // Wait for the operation to complete. + Operation operation = firewallsClient.insertAsync(request).get(3, TimeUnit.MINUTES); + ; + + if (operation.hasError()) { + System.out.println("Firewall rule creation failed ! ! " + operation.getError()); + return; + } + + System.out.printf("Firewall rule created %s", firewallRuleName); + } + } +} +// [END compute_create_egress_rule_windows_activation] \ No newline at end of file diff --git a/compute/cloud-client/src/main/java/compute/windows/windowsinstances/CreateRouteToWindowsActivationHost.java b/compute/cloud-client/src/main/java/compute/windows/windowsinstances/CreateRouteToWindowsActivationHost.java new file mode 100644 index 00000000000..8c9cd06a57b --- /dev/null +++ b/compute/cloud-client/src/main/java/compute/windows/windowsinstances/CreateRouteToWindowsActivationHost.java @@ -0,0 +1,85 @@ +// Copyright 2022 Google LLC +// +// 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. + +package compute.windows.windowsinstances; + +// [START compute_create_route_windows_activation] + +import com.google.cloud.compute.v1.InsertRouteRequest; +import com.google.cloud.compute.v1.Operation; +import com.google.cloud.compute.v1.Route; +import com.google.cloud.compute.v1.RoutesClient; +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class CreateRouteToWindowsActivationHost { + + public static void main(String[] args) + throws IOException, ExecutionException, InterruptedException, TimeoutException { + // TODO(developer): Replace these variables before running the sample. + // projectId - ID or number of the project you want to use. + String projectId = "your-google-cloud-project-id"; + + // routeName - Name of the route you want to create. + String routeName = "route-name"; + + // networkName - Name of the network you want the new instance to use. + // * For example: "global/networks/default" represents the network + // * named "default", which is created automatically for each project. + String networkName = "global/networks/default"; + + createRouteToWindowsActivationHost(projectId, routeName, networkName); + } + + // Creates a new route to kms.windows.googlecloud.com (35.190.247.13) for Windows activation. + public static void createRouteToWindowsActivationHost(String projectId, String routeName, + String networkName) + throws IOException, ExecutionException, InterruptedException, TimeoutException { + // Instantiates a client. + try (RoutesClient routesClient = RoutesClient.create()) { + + // If you have Windows instances without external IP addresses, + // you must also enable Private Google Access so that instances + // with only internal IP addresses can send traffic to the external + // IP address for kms.windows.googlecloud.com. + // More information: https://cloud.google.com/vpc/docs/configure-private-google-access#enabling + Route route = Route.newBuilder() + .setName(routeName) + .setDestRange("35.190.247.13/32") + .setNetwork(networkName) + .setNextHopGateway( + String.format("projects/%s/global/gateways/default-internet-gateway", projectId)) + .build(); + + InsertRouteRequest request = InsertRouteRequest.newBuilder() + .setProject(projectId) + .setRouteResource(route) + .build(); + + // Wait for the operation to complete. + Operation operation = routesClient.insertAsync(request).get(3, TimeUnit.MINUTES); + ; + + if (operation.hasError()) { + System.out.printf("Error in creating route %s", operation.getError()); + return; + } + + System.out.printf("Route created %s", routeName); + } + } +} +// [END compute_create_route_windows_activation] \ No newline at end of file diff --git a/compute/cloud-client/src/main/java/compute/windows/windowsinstances/CreateWindowsServerInstanceExternalIp.java b/compute/cloud-client/src/main/java/compute/windows/windowsinstances/CreateWindowsServerInstanceExternalIp.java new file mode 100644 index 00000000000..84367a26b57 --- /dev/null +++ b/compute/cloud-client/src/main/java/compute/windows/windowsinstances/CreateWindowsServerInstanceExternalIp.java @@ -0,0 +1,122 @@ +// Copyright 2022 Google LLC +// +// 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. + +package compute.windows.windowsinstances; + +// [START compute_create_windows_instance_external_ip] + +import com.google.cloud.compute.v1.AccessConfig; +import com.google.cloud.compute.v1.AttachedDisk; +import com.google.cloud.compute.v1.AttachedDiskInitializeParams; +import com.google.cloud.compute.v1.InsertInstanceRequest; +import com.google.cloud.compute.v1.Instance; +import com.google.cloud.compute.v1.InstancesClient; +import com.google.cloud.compute.v1.NetworkInterface; +import com.google.cloud.compute.v1.Operation; +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class CreateWindowsServerInstanceExternalIp { + + public static void main(String[] args) + throws IOException, ExecutionException, InterruptedException, TimeoutException { + // TODO(developer): Replace these variables before running the sample. + // projectId - ID or number of the project you want to use. + String projectId = "your-google-cloud-project-id"; + + // zone - Name of the zone you want to use, for example: us-west3-b + String zone = "europe-central2-b"; + + // instanceName - Name of the new machine. + String instanceName = "instance-name"; + + createWindowsServerInstanceExternalIp(projectId, zone, instanceName); + } + + // Creates a new Windows Server instance that has an external IP address. + public static void createWindowsServerInstanceExternalIp(String projectId, String zone, + String instanceName) + throws IOException, ExecutionException, InterruptedException, TimeoutException { + + // machineType - Machine type you want to create in following format: + // * "zones/{zone}/machineTypes/{type_name}". For example: + // * "zones/europe-west3-c/machineTypes/f1-micro" + // * You can find the list of available machine types using: + // * https://cloud.google.com/sdk/gcloud/reference/compute/machine-types/list + String machineType = "n1-standard-1"; + // sourceImageFamily - Name of the public image family for Windows Server or SQL Server images. + // * https://cloud.google.com/compute/docs/images#os-compute-support + String sourceImageFamily = "windows-2012-r2"; + + // Instantiates a client. + try (InstancesClient instancesClient = InstancesClient.create()) { + + AttachedDisk attachedDisk = AttachedDisk.newBuilder() + // Describe the size and source image of the boot disk to attach to the instance. + .setInitializeParams(AttachedDiskInitializeParams.newBuilder() + .setDiskSizeGb(64) + .setSourceImage( + String.format("projects/windows-cloud/global/images/family/%s", + sourceImageFamily)) + .build()) + .setAutoDelete(true) + .setBoot(true) + .setType(AttachedDisk.Type.PERSISTENT.toString()) + .build(); + + Instance instance = Instance.newBuilder() + .setName(instanceName) + .setMachineType(String.format("zones/%s/machineTypes/%s", zone, machineType)) + .addDisks(attachedDisk) + .addNetworkInterfaces(NetworkInterface.newBuilder() + .addAccessConfigs(AccessConfig.newBuilder() + .setType("ONE_TO_ONE_NAT") + .setName("External NAT") + .build()) + // If you going you use custom VPC network, it must be configured + // to allow access to kms.windows.googlecloud.com. + // https://cloud.google.com/compute/docs/instances/windows/creating-managing-windows-instances#kms-server. + .setName("global/networks/default") + .build()) + // If you chose an image that supports Shielded VM, you can optionally change the + // instance's Shielded VM settings. + // .setShieldedInstanceConfig(ShieldedInstanceConfig.newBuilder() + // .setEnableSecureBoot(true) + // .setEnableVtpm(true) + // .setEnableIntegrityMonitoring(true) + // .build()) + .build(); + + InsertInstanceRequest request = InsertInstanceRequest.newBuilder() + .setProject(projectId) + .setZone(zone) + .setInstanceResource(instance) + .build(); + + // Wait for the operation to complete. + Operation operation = instancesClient.insertAsync(request).get(3, TimeUnit.MINUTES); + ; + + if (operation.hasError()) { + System.out.printf("Error in creating instance %s", operation.getError()); + return; + } + + System.out.printf("Instance created %s", instanceName); + } + } +} +// [END compute_create_windows_instance_external_ip] \ No newline at end of file diff --git a/compute/cloud-client/src/main/java/compute/windows/windowsinstances/CreateWindowsServerInstanceInternalIp.java b/compute/cloud-client/src/main/java/compute/windows/windowsinstances/CreateWindowsServerInstanceInternalIp.java new file mode 100644 index 00000000000..f54e0a2aa1b --- /dev/null +++ b/compute/cloud-client/src/main/java/compute/windows/windowsinstances/CreateWindowsServerInstanceInternalIp.java @@ -0,0 +1,133 @@ +// Copyright 2022 Google LLC +// +// 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. + +package compute.windows.windowsinstances; + +// [START compute_create_windows_instance_internal_ip] + +import com.google.cloud.compute.v1.AttachedDisk; +import com.google.cloud.compute.v1.AttachedDiskInitializeParams; +import com.google.cloud.compute.v1.InsertInstanceRequest; +import com.google.cloud.compute.v1.Instance; +import com.google.cloud.compute.v1.InstancesClient; +import com.google.cloud.compute.v1.NetworkInterface; +import com.google.cloud.compute.v1.Operation; +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class CreateWindowsServerInstanceInternalIp { + + public static void main(String[] args) + throws IOException, ExecutionException, InterruptedException, TimeoutException { + // TODO(developer): Replace these variables before running the sample. + // projectId - ID or number of the project you want to use. + String projectId = "your-google-cloud-project-id"; + + // zone - Name of the zone you want to use, for example: us-west3-b + String zone = "europe-central2-b"; + + // instanceName - Name of the new machine. + String instanceName = "instance-name"; + + // networkLink - Name of the network you want the new instance to use. + // * For example: "global/networks/default" represents the network + // * named "default", which is created automatically for each project. + String networkLink = "global/networks/default"; + + // subnetworkLink - Name of the subnetwork you want the new instance to use. + // * This value uses the following format: + // * "regions/{region}/subnetworks/{subnetwork_name}" + String subnetworkLink = "regions/europe-central2/subnetworks/default"; + + createWindowsServerInstanceInternalIp(projectId, zone, instanceName, networkLink, + subnetworkLink); + } + + // Creates a new Windows Server instance that has only an internal IP address. + public static void createWindowsServerInstanceInternalIp(String projectId, String zone, + String instanceName, String networkLink, String subnetworkLink) + throws IOException, ExecutionException, InterruptedException, TimeoutException { + + // machineType - Machine type you want to create in following format: + // * "zones/{zone}/machineTypes/{type_name}". For example: + // * "zones/europe-west3-c/machineTypes/f1-micro" + // * You can find the list of available machine types using: + // * https://cloud.google.com/sdk/gcloud/reference/compute/machine-types/list + String machineType = "n1-standard-1"; + // sourceImageFamily - Name of the public image family for Windows Server or SQL Server images. + // * https://cloud.google.com/compute/docs/images#os-compute-support + String sourceImageFamily = "windows-2012-r2"; + + // Instantiates a client. + try (InstancesClient instancesClient = InstancesClient.create()) { + + AttachedDisk attachedDisk = AttachedDisk.newBuilder() + // Describe the size and source image of the boot disk to attach to the instance. + .setInitializeParams(AttachedDiskInitializeParams.newBuilder() + .setDiskSizeGb(64) + .setSourceImage( + String.format("projects/windows-cloud/global/images/family/%s", + sourceImageFamily)) + .build()) + .setAutoDelete(true) + .setBoot(true) + .setType(AttachedDisk.Type.PERSISTENT.toString()) + .build(); + + Instance instance = Instance.newBuilder() + .setName(instanceName) + .setMachineType(String.format("zones/%s/machineTypes/%s", zone, machineType)) + .addDisks(attachedDisk) + .addNetworkInterfaces(NetworkInterface.newBuilder() + // You must verify or configure routes and firewall rules in your VPC network + // to allow access to kms.windows.googlecloud.com. + // More information about access to kms.windows.googlecloud.com: https://cloud.google.com/compute/docs/instances/windows/creating-managing-windows-instances#kms-server + + // Additionally, you must enable Private Google Access for subnets in your VPC network + // that contain Windows instances with only internal IP addresses. + // More information about Private Google Access: https://cloud.google.com/vpc/docs/configure-private-google-access#enabling + .setName(networkLink) + .setSubnetwork(subnetworkLink) + .build()) + // If you chose an image that supports Shielded VM, you can optionally change the + // instance's Shielded VM settings. + // .setShieldedInstanceConfig(ShieldedInstanceConfig.newBuilder() + // .setEnableSecureBoot(true) + // .setEnableVtpm(true) + // .setEnableIntegrityMonitoring(true) + // .build()) + .build(); + + InsertInstanceRequest request = InsertInstanceRequest.newBuilder() + .setProject(projectId) + .setZone(zone) + .setInstanceResource(instance) + .build(); + + // Wait for the operation to complete. + Operation operation = instancesClient.insertAsync(request).get(3, TimeUnit.MINUTES); + ; + + if (operation.hasError()) { + System.out.printf("Error in creating instance %s", operation.getError()); + return; + } + + System.out.printf("Instance created %s", instanceName); + } + } +} +// [END compute_create_windows_instance_internal_ip] \ No newline at end of file diff --git a/compute/cloud-client/src/main/java/compute/windows/windowsinstances/GetInstanceSerialPort.java b/compute/cloud-client/src/main/java/compute/windows/windowsinstances/GetInstanceSerialPort.java new file mode 100644 index 00000000000..6a60d644003 --- /dev/null +++ b/compute/cloud-client/src/main/java/compute/windows/windowsinstances/GetInstanceSerialPort.java @@ -0,0 +1,52 @@ +// Copyright 2022 Google LLC +// +// 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. + +package compute.windows.windowsinstances; + +// [START compute_get_instance_serial_port] + +import com.google.cloud.compute.v1.InstancesClient; +import com.google.cloud.compute.v1.SerialPortOutput; +import java.io.IOException; + +public class GetInstanceSerialPort { + + public static void main(String[] args) throws IOException { + // TODO(developer): Replace these variables before running the sample. + // projectId - ID or number of the project you want to use. + String projectId = "your-google-cloud-project-id"; + + // zone - Name of the zone you want to check, for example: us-west3-b + String zone = "europe-central2-b"; + + // instanceName - Name of the instance you want to check. + String instanceName = "instance-name"; + + getInstanceSerialPort(projectId, zone, instanceName); + } + + // Prints an instance serial port output. + public static void getInstanceSerialPort(String projectId, String zone, String instanceName) + throws IOException { + // Instantiates a client. + try (InstancesClient instancesClient = InstancesClient.create()) { + + SerialPortOutput serialPortOutput = instancesClient.getSerialPortOutput(projectId, zone, + instanceName); + + System.out.printf("Output from instance serial port %s", serialPortOutput.getContents()); + } + } +} +// [END compute_get_instance_serial_port] \ No newline at end of file diff --git a/compute/cloud-client/src/test/java/compute/FirewallIT.java b/compute/cloud-client/src/test/java/compute/FirewallIT.java new file mode 100644 index 00000000000..6a64961274d --- /dev/null +++ b/compute/cloud-client/src/test/java/compute/FirewallIT.java @@ -0,0 +1,164 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package compute; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import com.google.api.gax.rpc.InvalidArgumentException; +import com.google.api.gax.rpc.NotFoundException; +import com.google.cloud.compute.v1.FirewallsClient; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.Assert; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +@Timeout(value = 10, unit = TimeUnit.MINUTES) +public class FirewallIT { + + private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); + private static String FIREWALL_RULE_CREATE; + private static String NETWORK_NAME; + + private ByteArrayOutputStream stdOut; + + // Check if the required environment variables are set. + public static void requireEnvVar(String envVarName) { + assertWithMessage(String.format("Missing environment variable '%s' ", envVarName)) + .that(System.getenv(envVarName)).isNotEmpty(); + } + + @BeforeAll + public static void setUp() + throws IOException, InterruptedException, ExecutionException, TimeoutException { + final PrintStream out = System.out; + ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); + System.setOut(new PrintStream(stdOut)); + requireEnvVar("GOOGLE_APPLICATION_CREDENTIALS"); + requireEnvVar("GOOGLE_CLOUD_PROJECT"); + + FIREWALL_RULE_CREATE = "firewall-rule-" + UUID.randomUUID(); + NETWORK_NAME = "global/networks/default"; + + compute.CreateFirewallRule.createFirewall(PROJECT_ID, FIREWALL_RULE_CREATE, NETWORK_NAME); + TimeUnit.SECONDS.sleep(10); + + stdOut.close(); + System.setOut(out); + } + + + @AfterAll + public static void cleanup() + throws IOException, InterruptedException, ExecutionException, TimeoutException { + final PrintStream out = System.out; + ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); + System.setOut(new PrintStream(stdOut)); + // Delete all instances created for testing. + requireEnvVar("GOOGLE_APPLICATION_CREDENTIALS"); + requireEnvVar("GOOGLE_CLOUD_PROJECT"); + + if (!isFirewallRuleDeletedByGceEnforcer(PROJECT_ID, FIREWALL_RULE_CREATE)) { + DeleteFirewallRule.deleteFirewallRule(PROJECT_ID, FIREWALL_RULE_CREATE); + } + + stdOut.close(); + System.setOut(out); + } + + public static boolean isFirewallRuleDeletedByGceEnforcer( + String projectId, String firewallRule) + throws IOException { + /* (**INTERNAL method**) + This method will prevent test failure if the firewall rule was auto-deleted by GCE Enforcer. + (Feel free to remove this method if not running on a Google-owned project.) + */ + try { + GetFirewallRule.getFirewallRule(projectId, firewallRule); + } catch (NotFoundException e) { + System.out.println("Rule already deleted! "); + return true; + } catch (InvalidArgumentException | NullPointerException e) { + System.out.println("Rule is not ready (probably being deleted)."); + return true; + } + return false; + } + + @BeforeEach + public void beforeEach() { + stdOut = new ByteArrayOutputStream(); + System.setOut(new PrintStream(stdOut)); + } + + @AfterEach + public void afterEach() { + stdOut = null; + System.setOut(null); + } + + @Test + public void testListFirewallRules() + throws IOException, ExecutionException, InterruptedException { + final PrintStream out = System.out; + ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); + System.setOut(new PrintStream(stdOut)); + if (!isFirewallRuleDeletedByGceEnforcer(PROJECT_ID, FIREWALL_RULE_CREATE)) { + compute.ListFirewallRules.listFirewallRules(PROJECT_ID); + assertThat(stdOut.toString()).contains(FIREWALL_RULE_CREATE); + } + // Clear system output to not affect other tests. + // Refrain from setting out to null. + stdOut.close(); + System.setOut(out); + } + + @Test + public void testPatchFirewallRule() + throws IOException, InterruptedException, ExecutionException, TimeoutException { + final PrintStream out = System.out; + ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); + System.setOut(new PrintStream(stdOut)); + // Firewall rule is auto-deleted by GCE Enforcer within a few minutes. + if (!isFirewallRuleDeletedByGceEnforcer(PROJECT_ID, FIREWALL_RULE_CREATE)) { + try (FirewallsClient client = FirewallsClient.create()) { + Assert.assertEquals(1000, client.get(PROJECT_ID, FIREWALL_RULE_CREATE).getPriority()); + compute.PatchFirewallRule.patchFirewallPriority(PROJECT_ID, FIREWALL_RULE_CREATE, 500); + TimeUnit.SECONDS.sleep(5); + Assert.assertEquals(500, client.get(PROJECT_ID, FIREWALL_RULE_CREATE).getPriority()); + } + } + // Clear system output to not affect other tests. + // Refrain from setting out to null as it will throw NullPointer in the subsequent tests. + stdOut.close(); + System.setOut(out); + } + +} diff --git a/compute/cloud-client/src/test/java/compute/InstanceOperationsIT.java b/compute/cloud-client/src/test/java/compute/InstanceOperationsIT.java new file mode 100644 index 00000000000..eae0ac8c2ec --- /dev/null +++ b/compute/cloud-client/src/test/java/compute/InstanceOperationsIT.java @@ -0,0 +1,176 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package compute; + +import static com.google.common.truth.Truth.assertWithMessage; + +import com.google.cloud.compute.v1.Instance.Status; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.time.LocalDateTime; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.Assert; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +@Timeout(value = 10, unit = TimeUnit.MINUTES) +public class InstanceOperationsIT { + + private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); + private static String ZONE; + private static String MACHINE_NAME; + private static String MACHINE_NAME_ENCRYPTED; + private static String RAW_KEY; + + private ByteArrayOutputStream stdOut; + + // Check if the required environment variables are set. + public static void requireEnvVar(String envVarName) { + assertWithMessage(String.format("Missing environment variable '%s' ", envVarName)) + .that(System.getenv(envVarName)).isNotEmpty(); + } + + @BeforeAll + public static void setUp() + throws IOException, InterruptedException, ExecutionException, TimeoutException { + final PrintStream out = System.out; + ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); + System.setOut(new PrintStream(stdOut)); + requireEnvVar("GOOGLE_APPLICATION_CREDENTIALS"); + requireEnvVar("GOOGLE_CLOUD_PROJECT"); + + ZONE = "us-central1-a"; + MACHINE_NAME = "my-new-test-instance" + UUID.randomUUID(); + MACHINE_NAME_ENCRYPTED = "encrypted-test-instance" + UUID.randomUUID(); + RAW_KEY = Util.getBase64EncodedKey(); + + // Cleanup existing stale resources. + Util.cleanUpExistingInstances("my-new-test-instance", PROJECT_ID, ZONE); + Util.cleanUpExistingInstances("encrypted-test-instance", PROJECT_ID, ZONE); + + compute.CreateInstance.createInstance(PROJECT_ID, ZONE, MACHINE_NAME); + compute.CreateEncryptedInstance + .createEncryptedInstance(PROJECT_ID, ZONE, MACHINE_NAME_ENCRYPTED, RAW_KEY); + + TimeUnit.SECONDS.sleep(10); + + stdOut.close(); + System.setOut(out); + } + + + @AfterAll + public static void cleanup() + throws IOException, InterruptedException, ExecutionException, TimeoutException { + final PrintStream out = System.out; + ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); + System.setOut(new PrintStream(stdOut)); + + // Delete all instances created for testing. + compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME_ENCRYPTED); + compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME); + + stdOut.close(); + System.setOut(out); + } + + @BeforeEach + public void beforeEach() { + stdOut = new ByteArrayOutputStream(); + System.setOut(new PrintStream(stdOut)); + } + + @AfterEach + public void afterEach() { + stdOut = null; + System.setOut(null); + } + + @Test + public void testInstanceOperations() + throws IOException, ExecutionException, InterruptedException, TimeoutException { + Assert.assertEquals(Util.getInstanceStatus(PROJECT_ID, ZONE, MACHINE_NAME), + Status.RUNNING.toString()); + + // Stopping the instance. + StopInstance.stopInstance(PROJECT_ID, ZONE, MACHINE_NAME); + // Wait for the operation to complete. Setting timeout to 3 mins. + LocalDateTime endTime = LocalDateTime.now().plusMinutes(3); + while (!Util.getInstanceStatus(PROJECT_ID, ZONE, MACHINE_NAME) + .equalsIgnoreCase(Status.STOPPED.toString()) + && LocalDateTime.now().isBefore(endTime)) { + TimeUnit.SECONDS.sleep(5); + } + Assert.assertEquals(Util.getInstanceStatus(PROJECT_ID, ZONE, MACHINE_NAME), + Status.TERMINATED.toString()); + + // Starting the instance. + StartInstance.startInstance(PROJECT_ID, ZONE, MACHINE_NAME); + // Wait for the operation to complete. Setting timeout to 3 mins. + endTime = LocalDateTime.now().plusMinutes(3); + while (!Util.getInstanceStatus(PROJECT_ID, ZONE, MACHINE_NAME) + .equalsIgnoreCase(Status.RUNNING.toString()) + && LocalDateTime.now().isBefore(endTime)) { + TimeUnit.SECONDS.sleep(5); + } + Assert.assertEquals(Util.getInstanceStatus(PROJECT_ID, ZONE, MACHINE_NAME), + Status.RUNNING.toString()); + } + + @Test + public void testEncryptedInstanceOperations() + throws IOException, ExecutionException, InterruptedException, TimeoutException { + Assert.assertEquals(Util.getInstanceStatus(PROJECT_ID, ZONE, MACHINE_NAME_ENCRYPTED), + Status.RUNNING.toString()); + + // Stopping the encrypted instance. + StopInstance.stopInstance(PROJECT_ID, ZONE, MACHINE_NAME_ENCRYPTED); + // Wait for the operation to complete. Setting timeout to 3 mins. + LocalDateTime endTime = LocalDateTime.now().plusMinutes(3); + while (!Util.getInstanceStatus(PROJECT_ID, ZONE, MACHINE_NAME_ENCRYPTED) + .equalsIgnoreCase(Status.STOPPED.toString()) + && LocalDateTime.now().isBefore(endTime)) { + TimeUnit.SECONDS.sleep(5); + } + Assert.assertEquals(Util.getInstanceStatus(PROJECT_ID, ZONE, MACHINE_NAME_ENCRYPTED), + Status.TERMINATED.toString()); + + // Starting the encrypted instance. + StartEncryptedInstance + .startEncryptedInstance(PROJECT_ID, ZONE, MACHINE_NAME_ENCRYPTED, RAW_KEY); + // Wait for the operation to complete. Setting timeout to 3 mins. + endTime = LocalDateTime.now().plusMinutes(3); + while (!Util.getInstanceStatus(PROJECT_ID, ZONE, MACHINE_NAME_ENCRYPTED) + .equalsIgnoreCase(Status.RUNNING.toString()) + && LocalDateTime.now().isBefore(endTime)) { + TimeUnit.SECONDS.sleep(5); + } + Assert.assertEquals(Util.getInstanceStatus(PROJECT_ID, ZONE, MACHINE_NAME_ENCRYPTED), + Status.RUNNING.toString()); + } +} diff --git a/compute/cloud-client/src/test/java/compute/InstanceTemplatesIT.java b/compute/cloud-client/src/test/java/compute/InstanceTemplatesIT.java index 0fb8b4bef49..6bef26c0c06 100644 --- a/compute/cloud-client/src/test/java/compute/InstanceTemplatesIT.java +++ b/compute/cloud-client/src/test/java/compute/InstanceTemplatesIT.java @@ -27,19 +27,21 @@ import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import org.junit.After; -import org.junit.AfterClass; +import java.util.concurrent.TimeoutException; import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) +@Timeout(value = 10, unit = TimeUnit.MINUTES) public class InstanceTemplatesIT { - private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); private static final String DEFAULT_REGION = "us-central1"; private static final String DEFAULT_ZONE = DEFAULT_REGION + "-a"; @@ -59,8 +61,10 @@ public static void requireEnvVar(String envVarName) { .that(System.getenv(envVarName)).isNotEmpty(); } - @BeforeClass - public static void setup() throws IOException, ExecutionException, InterruptedException { + @BeforeAll + public static void setup() + throws IOException, ExecutionException, InterruptedException, TimeoutException { + final PrintStream out = System.out; ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); System.setOut(new PrintStream(stdOut)); requireEnvVar("GOOGLE_APPLICATION_CREDENTIALS"); @@ -113,11 +117,13 @@ public static void setup() throws IOException, ExecutionException, InterruptedEx Assert.assertEquals( getInstance(DEFAULT_ZONE, MACHINE_NAME_CR_TEMPLATE_OR).getDisksCount(), 2); stdOut.close(); - System.setOut(null); + System.setOut(out); } - @AfterClass - public static void cleanup() throws IOException, ExecutionException, InterruptedException { + @AfterAll + public static void cleanup() + throws IOException, ExecutionException, InterruptedException, TimeoutException { + final PrintStream out = System.out; ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); System.setOut(new PrintStream(stdOut)); // Delete instances. @@ -135,7 +141,7 @@ public static void cleanup() throws IOException, ExecutionException, Interrupted assertThat(stdOut.toString()) .contains("Instance template deletion operation status for " + TEMPLATE_NAME_WITH_SUBNET); stdOut.close(); - System.setOut(null); + System.setOut(out); } public static Instance getInstance(String zone, String instanceName) throws IOException { @@ -144,13 +150,13 @@ public static Instance getInstance(String zone, String instanceName) throws IOEx } } - @Before + @BeforeEach public void beforeEach() { stdOut = new ByteArrayOutputStream(); System.setOut(new PrintStream(stdOut)); } - @After + @AfterEach public void afterEach() { stdOut = null; System.setOut(null); diff --git a/compute/cloud-client/src/test/java/compute/InstancesAdvancedIT.java b/compute/cloud-client/src/test/java/compute/InstancesAdvancedIT.java new file mode 100644 index 00000000000..cd0552603f7 --- /dev/null +++ b/compute/cloud-client/src/test/java/compute/InstancesAdvancedIT.java @@ -0,0 +1,276 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package compute; + +import static com.google.common.truth.Truth.assertWithMessage; + +import com.google.api.gax.longrunning.OperationFuture; +import com.google.cloud.compute.v1.Disk; +import com.google.cloud.compute.v1.DisksClient; +import com.google.cloud.compute.v1.Image; +import com.google.cloud.compute.v1.ImagesClient; +import com.google.cloud.compute.v1.Instance.Status; +import com.google.cloud.compute.v1.Operation; +import com.google.cloud.compute.v1.Snapshot; +import com.google.cloud.compute.v1.SnapshotsClient; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.Assert; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +@Timeout(value = 10, unit = TimeUnit.MINUTES) +public class InstancesAdvancedIT { + + private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); + private static String ZONE; + private static String MACHINE_NAME_PUBLIC_IMAGE; + private static String MACHINE_NAME_CUSTOM_IMAGE; + private static String MACHINE_NAME_ADDITIONAL_DISK; + private static String MACHINE_NAME_SNAPSHOT; + private static String MACHINE_NAME_SNAPSHOT_ADDITIONAL; + private static String MACHINE_NAME_SUBNETWORK; + private static Disk TEST_DISK; + private static Image TEST_IMAGE; + private static Snapshot TEST_SNAPSHOT; + private static String NETWORK_NAME; + private static String SUBNETWORK_NAME; + + private ByteArrayOutputStream stdOut; + + // Check if the required environment variables are set. + public static void requireEnvVar(String envVarName) { + assertWithMessage(String.format("Missing environment variable '%s' ", envVarName)) + .that(System.getenv(envVarName)).isNotEmpty(); + } + + @BeforeAll + public static void setup() + throws IOException, ExecutionException, InterruptedException, TimeoutException { + final PrintStream out = System.out; + ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); + System.setOut(new PrintStream(stdOut)); + requireEnvVar("GOOGLE_APPLICATION_CREDENTIALS"); + requireEnvVar("GOOGLE_CLOUD_PROJECT"); + + ZONE = "us-central1-a"; + MACHINE_NAME_PUBLIC_IMAGE = "test-instance-pub-" + UUID.randomUUID(); + MACHINE_NAME_CUSTOM_IMAGE = "test-instance-cust-" + UUID.randomUUID(); + MACHINE_NAME_ADDITIONAL_DISK = "test-instance-add-" + UUID.randomUUID(); + MACHINE_NAME_SNAPSHOT = "test-instance-snap-" + UUID.randomUUID(); + MACHINE_NAME_SNAPSHOT_ADDITIONAL = "test-instance-snapa-" + UUID.randomUUID(); + MACHINE_NAME_SUBNETWORK = "test-instance-subnet-" + UUID.randomUUID(); + NETWORK_NAME = "global/networks/default"; + SUBNETWORK_NAME = "regions/us-central1/subnetworks/default"; + + TEST_DISK = createSourceDisk(); + TEST_SNAPSHOT = createSnapshot(TEST_DISK); + TEST_IMAGE = createImage(TEST_DISK); + + Util.cleanUpExistingInstances("test-instance", PROJECT_ID, ZONE); + + compute.CreateInstancesAdvanced.createFromPublicImage(PROJECT_ID, ZONE, + MACHINE_NAME_PUBLIC_IMAGE); + compute.CreateInstancesAdvanced.createFromCustomImage(PROJECT_ID, ZONE, + MACHINE_NAME_CUSTOM_IMAGE, TEST_IMAGE.getSelfLink()); + compute.CreateInstancesAdvanced.createWithAdditionalDisk(PROJECT_ID, ZONE, + MACHINE_NAME_ADDITIONAL_DISK); + compute.CreateInstancesAdvanced.createFromSnapshot(PROJECT_ID, ZONE, MACHINE_NAME_SNAPSHOT, + TEST_SNAPSHOT.getSelfLink()); + compute.CreateInstancesAdvanced.createWithSnapshottedDataDisk(PROJECT_ID, ZONE, + MACHINE_NAME_SNAPSHOT_ADDITIONAL, TEST_SNAPSHOT.getSelfLink()); + compute.CreateInstancesAdvanced.createWithSubnetwork(PROJECT_ID, ZONE, MACHINE_NAME_SUBNETWORK, + NETWORK_NAME, SUBNETWORK_NAME); + + TimeUnit.SECONDS.sleep(10); + stdOut.close(); + System.setOut(out); + } + + @AfterAll + public static void cleanup() + throws IOException, InterruptedException, ExecutionException, TimeoutException { + final PrintStream out = System.out; + ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); + System.setOut(new PrintStream(stdOut)); + // Delete all instances created for testing. + compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME_PUBLIC_IMAGE); + compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME_CUSTOM_IMAGE); + compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME_ADDITIONAL_DISK); + compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME_SNAPSHOT); + compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME_SNAPSHOT_ADDITIONAL); + compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME_SUBNETWORK); + + deleteImage(TEST_IMAGE); + deleteSnapshot(TEST_SNAPSHOT); + deleteDisk(TEST_DISK); + + stdOut.close(); + System.setOut(out); + } + + private static Image getActiveDebian() + throws IOException { + try (ImagesClient imagesClient = ImagesClient.create()) { + return imagesClient.getFromFamily("debian-cloud", "debian-11"); + } + } + + private static Disk createSourceDisk() + throws IOException, ExecutionException, InterruptedException, TimeoutException { + try (DisksClient disksClient = DisksClient.create()) { + + Disk disk = Disk.newBuilder() + .setSourceImage(getActiveDebian().getSelfLink()) + .setName("test-disk-" + UUID.randomUUID()) + .build(); + + OperationFuture operation = disksClient.insertAsync(PROJECT_ID, ZONE, + disk); + // Wait for the operation to complete. + operation.get(3, TimeUnit.MINUTES); + return disksClient.get(PROJECT_ID, ZONE, disk.getName()); + } + } + + private static void deleteDisk(Disk disk) + throws IOException, InterruptedException, ExecutionException, TimeoutException { + try (DisksClient disksClient = DisksClient.create()) { + OperationFuture operation = disksClient.deleteAsync(PROJECT_ID, ZONE, + disk.getName()); + operation.get(3, TimeUnit.MINUTES); + } + } + + private static Snapshot createSnapshot(Disk srcDisk) + throws IOException, InterruptedException, ExecutionException, TimeoutException { + try (SnapshotsClient snapshotsClient = SnapshotsClient.create(); + DisksClient disksClient = DisksClient.create()) { + + Snapshot snapshot = Snapshot.newBuilder() + .setName("test-snap-" + UUID.randomUUID()) + .build(); + + OperationFuture operation = disksClient.createSnapshotAsync(PROJECT_ID, + ZONE, srcDisk.getName(), + snapshot); + operation.get(3, TimeUnit.MINUTES); + return snapshotsClient.get(PROJECT_ID, snapshot.getName()); + } + } + + private static void deleteSnapshot(Snapshot snapshot) + throws IOException, InterruptedException, ExecutionException, TimeoutException { + try (SnapshotsClient snapshotsClient = SnapshotsClient.create()) { + OperationFuture operation = snapshotsClient.deleteAsync(PROJECT_ID, + snapshot.getName()); + operation.get(3, TimeUnit.MINUTES); + } + } + + private static Image createImage(Disk srcDisk) + throws IOException, InterruptedException, ExecutionException, TimeoutException { + try (ImagesClient imagesClient = ImagesClient.create()) { + + Image image = Image.newBuilder() + .setName("test-img-" + UUID.randomUUID()) + .setSourceDisk(srcDisk.getSelfLink()) + .build(); + + OperationFuture operation = imagesClient.insertAsync(PROJECT_ID, image); + operation.get(3, TimeUnit.MINUTES); + return imagesClient.get(PROJECT_ID, image.getName()); + } + } + + private static void deleteImage(Image image) + throws IOException, InterruptedException, ExecutionException, TimeoutException { + try (ImagesClient imagesClient = ImagesClient.create()) { + OperationFuture operation = imagesClient.deleteAsync(PROJECT_ID, + image.getName()); + operation.get(3, TimeUnit.MINUTES); + } + } + + + @BeforeEach + public void beforeEach() { + stdOut = new ByteArrayOutputStream(); + System.setOut(new PrintStream(stdOut)); + } + + @AfterEach + public void afterEach() { + stdOut = null; + System.setOut(null); + } + + @Test + public void testCreatePublicImage() throws IOException { + // Check if the instance was successfully created during the setup. + String response = Util.getInstanceStatus(PROJECT_ID, ZONE, MACHINE_NAME_PUBLIC_IMAGE); + Assert.assertEquals(response, Status.RUNNING.toString()); + } + + @Test + public void testCreateCustomImage() throws IOException { + // Check if the instance was successfully created during the setup. + String response = Util.getInstanceStatus(PROJECT_ID, ZONE, MACHINE_NAME_CUSTOM_IMAGE); + Assert.assertEquals(response, Status.RUNNING.toString()); + } + + @Test + public void testCreateAdditionalDisk() throws IOException { + // Check if the instance was successfully created during the setup. + String response = Util.getInstanceStatus(PROJECT_ID, ZONE, MACHINE_NAME_ADDITIONAL_DISK); + Assert.assertEquals(response, Status.RUNNING.toString()); + } + + @Test + public void testCreateFromSnapshot() throws IOException { + // Check if the instance was successfully created during the setup. + String response = Util.getInstanceStatus(PROJECT_ID, ZONE, MACHINE_NAME_SNAPSHOT); + Assert.assertEquals(response, Status.RUNNING.toString()); + } + + @Test + public void testCreateFromSnapshotAdditional() throws IOException { + // Check if the instance was successfully created during the setup. + String response = Util.getInstanceStatus(PROJECT_ID, ZONE, MACHINE_NAME_SNAPSHOT_ADDITIONAL); + Assert.assertEquals(response, Status.RUNNING.toString()); + } + + @Test + public void testCreateInSubnetwork() throws IOException { + // Check if the instance was successfully created during the setup. + String response = Util.getInstanceStatus(PROJECT_ID, ZONE, MACHINE_NAME_SUBNETWORK); + Assert.assertEquals(response, Status.RUNNING.toString()); + } + +} diff --git a/compute/cloud-client/src/test/java/compute/SnippetsIT.java b/compute/cloud-client/src/test/java/compute/SnippetsIT.java index af353c65662..9c0a0f1dd2b 100644 --- a/compute/cloud-client/src/test/java/compute/SnippetsIT.java +++ b/compute/cloud-client/src/test/java/compute/SnippetsIT.java @@ -20,19 +20,9 @@ import static com.google.common.truth.Truth.assertWithMessage; import com.google.api.gax.longrunning.OperationFuture; -import com.google.api.gax.rpc.InvalidArgumentException; -import com.google.api.gax.rpc.NotFoundException; -import com.google.cloud.compute.v1.Disk; -import com.google.cloud.compute.v1.DisksClient; -import com.google.cloud.compute.v1.FirewallsClient; -import com.google.cloud.compute.v1.Image; -import com.google.cloud.compute.v1.ImagesClient; -import com.google.cloud.compute.v1.Instance; import com.google.cloud.compute.v1.Instance.Status; import com.google.cloud.compute.v1.InstancesClient; import com.google.cloud.compute.v1.Operation; -import com.google.cloud.compute.v1.Snapshot; -import com.google.cloud.compute.v1.SnapshotsClient; import com.google.cloud.compute.v1.UsageExportLocation; import com.google.cloud.storage.Bucket; import com.google.cloud.storage.BucketInfo; @@ -41,48 +31,33 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; -import java.nio.charset.StandardCharsets; -import java.security.SecureRandom; -import java.time.LocalDateTime; -import java.util.Base64; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import java.util.stream.IntStream; -import org.junit.After; -import org.junit.AfterClass; +import java.util.concurrent.TimeoutException; import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) +@Timeout(value = 10, unit = TimeUnit.MINUTES) public class SnippetsIT { private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); private static String ZONE; private static String MACHINE_NAME; - private static String MACHINE_NAME_DELETE; private static String MACHINE_NAME_LIST_INSTANCE; private static String MACHINE_NAME_WAIT_FOR_OP; private static String MACHINE_NAME_ENCRYPTED; - private static String MACHINE_NAME_PUBLIC_IMAGE; - private static String MACHINE_NAME_CUSTOM_IMAGE; - private static String MACHINE_NAME_ADDITIONAL_DISK; - private static String MACHINE_NAME_SNAPSHOT; - private static String MACHINE_NAME_SNAPSHOT_ADDITIONAL; - private static String MACHINE_NAME_SUBNETWORK; private static String BUCKET_NAME; private static String IMAGE_PROJECT_NAME; - private static String FIREWALL_RULE_CREATE; - private static String NETWORK_NAME; - private static String SUBNETWORK_NAME; private static String RAW_KEY; - private static Disk TEST_DISK; - private static Image TEST_IMAGE; - private static Snapshot TEST_SNAPSHOT; private ByteArrayOutputStream stdOut; @@ -92,8 +67,10 @@ public static void requireEnvVar(String envVarName) { .that(System.getenv(envVarName)).isNotEmpty(); } - @BeforeClass - public static void setUp() throws IOException, InterruptedException, ExecutionException { + @BeforeAll + public static void setUp() + throws IOException, InterruptedException, ExecutionException, TimeoutException { + final PrintStream out = System.out; ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); System.setOut(new PrintStream(stdOut)); requireEnvVar("GOOGLE_APPLICATION_CREDENTIALS"); @@ -101,22 +78,12 @@ public static void setUp() throws IOException, InterruptedException, ExecutionEx ZONE = "us-central1-a"; MACHINE_NAME = "my-new-test-instance" + UUID.randomUUID(); - MACHINE_NAME_DELETE = "my-new-test-instance" + UUID.randomUUID(); MACHINE_NAME_LIST_INSTANCE = "my-new-test-instance" + UUID.randomUUID(); MACHINE_NAME_WAIT_FOR_OP = "my-new-test-instance" + UUID.randomUUID(); MACHINE_NAME_ENCRYPTED = "encrypted-test-instance" + UUID.randomUUID(); - MACHINE_NAME_PUBLIC_IMAGE = "test-instance-pub-" + UUID.randomUUID(); - MACHINE_NAME_CUSTOM_IMAGE = "test-instance-cust-" + UUID.randomUUID(); - MACHINE_NAME_ADDITIONAL_DISK = "test-instance-add-" + UUID.randomUUID(); - MACHINE_NAME_SNAPSHOT = "test-instance-snap-" + UUID.randomUUID(); - MACHINE_NAME_SNAPSHOT_ADDITIONAL = "test-instance-snapa-" + UUID.randomUUID(); - MACHINE_NAME_SUBNETWORK = "test-instance-subnet-" + UUID.randomUUID(); BUCKET_NAME = "my-new-test-bucket" + UUID.randomUUID(); IMAGE_PROJECT_NAME = "windows-sql-cloud"; - FIREWALL_RULE_CREATE = "firewall-rule-" + UUID.randomUUID(); - NETWORK_NAME = "global/networks/default"; - SUBNETWORK_NAME = "regions/us-central1/subnetworks/default"; - RAW_KEY = getBase64EncodedKey(); + RAW_KEY = Util.getBase64EncodedKey(); // Cleanup existing stale resources. Util.cleanUpExistingInstances("my-new-test-instance", PROJECT_ID, ZONE); @@ -124,242 +91,53 @@ public static void setUp() throws IOException, InterruptedException, ExecutionEx Util.cleanUpExistingInstances("test-instance-", PROJECT_ID, ZONE); compute.CreateInstance.createInstance(PROJECT_ID, ZONE, MACHINE_NAME); - compute.CreateInstance.createInstance(PROJECT_ID, ZONE, MACHINE_NAME_DELETE); compute.CreateInstance.createInstance(PROJECT_ID, ZONE, MACHINE_NAME_LIST_INSTANCE); compute.CreateInstance.createInstance(PROJECT_ID, ZONE, MACHINE_NAME_WAIT_FOR_OP); compute.CreateEncryptedInstance .createEncryptedInstance(PROJECT_ID, ZONE, MACHINE_NAME_ENCRYPTED, RAW_KEY); - TEST_DISK = createSourceDisk(); - TEST_SNAPSHOT = createSnapshot(TEST_DISK); - TEST_IMAGE = createImage(TEST_DISK); - - compute.CreateInstancesAdvanced.createFromPublicImage(PROJECT_ID, ZONE, - MACHINE_NAME_PUBLIC_IMAGE); - compute.CreateInstancesAdvanced.createFromCustomImage(PROJECT_ID, ZONE, - MACHINE_NAME_CUSTOM_IMAGE, TEST_IMAGE.getSelfLink()); - compute.CreateInstancesAdvanced.createWithAdditionalDisk(PROJECT_ID, ZONE, - MACHINE_NAME_ADDITIONAL_DISK); - compute.CreateInstancesAdvanced.createFromSnapshot(PROJECT_ID, ZONE, MACHINE_NAME_SNAPSHOT, - TEST_SNAPSHOT.getSelfLink()); - compute.CreateInstancesAdvanced.createWithSnapshottedDataDisk(PROJECT_ID, ZONE, - MACHINE_NAME_SNAPSHOT_ADDITIONAL, TEST_SNAPSHOT.getSelfLink()); - compute.CreateInstancesAdvanced.createWithSubnetwork(PROJECT_ID, ZONE, MACHINE_NAME_SUBNETWORK, - NETWORK_NAME, SUBNETWORK_NAME); - - TimeUnit.SECONDS.sleep(10); - compute.CreateFirewallRule.createFirewall(PROJECT_ID, FIREWALL_RULE_CREATE, NETWORK_NAME); TimeUnit.SECONDS.sleep(10); - // Moving the following tests to setup section as the created firewall rule is auto-deleted - // by GCE Enforcer within a few minutes. - testListFirewallRules(); - testPatchFirewallRule(); // Create a Google Cloud Storage bucket for UsageReports Storage storage = StorageOptions.newBuilder().setProjectId(PROJECT_ID).build().getService(); storage.create(BucketInfo.of(BUCKET_NAME)); stdOut.close(); - System.setOut(null); + System.setOut(out); } - @AfterClass - public static void cleanup() throws IOException, InterruptedException, ExecutionException { + @AfterAll + public static void cleanup() + throws IOException, InterruptedException, ExecutionException, TimeoutException { + final PrintStream out = System.out; ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); System.setOut(new PrintStream(stdOut)); // Delete all instances created for testing. requireEnvVar("GOOGLE_APPLICATION_CREDENTIALS"); requireEnvVar("GOOGLE_CLOUD_PROJECT"); - if (!isFirewallRuleDeletedByGceEnforcer(PROJECT_ID, FIREWALL_RULE_CREATE)) { - DeleteFirewallRule.deleteFirewallRule(PROJECT_ID, FIREWALL_RULE_CREATE); - } compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME_ENCRYPTED); compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME); compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME_LIST_INSTANCE); - compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME_PUBLIC_IMAGE); - compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME_CUSTOM_IMAGE); - compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME_ADDITIONAL_DISK); - compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME_SNAPSHOT); - compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME_SNAPSHOT_ADDITIONAL); - compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME_SUBNETWORK); - - deleteImage(TEST_IMAGE); - deleteSnapshot(TEST_SNAPSHOT); - deleteDisk(TEST_DISK); - // Delete the Google Cloud Storage bucket created for usage reports. Storage storage = StorageOptions.newBuilder().setProjectId(PROJECT_ID).build().getService(); Bucket bucket = storage.get(BUCKET_NAME); bucket.delete(); - stdOut.close(); - System.setOut(null); - } - - private static Image getActiveDebian() - throws IOException { - try (ImagesClient imagesClient = ImagesClient.create()) { - return imagesClient.getFromFamily("debian-cloud", "debian-11"); - } - } - - private static Disk createSourceDisk() - throws IOException, ExecutionException, InterruptedException { - try (DisksClient disksClient = DisksClient.create()) { - - Disk disk = Disk.newBuilder() - .setSourceImage(getActiveDebian().getSelfLink()) - .setName("test-disk-" + UUID.randomUUID()) - .build(); - - OperationFuture operation = disksClient.insertAsync(PROJECT_ID, ZONE, - disk); - // Wait for the operation to complete. - operation.get(); - return disksClient.get(PROJECT_ID, ZONE, disk.getName()); - } - } - - private static void deleteDisk(Disk disk) - throws IOException, InterruptedException, ExecutionException { - try (DisksClient disksClient = DisksClient.create()) { - OperationFuture operation = disksClient.deleteAsync(PROJECT_ID, ZONE, - disk.getName()); - operation.get(); - } - } - - private static Snapshot createSnapshot(Disk srcDisk) - throws IOException, InterruptedException, ExecutionException { - try (SnapshotsClient snapshotsClient = SnapshotsClient.create(); - DisksClient disksClient = DisksClient.create()) { - - Snapshot snapshot = Snapshot.newBuilder() - .setName("test-snap-" + UUID.randomUUID()) - .build(); - - OperationFuture operation = disksClient.createSnapshotAsync(PROJECT_ID, - ZONE, srcDisk.getName(), - snapshot); - operation.get(); - return snapshotsClient.get(PROJECT_ID, snapshot.getName()); - } - } - - private static void deleteSnapshot(Snapshot snapshot) - throws IOException, InterruptedException, ExecutionException { - try (SnapshotsClient snapshotsClient = SnapshotsClient.create()) { - OperationFuture operation = snapshotsClient.deleteAsync(PROJECT_ID, - snapshot.getName()); - operation.get(); - } - } - - private static Image createImage(Disk srcDisk) - throws IOException, InterruptedException, ExecutionException { - try (ImagesClient imagesClient = ImagesClient.create()) { - - Image image = Image.newBuilder() - .setName("test-img-" + UUID.randomUUID()) - .setSourceDisk(srcDisk.getSelfLink()) - .build(); - - OperationFuture operation = imagesClient.insertAsync(PROJECT_ID, image); - operation.get(); - return imagesClient.get(PROJECT_ID, image.getName()); - } - } - - private static void deleteImage(Image image) - throws IOException, InterruptedException, ExecutionException { - try (ImagesClient imagesClient = ImagesClient.create()) { - OperationFuture operation = imagesClient.deleteAsync(PROJECT_ID, - image.getName()); - operation.get(); - } - } - - public static String getBase64EncodedKey() { - String sampleSpace = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - StringBuilder stringBuilder = new StringBuilder(); - SecureRandom random = new SecureRandom(); - IntStream.range(0, 32) - .forEach( - x -> stringBuilder.append(sampleSpace.charAt(random.nextInt(sampleSpace.length())))); - - return Base64.getEncoder() - .encodeToString(stringBuilder.toString().getBytes(StandardCharsets.US_ASCII)); - } - - public static void testListFirewallRules() - throws IOException, ExecutionException, InterruptedException { - final PrintStream out = System.out; - ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); - System.setOut(new PrintStream(stdOut)); - if (!isFirewallRuleDeletedByGceEnforcer(PROJECT_ID, FIREWALL_RULE_CREATE)) { - compute.ListFirewallRules.listFirewallRules(PROJECT_ID); - assertThat(stdOut.toString()).contains(FIREWALL_RULE_CREATE); - } - // Clear system output to not affect other tests. - // Refrain from setting out to null. - stdOut.close(); - System.setOut(out); - } - - public static void testPatchFirewallRule() - throws IOException, InterruptedException, ExecutionException { - final PrintStream out = System.out; - ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); - System.setOut(new PrintStream(stdOut)); - if (!isFirewallRuleDeletedByGceEnforcer(PROJECT_ID, FIREWALL_RULE_CREATE)) { - try (FirewallsClient client = FirewallsClient.create()) { - Assert.assertEquals(1000, client.get(PROJECT_ID, FIREWALL_RULE_CREATE).getPriority()); - compute.PatchFirewallRule.patchFirewallPriority(PROJECT_ID, FIREWALL_RULE_CREATE, 500); - TimeUnit.SECONDS.sleep(5); - Assert.assertEquals(500, client.get(PROJECT_ID, FIREWALL_RULE_CREATE).getPriority()); - } - } - // Clear system output to not affect other tests. - // Refrain from setting out to null as it will throw NullPointer in the subsequent tests. stdOut.close(); System.setOut(out); } - public static boolean isFirewallRuleDeletedByGceEnforcer(String projectId, - String firewallRule) throws IOException, ExecutionException, InterruptedException { - /* (**INTERNAL method**) - This method will prevent test failure if the firewall rule was auto-deleted by GCE Enforcer. - (Feel free to remove this method if not running on a Google-owned project.) - */ - try { - GetFirewallRule.getFirewallRule(projectId, firewallRule); - } catch (NotFoundException e) { - System.out.println("Rule already deleted ! "); - return true; - } catch (InvalidArgumentException | NullPointerException e) { - System.out.println("Rule is not ready (probably being deleted)."); - return true; - } - return false; - } - - public static String getInstanceStatus(String instanceName) throws IOException { - try (InstancesClient instancesClient = InstancesClient.create()) { - Instance response = instancesClient.get(PROJECT_ID, ZONE, instanceName); - return response.getStatus(); - } - } - @Before + @BeforeEach public void beforeEach() { stdOut = new ByteArrayOutputStream(); System.setOut(new PrintStream(stdOut)); } - @After + @AfterEach public void afterEach() { stdOut = null; System.setOut(null); @@ -368,58 +146,17 @@ public void afterEach() { @Test public void testCreateInstance() throws IOException { // Check if the instance was successfully created during the setup. - String response = getInstanceStatus(MACHINE_NAME); + String response = Util.getInstanceStatus(PROJECT_ID, ZONE, MACHINE_NAME); Assert.assertEquals(response, Status.RUNNING.toString()); } @Test public void testCreateEncryptedInstance() throws IOException { // Check if the instance was successfully created during the setup. - String response = getInstanceStatus(MACHINE_NAME_ENCRYPTED); + String response = Util.getInstanceStatus(PROJECT_ID, ZONE, MACHINE_NAME_ENCRYPTED); Assert.assertEquals(response, Status.RUNNING.toString()); } - @Test - public void testCreatePublicImage() throws IOException { - // Check if the instance was successfully created during the setup. - String response = getInstanceStatus(MACHINE_NAME_PUBLIC_IMAGE); - Assert.assertEquals(response, Status.RUNNING.toString()); - } - - @Test - public void testCreateCustomImage() throws IOException { - // Check if the instance was successfully created during the setup. - String response = getInstanceStatus(MACHINE_NAME_CUSTOM_IMAGE); - Assert.assertEquals(response, Status.RUNNING.toString()); - } - - @Test - public void testCreateAdditionalDisk() throws IOException { - // Check if the instance was successfully created during the setup. - String response = getInstanceStatus(MACHINE_NAME_ADDITIONAL_DISK); - Assert.assertEquals(response, Status.RUNNING.toString()); - } - - @Test - public void testCreateFromSnapshot() throws IOException { - // Check if the instance was successfully created during the setup. - String response = getInstanceStatus(MACHINE_NAME_SNAPSHOT); - Assert.assertEquals(response, Status.RUNNING.toString()); - } - - @Test - public void testCreateFromSnapshotAdditional() throws IOException { - // Check if the instance was successfully created during the setup. - String response = getInstanceStatus(MACHINE_NAME_SNAPSHOT_ADDITIONAL); - Assert.assertEquals(response, Status.RUNNING.toString()); - } - - @Test - public void testCreateInSubnetwork() throws IOException { - // Check if the instance was successfully created during the setup. - String response = getInstanceStatus(MACHINE_NAME_SUBNETWORK); - Assert.assertEquals(response, Status.RUNNING.toString()); - } @Test public void testListInstance() throws IOException { @@ -434,25 +171,20 @@ public void testListAllInstances() throws IOException { } @Test - public void testDeleteInstance() throws IOException, InterruptedException, ExecutionException { - compute.DeleteInstance.deleteInstance(PROJECT_ID, ZONE, MACHINE_NAME_DELETE); - assertThat(stdOut.toString()).contains("Operation Status: DONE"); - } - - @Test - public void testWaitForOperation() throws IOException, InterruptedException, ExecutionException { + public void testWaitForOperation() + throws IOException, InterruptedException, ExecutionException, TimeoutException { // Construct a delete request and get the operation instance. InstancesClient instancesClient = InstancesClient.create(); OperationFuture operation = instancesClient.deleteAsync(PROJECT_ID, ZONE, MACHINE_NAME_WAIT_FOR_OP); // Wait for the operation to complete. - operation.get(); + operation.get(3, TimeUnit.MINUTES); assertThat(stdOut.toString().contains("Operation Status: DONE")); } @Test public void testSetUsageBucketExportCustomPrefix() - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException, ExecutionException, TimeoutException { // Set custom Report Name Prefix. String customPrefix = "my-custom-prefix"; compute.SetUsageExportBucket.setUsageExportBucket(PROJECT_ID, BUCKET_NAME, customPrefix); @@ -488,57 +220,4 @@ public void testListImagesByPage() throws IOException { Assert.assertTrue(stdOut.toString().contains("Page Number: 1")); } - @Test - public void testInstanceOperations() - throws IOException, ExecutionException, InterruptedException { - Assert.assertEquals(getInstanceStatus(MACHINE_NAME), Status.RUNNING.toString()); - - // Stopping the instance. - StopInstance.stopInstance(PROJECT_ID, ZONE, MACHINE_NAME); - // Wait for the operation to complete. Setting timeout to 3 mins. - LocalDateTime endTime = LocalDateTime.now().plusMinutes(3); - while (getInstanceStatus(MACHINE_NAME).equalsIgnoreCase(Status.STOPPING.toString()) - && LocalDateTime.now().isBefore(endTime)) { - TimeUnit.SECONDS.sleep(5); - } - Assert.assertEquals(getInstanceStatus(MACHINE_NAME), Status.TERMINATED.toString()); - - // Starting the instance. - StartInstance.startInstance(PROJECT_ID, ZONE, MACHINE_NAME); - // Wait for the operation to complete. Setting timeout to 3 mins. - endTime = LocalDateTime.now().plusMinutes(3); - while (getInstanceStatus(MACHINE_NAME).equalsIgnoreCase(Status.RUNNING.toString()) - && LocalDateTime.now().isBefore(endTime)) { - TimeUnit.SECONDS.sleep(5); - } - Assert.assertEquals(getInstanceStatus(MACHINE_NAME), Status.RUNNING.toString()); - } - - @Test - public void testEncryptedInstanceOperations() - throws IOException, ExecutionException, InterruptedException { - Assert.assertEquals(getInstanceStatus(MACHINE_NAME_ENCRYPTED), Status.RUNNING.toString()); - - // Stopping the encrypted instance. - StopInstance.stopInstance(PROJECT_ID, ZONE, MACHINE_NAME_ENCRYPTED); - // Wait for the operation to complete. Setting timeout to 3 mins. - LocalDateTime endTime = LocalDateTime.now().plusMinutes(3); - while (getInstanceStatus(MACHINE_NAME_ENCRYPTED).equalsIgnoreCase(Status.STOPPING.toString()) - && LocalDateTime.now().isBefore(endTime)) { - TimeUnit.SECONDS.sleep(5); - } - Assert.assertEquals(getInstanceStatus(MACHINE_NAME_ENCRYPTED), Status.TERMINATED.toString()); - - // Starting the encrypted instance. - StartEncryptedInstance - .startEncryptedInstance(PROJECT_ID, ZONE, MACHINE_NAME_ENCRYPTED, RAW_KEY); - // Wait for the operation to complete. Setting timeout to 3 mins. - endTime = LocalDateTime.now().plusMinutes(3); - while (getInstanceStatus(MACHINE_NAME_ENCRYPTED).equalsIgnoreCase(Status.RUNNING.toString()) - && LocalDateTime.now().isBefore(endTime)) { - TimeUnit.SECONDS.sleep(5); - } - Assert.assertEquals(getInstanceStatus(MACHINE_NAME_ENCRYPTED), Status.RUNNING.toString()); - } - } diff --git a/compute/cloud-client/src/test/java/compute/Util.java b/compute/cloud-client/src/test/java/compute/Util.java index f68cca0f0ce..cf0adb0de7e 100644 --- a/compute/cloud-client/src/test/java/compute/Util.java +++ b/compute/cloud-client/src/test/java/compute/Util.java @@ -16,15 +16,27 @@ package compute; +import com.google.cloud.compute.v1.AggregatedListInstancesRequest; import com.google.cloud.compute.v1.Instance; +import com.google.cloud.compute.v1.Instance.Status; import com.google.cloud.compute.v1.InstanceTemplate; +import com.google.cloud.compute.v1.InstanceTemplatesClient; +import com.google.cloud.compute.v1.InstanceTemplatesClient.ListPagedResponse; +import com.google.cloud.compute.v1.InstancesClient; +import com.google.cloud.compute.v1.InstancesClient.AggregatedListPagedResponse; import com.google.cloud.compute.v1.InstancesScopedList; +import com.google.cloud.compute.v1.ListInstanceTemplatesRequest; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; import java.time.Instant; import java.time.OffsetDateTime; import java.time.temporal.ChronoUnit; +import java.util.Base64; import java.util.Map.Entry; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import java.util.stream.IntStream; public class Util { // Cleans existing test resources if any. @@ -36,14 +48,15 @@ public class Util { // Delete templates which starts with the given prefixToDelete and // has creation timestamp >24 hours. public static void cleanUpExistingInstanceTemplates(String prefixToDelete, String projectId) - throws IOException, ExecutionException, InterruptedException { - for (InstanceTemplate template : ListInstanceTemplates.listInstanceTemplates(projectId) + throws IOException, ExecutionException, InterruptedException, TimeoutException { + for (InstanceTemplate template : listFilteredInstanceTemplates(projectId, prefixToDelete) .iterateAll()) { if (!template.hasCreationTimestamp()) { continue; } if (template.getName().contains(prefixToDelete) - && isCreatedBeforeThresholdTime(template.getCreationTimestamp())) { + && isCreatedBeforeThresholdTime(template.getCreationTimestamp()) + && template.isInitialized()) { DeleteInstanceTemplate.deleteInstanceTemplate(projectId, template.getName()); } } @@ -54,15 +67,16 @@ && isCreatedBeforeThresholdTime(template.getCreationTimestamp())) { // has creation timestamp >24 hours. public static void cleanUpExistingInstances(String prefixToDelete, String projectId, String instanceZone) - throws IOException, ExecutionException, InterruptedException { - for (Entry instanceGroup : ListAllInstances.listAllInstances( - projectId).iterateAll()) { + throws IOException, ExecutionException, InterruptedException, TimeoutException { + for (Entry instanceGroup : listFilteredInstances( + projectId, prefixToDelete).iterateAll()) { for (Instance instance : instanceGroup.getValue().getInstancesList()) { if (!instance.hasCreationTimestamp()) { continue; } if (instance.getName().contains(prefixToDelete) - && isCreatedBeforeThresholdTime(instance.getCreationTimestamp())) { + && isCreatedBeforeThresholdTime(instance.getCreationTimestamp()) + && instance.getStatus().equalsIgnoreCase(Status.RUNNING.toString())) { DeleteInstance.deleteInstance(projectId, instanceZone, instance.getName()); } } @@ -74,4 +88,52 @@ public static boolean isCreatedBeforeThresholdTime(String timestamp) { .isBefore(Instant.now().minus(DELETION_THRESHOLD_TIME_HOURS, ChronoUnit.HOURS)); } + public static AggregatedListPagedResponse listFilteredInstances(String project, + String instanceNamePrefix) throws IOException { + try (InstancesClient instancesClient = InstancesClient.create()) { + + AggregatedListInstancesRequest aggregatedListInstancesRequest = AggregatedListInstancesRequest + .newBuilder() + .setProject(project) + .setFilter(String.format("name:%s", instanceNamePrefix)) + .build(); + + return instancesClient + .aggregatedList(aggregatedListInstancesRequest); + } + } + + public static ListPagedResponse listFilteredInstanceTemplates(String projectId, + String instanceTemplatePrefix) throws IOException { + try (InstanceTemplatesClient instanceTemplatesClient = InstanceTemplatesClient.create()) { + ListInstanceTemplatesRequest listInstanceTemplatesRequest = + ListInstanceTemplatesRequest.newBuilder() + .setProject(projectId) + .setFilter(String.format("name:%s", instanceTemplatePrefix)) + .build(); + + return instanceTemplatesClient.list(listInstanceTemplatesRequest); + } + } + + public static String getBase64EncodedKey() { + String sampleSpace = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + StringBuilder stringBuilder = new StringBuilder(); + SecureRandom random = new SecureRandom(); + IntStream.range(0, 32) + .forEach( + x -> stringBuilder.append(sampleSpace.charAt(random.nextInt(sampleSpace.length())))); + + return Base64.getEncoder() + .encodeToString(stringBuilder.toString().getBytes(StandardCharsets.US_ASCII)); + } + + public static String getInstanceStatus(String project, String zone, String instanceName) + throws IOException { + try (InstancesClient instancesClient = InstancesClient.create()) { + Instance response = instancesClient.get(project, zone, instanceName); + return response.getStatus(); + } + } + } \ No newline at end of file diff --git a/compute/cloud-client/src/test/java/compute/customhostname/CustomHostnameInstanceIT.java b/compute/cloud-client/src/test/java/compute/customhostname/CustomHostnameInstanceIT.java index 770b4e2aca6..4766568c834 100644 --- a/compute/cloud-client/src/test/java/compute/customhostname/CustomHostnameInstanceIT.java +++ b/compute/cloud-client/src/test/java/compute/customhostname/CustomHostnameInstanceIT.java @@ -25,17 +25,20 @@ import java.io.PrintStream; import java.util.UUID; import java.util.concurrent.ExecutionException; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) +@Timeout(value = 10, unit = TimeUnit.MINUTES) public class CustomHostnameInstanceIT { - private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); private static String INSTANCE_NAME; private static String ZONE; @@ -49,8 +52,9 @@ public static void requireEnvVar(String envVarName) { .that(System.getenv(envVarName)).isNotEmpty(); } - @BeforeClass - public static void setup() throws IOException, ExecutionException, InterruptedException { + @BeforeAll + public static void setup() + throws IOException, ExecutionException, InterruptedException, TimeoutException { final PrintStream out = System.out; ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); System.setOut(new PrintStream(stdOut)); @@ -70,8 +74,9 @@ public static void setup() throws IOException, ExecutionException, InterruptedEx System.setOut(out); } - @AfterClass - public static void cleanUp() throws IOException, ExecutionException, InterruptedException { + @AfterAll + public static void cleanUp() + throws IOException, ExecutionException, InterruptedException, TimeoutException { final PrintStream out = System.out; ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); System.setOut(new PrintStream(stdOut)); @@ -80,13 +85,13 @@ public static void cleanUp() throws IOException, ExecutionException, Interrupted System.setOut(out); } - @Before + @BeforeEach public void beforeEach() { stdOut = new ByteArrayOutputStream(); System.setOut(new PrintStream(stdOut)); } - @After + @AfterEach public void afterEach() { stdOut = null; System.setOut(null); diff --git a/compute/cloud-client/src/test/java/compute/deleteprotection/DeleteProtectionIT.java b/compute/cloud-client/src/test/java/compute/deleteprotection/DeleteProtectionIT.java index 7ccda634cb1..9409b1128a7 100644 --- a/compute/cloud-client/src/test/java/compute/deleteprotection/DeleteProtectionIT.java +++ b/compute/cloud-client/src/test/java/compute/deleteprotection/DeleteProtectionIT.java @@ -26,16 +26,20 @@ import java.io.PrintStream; import java.util.UUID; import java.util.concurrent.ExecutionException; -import org.junit.After; -import org.junit.AfterClass; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) +@Timeout(value = 10, unit = TimeUnit.MINUTES) public class DeleteProtectionIT { private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); @@ -50,8 +54,9 @@ public static void requireEnvVar(String envVarName) { .that(System.getenv(envVarName)).isNotEmpty(); } - @BeforeClass - public static void setup() throws IOException, ExecutionException, InterruptedException { + @BeforeAll + public static void setup() + throws IOException, ExecutionException, InterruptedException, TimeoutException { final PrintStream out = System.out; ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); System.setOut(new PrintStream(stdOut)); @@ -72,8 +77,9 @@ public static void setup() throws IOException, ExecutionException, InterruptedEx System.setOut(out); } - @AfterClass - public static void cleanUp() throws IOException, ExecutionException, InterruptedException { + @AfterAll + public static void cleanUp() + throws IOException, ExecutionException, InterruptedException, TimeoutException { final PrintStream out = System.out; ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); System.setOut(new PrintStream(stdOut)); @@ -88,20 +94,21 @@ public static void cleanUp() throws IOException, ExecutionException, Interrupted System.setOut(out); } - @Before + @BeforeEach public void beforeEach() { stdOut = new ByteArrayOutputStream(); System.setOut(new PrintStream(stdOut)); } - @After + @AfterEach public void afterEach() { stdOut = null; System.setOut(null); } @Test - public void testDeleteProtection() throws IOException, ExecutionException, InterruptedException { + public void testDeleteProtection() + throws IOException, ExecutionException, InterruptedException, TimeoutException { Assert.assertTrue(GetDeleteProtection.getDeleteProtection(PROJECT_ID, ZONE, INSTANCE_NAME)); SetDeleteProtection.setDeleteProtection(PROJECT_ID, ZONE, INSTANCE_NAME, false); Assert.assertFalse(GetDeleteProtection.getDeleteProtection(PROJECT_ID, ZONE, INSTANCE_NAME)); diff --git a/compute/cloud-client/src/test/java/compute/preemptible/PreemptibleIT.java b/compute/cloud-client/src/test/java/compute/preemptible/PreemptibleIT.java index 5e33c314b99..98673bc14a9 100644 --- a/compute/cloud-client/src/test/java/compute/preemptible/PreemptibleIT.java +++ b/compute/cloud-client/src/test/java/compute/preemptible/PreemptibleIT.java @@ -28,15 +28,19 @@ import java.io.PrintStream; import java.util.UUID; import java.util.concurrent.ExecutionException; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) +@Timeout(value = 10, unit = TimeUnit.MINUTES) public class PreemptibleIT { private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); @@ -51,8 +55,9 @@ public static void requireEnvVar(String envVarName) { .that(System.getenv(envVarName)).isNotEmpty(); } - @BeforeClass - public static void setup() throws IOException, ExecutionException, InterruptedException { + @BeforeAll + public static void setup() + throws IOException, ExecutionException, InterruptedException, TimeoutException { final PrintStream out = System.out; ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); System.setOut(new PrintStream(stdOut)); @@ -73,8 +78,9 @@ public static void setup() throws IOException, ExecutionException, InterruptedEx System.setOut(out); } - @AfterClass - public static void cleanUp() throws IOException, ExecutionException, InterruptedException { + @AfterAll + public static void cleanUp() + throws IOException, ExecutionException, InterruptedException, TimeoutException { final PrintStream out = System.out; ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); System.setOut(new PrintStream(stdOut)); @@ -85,13 +91,13 @@ public static void cleanUp() throws IOException, ExecutionException, Interrupted System.setOut(out); } - @Before + @BeforeEach public void beforeEach() { stdOut = new ByteArrayOutputStream(); System.setOut(new PrintStream(stdOut)); } - @After + @AfterEach public void afterEach() { stdOut = null; System.setOut(null); diff --git a/compute/cloud-client/src/test/java/compute/windows/windowsinstances/CreatingManagingWindowsInstancesIT.java b/compute/cloud-client/src/test/java/compute/windows/windowsinstances/CreatingManagingWindowsInstancesIT.java new file mode 100644 index 00000000000..7a0d33aa094 --- /dev/null +++ b/compute/cloud-client/src/test/java/compute/windows/windowsinstances/CreatingManagingWindowsInstancesIT.java @@ -0,0 +1,138 @@ +// Copyright 2022 Google LLC +// +// 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. + +package compute.windows.windowsinstances; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import com.google.cloud.compute.v1.RoutesClient; +import compute.DeleteFirewallRule; +import compute.DeleteInstance; +import compute.Util; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +@Timeout(value = 10, unit = TimeUnit.MINUTES) +public class CreatingManagingWindowsInstancesIT { + + private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT"); + private static final String ZONE = "europe-central2-b"; + private static String INSTANCE_NAME_EXTERNAL; + private static String INSTANCE_NAME_INTERNAL; + private static String FIREWALL_RULE_NAME; + private static String NETWORK_NAME; + private static String SUBNETWORK_NAME; + private static String ROUTE_NAME; + + private ByteArrayOutputStream stdOut; + + // Check if the required environment variables are set. + public static void requireEnvVar(String envVarName) { + assertWithMessage(String.format("Missing environment variable '%s' ", envVarName)) + .that(System.getenv(envVarName)).isNotEmpty(); + } + + @BeforeAll + public static void setup() + throws IOException, ExecutionException, InterruptedException, TimeoutException { + final PrintStream out = System.out; + ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); + System.setOut(new PrintStream(stdOut)); + + requireEnvVar("GOOGLE_APPLICATION_CREDENTIALS"); + requireEnvVar("GOOGLE_CLOUD_PROJECT"); + + // Cleanup existing test instances. + Util.cleanUpExistingInstances("windows-test-instance", PROJECT_ID, ZONE); + + String uuid = UUID.randomUUID().toString().split("-")[0]; + INSTANCE_NAME_EXTERNAL = "windows-test-instance-external-" + uuid; + INSTANCE_NAME_INTERNAL = "windows-test-instance-internal-" + uuid; + FIREWALL_RULE_NAME = "windows-test-firewall-" + uuid; + NETWORK_NAME = "global/networks/default"; + SUBNETWORK_NAME = "regions/europe-central2/subnetworks/default"; + ROUTE_NAME = "windows-test-route-" + uuid; + + stdOut.close(); + System.setOut(out); + } + + public static void deleteRoute() + throws IOException, ExecutionException, InterruptedException, TimeoutException { + try (RoutesClient routesClient = RoutesClient.create()) { + routesClient.deleteAsync(PROJECT_ID, ROUTE_NAME).get(3, TimeUnit.MINUTES); + } + } + + @BeforeEach + public void beforeEach() { + stdOut = new ByteArrayOutputStream(); + System.setOut(new PrintStream(stdOut)); + } + + @AfterEach + public void afterEach() { + stdOut = null; + System.setOut(null); + } + + @Test + public void testCreateWindowsServerInstanceExternalIp() + throws IOException, ExecutionException, InterruptedException, TimeoutException { + // Create Windows server instance with external IP. + CreateWindowsServerInstanceExternalIp.createWindowsServerInstanceExternalIp(PROJECT_ID, ZONE, + INSTANCE_NAME_EXTERNAL); + assertThat(stdOut.toString()).contains("Instance created " + INSTANCE_NAME_EXTERNAL); + + // Delete instance. + DeleteInstance.deleteInstance(PROJECT_ID, ZONE, INSTANCE_NAME_EXTERNAL); + } + + @Test + public void testCreateWindowsServerInstanceInternalIp() + throws IOException, ExecutionException, InterruptedException, TimeoutException { + // Create Windows server instance with internal IP and firewall rule. + CreateWindowsServerInstanceInternalIp.createWindowsServerInstanceInternalIp(PROJECT_ID, ZONE, + INSTANCE_NAME_INTERNAL, NETWORK_NAME, SUBNETWORK_NAME); + assertThat(stdOut.toString()).contains("Instance created " + INSTANCE_NAME_INTERNAL); + CreateFirewallRuleForWindowsActivationHost.createFirewallRuleForWindowsActivationHost( + PROJECT_ID, FIREWALL_RULE_NAME, NETWORK_NAME); + assertThat(stdOut.toString()).contains( + String.format("Firewall rule created %s", FIREWALL_RULE_NAME)); + CreateRouteToWindowsActivationHost.createRouteToWindowsActivationHost(PROJECT_ID, ROUTE_NAME, + NETWORK_NAME); + assertThat(stdOut.toString()).contains(String.format("Route created %s", ROUTE_NAME)); + + // Delete Route. + deleteRoute(); + // Delete Firewall. + DeleteFirewallRule.deleteFirewallRule(PROJECT_ID, FIREWALL_RULE_NAME); + // Delete Instance. + DeleteInstance.deleteInstance(PROJECT_ID, ZONE, INSTANCE_NAME_INTERNAL); + } +} diff --git a/compute/cmdline/pom.xml b/compute/cmdline/pom.xml index 0991e948111..54541005105 100644 --- a/compute/cmdline/pom.xml +++ b/compute/cmdline/pom.xml @@ -50,7 +50,7 @@ limitations under the License. com.google.api-client google-api-client-gson - 1.33.4 + 1.34.1 diff --git a/compute/signed-metadata/pom.xml b/compute/signed-metadata/pom.xml index 05aca3e9311..7c7d4871c48 100644 --- a/compute/signed-metadata/pom.xml +++ b/compute/signed-metadata/pom.xml @@ -40,7 +40,7 @@ com.auth0 java-jwt - 3.19.1 + 3.19.2 com.google.code.gson diff --git a/container-registry/vulnerability-notification-function/pom.xml b/container-registry/vulnerability-notification-function/pom.xml index 676a1d17b93..3c96669e0b7 100644 --- a/container-registry/vulnerability-notification-function/pom.xml +++ b/container-registry/vulnerability-notification-function/pom.xml @@ -55,7 +55,7 @@ org.projectlombok lombok - 1.18.22 + 1.18.24 @@ -68,7 +68,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/dataflow/encryption-keys/pom.xml b/dataflow/encryption-keys/pom.xml index ed19b5c70e6..00f65700009 100644 --- a/dataflow/encryption-keys/pom.xml +++ b/dataflow/encryption-keys/pom.xml @@ -34,7 +34,7 @@ 11 UTF-8 - 2.37.0 + 2.38.0 3.10.1 3.0.0 diff --git a/dataflow/flex-templates/kafka_to_bigquery/pom.xml b/dataflow/flex-templates/kafka_to_bigquery/pom.xml index 2c349b37577..b2d3c9af91c 100644 --- a/dataflow/flex-templates/kafka_to_bigquery/pom.xml +++ b/dataflow/flex-templates/kafka_to_bigquery/pom.xml @@ -33,7 +33,7 @@ 11 11 UTF-8 - 2.37.0 + 2.38.0 7.0.2-ce 3.0.0 3.10.1 diff --git a/dataflow/flex-templates/streaming_beam_sql/pom.xml b/dataflow/flex-templates/streaming_beam_sql/pom.xml index b877cb5565c..c4edc19608e 100644 --- a/dataflow/flex-templates/streaming_beam_sql/pom.xml +++ b/dataflow/flex-templates/streaming_beam_sql/pom.xml @@ -34,7 +34,7 @@ 11 UTF-8 - 2.37.0 + 2.38.0 3.0.0 3.10.1 diff --git a/dataflow/spanner-io/pom.xml b/dataflow/spanner-io/pom.xml index c52e7f8c430..5d26ad7e5f3 100644 --- a/dataflow/spanner-io/pom.xml +++ b/dataflow/spanner-io/pom.xml @@ -37,7 +37,7 @@ 11 11 UTF-8 - 2.37.0 + 2.38.0 1.7.36 diff --git a/dataflow/templates/pom.xml b/dataflow/templates/pom.xml index 6311f58d856..dce23ad332c 100644 --- a/dataflow/templates/pom.xml +++ b/dataflow/templates/pom.xml @@ -34,7 +34,7 @@ 11 UTF-8 - 2.37.0 + 2.38.0 3.10.1 3.0.0 diff --git a/dialogflow/basic-webhook/pom.xml b/dialogflow/basic-webhook/pom.xml index b6c67f0e10e..b9221fc6139 100644 --- a/dialogflow/basic-webhook/pom.xml +++ b/dialogflow/basic-webhook/pom.xml @@ -35,7 +35,7 @@ com.google.cloud google-cloud-dialogflow-cx - 0.12.0 + 0.12.1 com.google.code.gson @@ -62,7 +62,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/endpoints/bookstore-grpc/api/build.gradle b/endpoints/bookstore-grpc/api/build.gradle index 34811d7a609..42c2fe40874 100644 --- a/endpoints/bookstore-grpc/api/build.gradle +++ b/endpoints/bookstore-grpc/api/build.gradle @@ -38,12 +38,12 @@ dependencies { protobuf { protoc { - artifact = 'com.google.protobuf:protoc:3.20.0' + artifact = 'com.google.protobuf:protoc:3.20.1' } plugins { grpc { - artifact = 'io.grpc:protoc-gen-grpc-java:1.45.1' + artifact = 'io.grpc:protoc-gen-grpc-java:1.46.0' } } generateProtoTasks { diff --git a/endpoints/getting-started-grpc/api/build.gradle b/endpoints/getting-started-grpc/api/build.gradle index 1421f3a4096..68d6d057665 100644 --- a/endpoints/getting-started-grpc/api/build.gradle +++ b/endpoints/getting-started-grpc/api/build.gradle @@ -40,7 +40,7 @@ dependencies { protobuf { protoc { - artifact = 'com.google.protobuf:protoc:3.20.0' + artifact = 'com.google.protobuf:protoc:3.20.1' } plugins { diff --git a/endpoints/getting-started/clients/pom.xml b/endpoints/getting-started/clients/pom.xml index c77433153fd..086c14202c7 100644 --- a/endpoints/getting-started/clients/pom.xml +++ b/endpoints/getting-started/clients/pom.xml @@ -33,7 +33,7 @@ com.auth0 java-jwt - 3.19.1 + 3.19.2 diff --git a/endpoints/getting-started/pom.xml b/endpoints/getting-started/pom.xml index cf04bcecb25..3392f5eefef 100644 --- a/endpoints/getting-started/pom.xml +++ b/endpoints/getting-started/pom.xml @@ -68,7 +68,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 diff --git a/endpoints/multiple-versions/pom.xml b/endpoints/multiple-versions/pom.xml index 898870feb3b..270fc3f20cc 100644 --- a/endpoints/multiple-versions/pom.xml +++ b/endpoints/multiple-versions/pom.xml @@ -38,7 +38,7 @@ 2.6 - 2.4.1 + 2.4.2 9.4.44.v20210927 false @@ -70,6 +70,11 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin diff --git a/flexible/analytics/pom.xml b/flexible/analytics/pom.xml index b7f1de664a4..5f057167f82 100644 --- a/flexible/analytics/pom.xml +++ b/flexible/analytics/pom.xml @@ -34,7 +34,7 @@ 1.8 1.8 - 2.4.1 + 2.4.2 9.4.44.v20210927 false @@ -57,6 +57,11 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin diff --git a/flexible/async-rest/pom.xml b/flexible/async-rest/pom.xml index 2ad87d89c1f..b4e1f5d4a9d 100644 --- a/flexible/async-rest/pom.xml +++ b/flexible/async-rest/pom.xml @@ -36,7 +36,7 @@ false - 2.4.1 + 2.4.2 9.4.44.v20210927 1.8 @@ -44,6 +44,11 @@ + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + org.eclipse.jetty jetty-maven-plugin diff --git a/flexible/cloudsql/pom.xml b/flexible/cloudsql/pom.xml index dd7ee4cbd63..07e0e744f44 100644 --- a/flexible/cloudsql/pom.xml +++ b/flexible/cloudsql/pom.xml @@ -56,12 +56,12 @@ com.google.api-client google-api-client - 1.33.4 + 1.34.1 com.google.api-client google-api-client-appengine - 1.33.4 + 1.34.0 com.google.api-client @@ -79,7 +79,7 @@ mysql mysql-connector-java - 8.0.28 + 8.0.29 com.google.cloud.sql @@ -100,10 +100,15 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/flexible/cloudstorage/pom.xml b/flexible/cloudstorage/pom.xml index 181bb8dbf8e..3c1ca6ec892 100644 --- a/flexible/cloudstorage/pom.xml +++ b/flexible/cloudstorage/pom.xml @@ -36,7 +36,7 @@ false - 2.4.1 + 2.4.2 9.4.44.v20210927 @@ -75,6 +75,11 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin diff --git a/flexible/cron/pom.xml b/flexible/cron/pom.xml index ea1ab71f2ff..85ee4aca8fd 100644 --- a/flexible/cron/pom.xml +++ b/flexible/cron/pom.xml @@ -36,7 +36,7 @@ false - 2.4.1 + 2.4.2 9.4.44.v20210927 @@ -54,6 +54,11 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin diff --git a/flexible/datastore/pom.xml b/flexible/datastore/pom.xml index 2434fc683ea..e4d384332d7 100644 --- a/flexible/datastore/pom.xml +++ b/flexible/datastore/pom.xml @@ -36,7 +36,7 @@ false - 2.4.1 + 2.4.2 9.4.44.v20210927 @@ -75,6 +75,11 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin diff --git a/flexible/disk/pom.xml b/flexible/disk/pom.xml index a79a98c97cd..b936a875454 100644 --- a/flexible/disk/pom.xml +++ b/flexible/disk/pom.xml @@ -36,7 +36,7 @@ false - 2.4.1 + 2.4.2 9.4.44.v20210927 @@ -53,6 +53,11 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin diff --git a/flexible/errorreporting/pom.xml b/flexible/errorreporting/pom.xml index 5ff163b77a2..ff487cee57c 100644 --- a/flexible/errorreporting/pom.xml +++ b/flexible/errorreporting/pom.xml @@ -31,7 +31,7 @@ - 2.4.1 + 2.4.2 1.8 1.8 false @@ -57,6 +57,11 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin diff --git a/flexible/extending-runtime/pom.xml b/flexible/extending-runtime/pom.xml index 1af24568690..a666a3a7c98 100644 --- a/flexible/extending-runtime/pom.xml +++ b/flexible/extending-runtime/pom.xml @@ -36,7 +36,7 @@ false - 2.4.1 + 2.4.2 9.4.44.v20210927 @@ -53,6 +53,11 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin diff --git a/flexible/gaeinfo/pom.xml b/flexible/gaeinfo/pom.xml index 1e5bc9dc5ff..66d401efd68 100644 --- a/flexible/gaeinfo/pom.xml +++ b/flexible/gaeinfo/pom.xml @@ -94,7 +94,7 @@ Copyright 2017 Google Inc. com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/flexible/helloworld-springboot/pom.xml b/flexible/helloworld-springboot/pom.xml index 808b661df82..d5300f8d64c 100644 --- a/flexible/helloworld-springboot/pom.xml +++ b/flexible/helloworld-springboot/pom.xml @@ -78,7 +78,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG diff --git a/flexible/helloworld/build.gradle b/flexible/helloworld/build.gradle index 37d52f83091..1974cdf7415 100644 --- a/flexible/helloworld/build.gradle +++ b/flexible/helloworld/build.gradle @@ -18,7 +18,7 @@ buildscript { // Configuration for building mavenCentral() } dependencies { - classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.4.2' + classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.4.3' classpath 'org.akhikhl.gretty:gretty:+' } } diff --git a/flexible/helloworld/pom.xml b/flexible/helloworld/pom.xml index ae7b54a1dc2..0f9f039dd03 100644 --- a/flexible/helloworld/pom.xml +++ b/flexible/helloworld/pom.xml @@ -36,7 +36,7 @@ false - 2.4.1 + 2.4.2 9.4.44.v20210927 @@ -59,6 +59,12 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + + com.google.cloud.tools appengine-maven-plugin diff --git a/flexible/memcache/pom.xml b/flexible/memcache/pom.xml index 8b67bb7bd22..c53ec74a8cc 100644 --- a/flexible/memcache/pom.xml +++ b/flexible/memcache/pom.xml @@ -37,7 +37,7 @@ false - 2.4.1 + 2.4.2 9.4.44.v20210927 @@ -62,6 +62,12 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + + com.google.cloud.tools appengine-maven-plugin diff --git a/flexible/postgres/pom.xml b/flexible/postgres/pom.xml index 33162025558..0d9f7ca3a2c 100644 --- a/flexible/postgres/pom.xml +++ b/flexible/postgres/pom.xml @@ -56,12 +56,12 @@ com.google.api-client google-api-client - 1.33.4 + 1.34.1 com.google.api-client google-api-client-appengine - 1.33.4 + 1.34.0 com.google.api-client @@ -79,7 +79,7 @@ org.postgresql postgresql - 42.3.3 + 42.3.5 @@ -102,10 +102,15 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/flexible/pubsub/pom.xml b/flexible/pubsub/pom.xml index 8098406a014..c84f5900107 100644 --- a/flexible/pubsub/pom.xml +++ b/flexible/pubsub/pom.xml @@ -36,7 +36,7 @@ false - 2.4.1 + 2.4.2 9.4.44.v20210927 @@ -64,7 +64,7 @@ com.google.cloud google-cloud-pubsub - 1.116.3 + 1.116.4 com.google.cloud @@ -103,6 +103,11 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin diff --git a/flexible/sparkjava/pom.xml b/flexible/sparkjava/pom.xml index 0cd0620f6fd..5c000f99a31 100644 --- a/flexible/sparkjava/pom.xml +++ b/flexible/sparkjava/pom.xml @@ -36,7 +36,7 @@ limitations under the License. 1.8 1.8 - 2.4.1 + 2.4.2 ${project.build.directory}/spark-1.0-jar-with-dependencies.jar diff --git a/flexible/static-files/pom.xml b/flexible/static-files/pom.xml index f96aa3e3073..bfcada7cf6a 100644 --- a/flexible/static-files/pom.xml +++ b/flexible/static-files/pom.xml @@ -36,7 +36,7 @@ false - 2.4.1 + 2.4.2 9.4.44.v20210927 @@ -53,6 +53,16 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin diff --git a/flexible/twilio/pom.xml b/flexible/twilio/pom.xml index a94f49ee9ff..85075f89d69 100644 --- a/flexible/twilio/pom.xml +++ b/flexible/twilio/pom.xml @@ -36,7 +36,7 @@ false - 2.4.1 + 2.4.2 9.4.44.v20210927 @@ -60,6 +60,11 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin diff --git a/flexible/websocket-jetty/pom.xml b/flexible/websocket-jetty/pom.xml index 683d8dfb51a..40c7ef68372 100644 --- a/flexible/websocket-jetty/pom.xml +++ b/flexible/websocket-jetty/pom.xml @@ -72,11 +72,16 @@ ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/flexible/websocket-jsr356/pom.xml b/flexible/websocket-jsr356/pom.xml index efe25e579a8..26913fbab68 100644 --- a/flexible/websocket-jsr356/pom.xml +++ b/flexible/websocket-jsr356/pom.xml @@ -69,11 +69,16 @@ limitations under the License. ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG GCLOUD_CONFIG diff --git a/functions/concepts/after-timeout/pom.xml b/functions/concepts/after-timeout/pom.xml index fe9bcabda2e..a04d890f70f 100644 --- a/functions/concepts/after-timeout/pom.xml +++ b/functions/concepts/after-timeout/pom.xml @@ -61,7 +61,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/concepts/env-vars/pom.xml b/functions/concepts/env-vars/pom.xml index ea1d4137b33..6135b52d424 100644 --- a/functions/concepts/env-vars/pom.xml +++ b/functions/concepts/env-vars/pom.xml @@ -55,7 +55,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/concepts/execution-count/pom.xml b/functions/concepts/execution-count/pom.xml index dccb3e8e926..cf1ec5853b1 100644 --- a/functions/concepts/execution-count/pom.xml +++ b/functions/concepts/execution-count/pom.xml @@ -55,7 +55,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test @@ -68,7 +68,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/concepts/file-system/pom.xml b/functions/concepts/file-system/pom.xml index 650fb337137..10032288559 100644 --- a/functions/concepts/file-system/pom.xml +++ b/functions/concepts/file-system/pom.xml @@ -55,7 +55,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/concepts/lazy-fields/pom.xml b/functions/concepts/lazy-fields/pom.xml index c37ff0980f3..b446a676772 100644 --- a/functions/concepts/lazy-fields/pom.xml +++ b/functions/concepts/lazy-fields/pom.xml @@ -55,7 +55,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/concepts/retry-pubsub/pom.xml b/functions/concepts/retry-pubsub/pom.xml index 2dded79704c..bd80719ebc7 100644 --- a/functions/concepts/retry-pubsub/pom.xml +++ b/functions/concepts/retry-pubsub/pom.xml @@ -69,7 +69,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/concepts/retry-timeout/pom.xml b/functions/concepts/retry-timeout/pom.xml index c9316662539..3b71aa15f3f 100644 --- a/functions/concepts/retry-timeout/pom.xml +++ b/functions/concepts/retry-timeout/pom.xml @@ -68,7 +68,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/concepts/scopes/pom.xml b/functions/concepts/scopes/pom.xml index 6d6163373d5..65068856812 100644 --- a/functions/concepts/scopes/pom.xml +++ b/functions/concepts/scopes/pom.xml @@ -55,7 +55,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/firebase/firestore-reactive/pom.xml b/functions/firebase/firestore-reactive/pom.xml index 389615da5f8..50df17981d8 100644 --- a/functions/firebase/firestore-reactive/pom.xml +++ b/functions/firebase/firestore-reactive/pom.xml @@ -71,7 +71,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/helloworld/groovy-helloworld/pom.xml b/functions/helloworld/groovy-helloworld/pom.xml index 763ee431026..90109716056 100644 --- a/functions/helloworld/groovy-helloworld/pom.xml +++ b/functions/helloworld/groovy-helloworld/pom.xml @@ -61,7 +61,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/helloworld/hello-error/src/main/java/functions/HelloError.java b/functions/helloworld/hello-error/src/main/java/functions/HelloError.java index 9325791f24d..f12c3ce9631 100644 --- a/functions/helloworld/hello-error/src/main/java/functions/HelloError.java +++ b/functions/helloworld/hello-error/src/main/java/functions/HelloError.java @@ -31,11 +31,11 @@ public class HelloError implements HttpFunction { @Override public void service(HttpRequest request, HttpResponse response) throws IOException { - // These will NOT be reported to Stackdriver error reporting + // These will NOT be reported to Error Reporting System.err.println("I failed you"); logger.severe("I failed you"); - // This WILL be reported to Stackdriver error reporting + // This WILL be reported to Error Reporting throw new RuntimeException("I failed you"); } } diff --git a/functions/helloworld/hello-http-gradle/build.gradle b/functions/helloworld/hello-http-gradle/build.gradle index 0b5f8ae154b..ccb1333c222 100644 --- a/functions/helloworld/hello-http-gradle/build.gradle +++ b/functions/helloworld/hello-http-gradle/build.gradle @@ -32,7 +32,7 @@ dependencies { testImplementation 'com.google.cloud.functions:functions-framework-api:1.0.4' testImplementation 'junit:junit:4.13.2' testImplementation 'com.google.truth:truth:1.1.3' - testImplementation 'org.mockito:mockito-core:4.4.0' + testImplementation 'org.mockito:mockito-core:4.5.1' } jar { diff --git a/functions/helloworld/hello-http/pom.xml b/functions/helloworld/hello-http/pom.xml index 0beb1ce7e0b..cbc8c0228ae 100644 --- a/functions/helloworld/hello-http/pom.xml +++ b/functions/helloworld/hello-http/pom.xml @@ -89,7 +89,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/helloworld/helloworld-gradle/build.gradle b/functions/helloworld/helloworld-gradle/build.gradle index e1987d1e70d..a3741bf10a9 100644 --- a/functions/helloworld/helloworld-gradle/build.gradle +++ b/functions/helloworld/helloworld-gradle/build.gradle @@ -38,7 +38,7 @@ dependencies { testImplementation 'com.google.cloud.functions:functions-framework-api:1.0.4' testImplementation 'junit:junit:4.13.2' testImplementation 'com.google.truth:truth:1.1.3' - testImplementation 'org.mockito:mockito-core:4.4.0' + testImplementation 'org.mockito:mockito-core:4.5.1' // [START functions_example_pom_dependencies] // [START functions_gradle_add_dependencies] diff --git a/functions/helloworld/helloworld/pom.xml b/functions/helloworld/helloworld/pom.xml index 3acecf97f7a..cfefa22fa7c 100644 --- a/functions/helloworld/helloworld/pom.xml +++ b/functions/helloworld/helloworld/pom.xml @@ -71,7 +71,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/helloworld/kotlin-hello-pubsub/pom.xml b/functions/helloworld/kotlin-hello-pubsub/pom.xml index c92ed4359ff..27960306fd6 100644 --- a/functions/helloworld/kotlin-hello-pubsub/pom.xml +++ b/functions/helloworld/kotlin-hello-pubsub/pom.xml @@ -34,7 +34,7 @@ 11 11 UTF-8 - 1.6.20 + 1.6.21 diff --git a/functions/helloworld/kotlin-helloworld/pom.xml b/functions/helloworld/kotlin-helloworld/pom.xml index 6906a675618..8489013a09d 100644 --- a/functions/helloworld/kotlin-helloworld/pom.xml +++ b/functions/helloworld/kotlin-helloworld/pom.xml @@ -37,7 +37,7 @@ 11 11 UTF-8 - 1.6.20 + 1.6.21 @@ -65,7 +65,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/helloworld/scala-helloworld/pom.xml b/functions/helloworld/scala-helloworld/pom.xml index abc76c2bc65..be052ba5bcf 100644 --- a/functions/helloworld/scala-helloworld/pom.xml +++ b/functions/helloworld/scala-helloworld/pom.xml @@ -59,7 +59,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/http/cors-enabled-auth/pom.xml b/functions/http/cors-enabled-auth/pom.xml index 6165450d449..03e978b70a2 100644 --- a/functions/http/cors-enabled-auth/pom.xml +++ b/functions/http/cors-enabled-auth/pom.xml @@ -61,7 +61,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/http/cors-enabled/pom.xml b/functions/http/cors-enabled/pom.xml index 1c3fb440c5d..ba979ff1843 100644 --- a/functions/http/cors-enabled/pom.xml +++ b/functions/http/cors-enabled/pom.xml @@ -61,7 +61,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/http/http-form-data/pom.xml b/functions/http/http-form-data/pom.xml index 139612e1e8b..74f569067c9 100644 --- a/functions/http/http-form-data/pom.xml +++ b/functions/http/http-form-data/pom.xml @@ -67,7 +67,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/http/http-method/pom.xml b/functions/http/http-method/pom.xml index baaa94f8b5e..05aeb2fa008 100644 --- a/functions/http/http-method/pom.xml +++ b/functions/http/http-method/pom.xml @@ -60,7 +60,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/http/parse-content-type/pom.xml b/functions/http/parse-content-type/pom.xml index e047f5a7b8a..550c7bf9cad 100644 --- a/functions/http/parse-content-type/pom.xml +++ b/functions/http/parse-content-type/pom.xml @@ -66,7 +66,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/http/parse-xml/pom.xml b/functions/http/parse-xml/pom.xml index 10b16049dbe..1dd5d139718 100644 --- a/functions/http/parse-xml/pom.xml +++ b/functions/http/parse-xml/pom.xml @@ -66,7 +66,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/http/send-http-request/pom.xml b/functions/http/send-http-request/pom.xml index 4a44b13fa4b..27e57ea0c8b 100644 --- a/functions/http/send-http-request/pom.xml +++ b/functions/http/send-http-request/pom.xml @@ -61,7 +61,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/logging/stackdriver-logging/pom.xml b/functions/logging/stackdriver-logging/pom.xml index 6117a9b4ab5..46e9835b679 100644 --- a/functions/logging/stackdriver-logging/pom.xml +++ b/functions/logging/stackdriver-logging/pom.xml @@ -105,6 +105,8 @@ 3.0.0-M5 + + --add-opens java.base/java.time=ALL-UNNAMED **/*Test.java diff --git a/functions/pubsub/publish-message/pom.xml b/functions/pubsub/publish-message/pom.xml index 85cf7a03847..3ca6c722a13 100644 --- a/functions/pubsub/publish-message/pom.xml +++ b/functions/pubsub/publish-message/pom.xml @@ -83,7 +83,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/slack/pom.xml b/functions/slack/pom.xml index 065e0240812..1d36495df43 100644 --- a/functions/slack/pom.xml +++ b/functions/slack/pom.xml @@ -66,7 +66,7 @@ com.google.http-client google-http-client-jackson2 - 1.41.6 + 1.41.8 @@ -77,7 +77,7 @@ com.slack.api slack-app-backend - 1.21.1 + 1.22.1 @@ -90,7 +90,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/functions/spanner/pom.xml b/functions/spanner/pom.xml index 224db35bb44..8f8049c3899 100644 --- a/functions/spanner/pom.xml +++ b/functions/spanner/pom.xml @@ -66,7 +66,7 @@ org.mockito mockito-core - 4.4.0 + 4.5.1 test diff --git a/healthcare/v1/pom.xml b/healthcare/v1/pom.xml index 98dbbcde3f2..5375492d8d7 100644 --- a/healthcare/v1/pom.xml +++ b/healthcare/v1/pom.xml @@ -75,7 +75,7 @@ com.google.http-client google-http-client-jackson2 - 1.41.6 + 1.41.8 com.google.apis @@ -85,7 +85,7 @@ com.google.api-client google-api-client - 1.33.4 + 1.34.1 com.google.auth diff --git a/iam/api-client/pom.xml b/iam/api-client/pom.xml index cb236ec8029..3343322171d 100644 --- a/iam/api-client/pom.xml +++ b/iam/api-client/pom.xml @@ -50,13 +50,13 @@ com.google.http-client google-http-client-jackson2 - 1.41.6 + 1.41.8 com.google.apis google-api-services-iam - v1-rev20220331-1.32.1 + v1-rev20220428-1.32.1 @@ -71,7 +71,7 @@ com.google.cloud google-cloud-policy-troubleshooter - 1.0.3 + 1.0.4 diff --git a/iot/api-client/codelab/manager/pom.xml b/iot/api-client/codelab/manager/pom.xml index 0f940802a5b..a481c1d34c5 100644 --- a/iot/api-client/codelab/manager/pom.xml +++ b/iot/api-client/codelab/manager/pom.xml @@ -67,12 +67,12 @@ com.google.apis google-api-services-cloudiot - v1-rev20211108-1.32.1 + v1-rev20220425-1.32.1 com.google.cloud google-cloud-pubsub - 1.116.3 + 1.116.4 com.google.cloud @@ -87,7 +87,7 @@ com.google.api-client google-api-client - 1.33.4 + 1.34.1 commons-cli diff --git a/iot/api-client/end-to-end-example/pom.xml b/iot/api-client/end-to-end-example/pom.xml index 12dfc42258e..ec2a74c338e 100644 --- a/iot/api-client/end-to-end-example/pom.xml +++ b/iot/api-client/end-to-end-example/pom.xml @@ -58,12 +58,12 @@ com.google.apis google-api-services-cloudiot - v1-rev20211108-1.32.1 + v1-rev20220425-1.32.1 com.google.cloud google-cloud-pubsub - 1.116.3 + 1.116.4 com.google.auth @@ -78,7 +78,7 @@ com.google.api-client google-api-client - 1.33.4 + 1.34.1 commons-cli @@ -88,12 +88,12 @@ com.google.api-client google-api-client-jackson2 - 1.33.4 + 1.34.1 com.google.http-client google-http-client-jackson2 - 1.41.6 + 1.41.8 diff --git a/iot/api-client/manager/pom.xml b/iot/api-client/manager/pom.xml index 149273c119a..cac5a6f1c72 100644 --- a/iot/api-client/manager/pom.xml +++ b/iot/api-client/manager/pom.xml @@ -62,12 +62,12 @@ com.google.apis google-api-services-cloudiot - v1-rev20211108-1.32.1 + v1-rev20220425-1.32.1 com.google.cloud google-cloud-pubsub - 1.116.3 + 1.116.4 com.google.cloud @@ -87,7 +87,7 @@ com.google.api-client google-api-client - 1.33.4 + 1.34.1 commons-cli @@ -97,12 +97,12 @@ com.google.api-client google-api-client-jackson2 - 1.33.4 + 1.34.1 com.google.http-client google-http-client-jackson2 - 1.41.6 + 1.41.8 diff --git a/jobs/v3/pom.xml b/jobs/v3/pom.xml index f5fb8d29bb6..94b702904db 100644 --- a/jobs/v3/pom.xml +++ b/jobs/v3/pom.xml @@ -42,7 +42,7 @@ com.google.http-client google-http-client-jackson2 - 1.41.6 + 1.41.8 diff --git a/jobs/v3/src/test/java/SampleTests.java b/jobs/v3/src/test/java/SampleTests.java index 48099d514da..287f9c8eac4 100644 --- a/jobs/v3/src/test/java/SampleTests.java +++ b/jobs/v3/src/test/java/SampleTests.java @@ -54,7 +54,7 @@ public void autoCompleteSampleTest() throws Exception { assertThat(bout.toString()) .containsMatch( ".*completionResults.*\"suggestion\":" - + "\"Google Search\",\"type\":\"COMPANY_NAME\"}.*\n" + + "\"Google.*\",\"type\":\"COMPANY_NAME\"}.*\n" + ".*completionResults.*\"suggestion\"" + ":\"Software Engineer\",\"type\":\"JOB_TITLE\".*\n" + ".*completionResults.*\"suggestion\"" diff --git a/jobs/v4/pom.xml b/jobs/v4/pom.xml index b3f80b9d0ab..a7e20928860 100644 --- a/jobs/v4/pom.xml +++ b/jobs/v4/pom.xml @@ -55,7 +55,7 @@ com.google.http-client google-http-client-jackson2 - 1.41.6 + 1.41.8 diff --git a/kms/pom.xml b/kms/pom.xml index 2b90b4d7ffa..40f91c506a6 100644 --- a/kms/pom.xml +++ b/kms/pom.xml @@ -25,19 +25,19 @@ com.google.cloud google-cloud-kms - 2.4.2 + 2.4.4 com.google.protobuf protobuf-java-util - 3.20.0 + 3.20.1 com.google.protobuf protobuf-java - 3.20.0 + 3.20.1 diff --git a/language/cloud-client/pom.xml b/language/cloud-client/pom.xml index a603ff7a4c1..15411b15e89 100644 --- a/language/cloud-client/pom.xml +++ b/language/cloud-client/pom.xml @@ -39,7 +39,7 @@ com.google.cloud google-cloud-language - 2.1.10 + 2.1.11 com.google.guava diff --git a/media/transcoder/pom.xml b/media/transcoder/pom.xml index 3d1e735778c..9cfdc44f36e 100644 --- a/media/transcoder/pom.xml +++ b/media/transcoder/pom.xml @@ -53,7 +53,7 @@ com.google.cloud google-cloud-video-transcoder - 1.0.3 + 1.0.4 com.google.cloud @@ -76,7 +76,7 @@ com.google.cloud google-cloud-storage - 2.6.0 + 2.6.1 \ No newline at end of file diff --git a/memorystore/redis/pom.xml b/memorystore/redis/pom.xml index 3a043ceac69..1067021c90c 100644 --- a/memorystore/redis/pom.xml +++ b/memorystore/redis/pom.xml @@ -49,7 +49,7 @@ redis.clients jedis - 4.2.1 + 4.2.3 @@ -58,6 +58,11 @@ servlet/target/visitcounter-1.0-SNAPSHOT/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + org.eclipse.jetty @@ -69,7 +74,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 diff --git a/mlengine/online-prediction/pom.xml b/mlengine/online-prediction/pom.xml index 9409f14e9f4..0d0e103d4e8 100644 --- a/mlengine/online-prediction/pom.xml +++ b/mlengine/online-prediction/pom.xml @@ -76,7 +76,7 @@ limitations under the License. com.google.http-client google-http-client-jackson2 - 1.41.6 + 1.41.8 diff --git a/opencensus/pom.xml b/opencensus/pom.xml index c73107e926b..f9140f04ee9 100644 --- a/opencensus/pom.xml +++ b/opencensus/pom.xml @@ -36,7 +36,7 @@ 1.8 1.8 UTF-8 - 0.31.0 + 0.31.1 diff --git a/pubsub/spring/build.gradle b/pubsub/spring/build.gradle index f69c9f7aae3..6771f659495 100644 --- a/pubsub/spring/build.gradle +++ b/pubsub/spring/build.gradle @@ -34,11 +34,11 @@ description = 'Spring Cloud GCP Pub/Sub Code Sample' java.sourceCompatibility = JavaVersion.VERSION_1_8 dependencies { - implementation 'com.github.spotbugs:spotbugs-annotations:4.6.0' + implementation 'com.github.spotbugs:spotbugs-annotations:4.7.0' implementation 'org.springframework.boot:spring-boot-starter-web:2.5.7' - implementation 'com.google.cloud:spring-cloud-gcp-starter-pubsub:3.2.0' - implementation 'org.springframework.integration:spring-integration-core:5.5.10' - implementation 'com.google.cloud:spring-cloud-gcp-pubsub-stream-binder:3.2.0' + implementation 'com.google.cloud:spring-cloud-gcp-starter-pubsub:3.2.1' + implementation 'org.springframework.integration:spring-integration-core:5.5.11' + implementation 'com.google.cloud:spring-cloud-gcp-pubsub-stream-binder:3.2.1' testImplementation 'junit:junit:4.13.2' testImplementation 'com.google.truth:truth:1.1.3' testImplementation 'org.springframework.boot:spring-boot-test:2.5.7' diff --git a/pubsub/spring/pom.xml b/pubsub/spring/pom.xml index 96b0160778b..4f647e074ea 100644 --- a/pubsub/spring/pom.xml +++ b/pubsub/spring/pom.xml @@ -55,7 +55,7 @@ com.google.cloud spring-cloud-gcp-dependencies - 3.2.0 + 3.2.1 pom import diff --git a/pubsub/streaming-analytics/build.gradle b/pubsub/streaming-analytics/build.gradle index 73ae05f8a8d..6da227e5235 100644 --- a/pubsub/streaming-analytics/build.gradle +++ b/pubsub/streaming-analytics/build.gradle @@ -33,10 +33,10 @@ repositories { } } -def beamVersion = '2.37.0' +def beamVersion = '2.38.0' def slf4jVersion = '1.7.36' dependencies { - implementation 'com.github.spotbugs:spotbugs-annotations:4.6.0' + implementation 'com.github.spotbugs:spotbugs-annotations:4.7.0' implementation "org.apache.beam:beam-sdks-java-core:${beamVersion}" implementation "org.apache.beam:beam-sdks-java-io-google-cloud-platform:${beamVersion}" implementation "org.apache.beam:beam-examples-java:${beamVersion}" diff --git a/pubsub/streaming-analytics/pom.xml b/pubsub/streaming-analytics/pom.xml index 854c362344e..ded6d9d9686 100644 --- a/pubsub/streaming-analytics/pom.xml +++ b/pubsub/streaming-analytics/pom.xml @@ -34,7 +34,7 @@ 1.8 UTF-8 - 2.37.0 + 2.38.0 3.10.1 3.0.0 diff --git a/pubsublite/streaming-analytics/build.gradle b/pubsublite/streaming-analytics/build.gradle index 55efe9e55bd..ccf71d36cf0 100644 --- a/pubsublite/streaming-analytics/build.gradle +++ b/pubsublite/streaming-analytics/build.gradle @@ -33,10 +33,10 @@ repositories { } } -def beamVersion = '2.37.0' +def beamVersion = '2.38.0' def slf4jVersion = '1.7.36' dependencies { - implementation 'com.github.spotbugs:spotbugs-annotations:4.6.0' + implementation 'com.github.spotbugs:spotbugs-annotations:4.7.0' implementation "org.slf4j:slf4j-api:${slf4jVersion}" implementation "org.slf4j:slf4j-jdk14:${slf4jVersion}" implementation "org.apache.beam:beam-sdks-java-core:${beamVersion}" @@ -44,10 +44,10 @@ dependencies { implementation "org.apache.beam:beam-examples-java:${beamVersion}" runtimeOnly "org.apache.beam:beam-runners-direct-java:${beamVersion}" runtimeOnly "org.apache.beam:beam-runners-google-cloud-dataflow-java:${beamVersion}" - testImplementation 'com.google.api-client:google-api-client:1.33.4' + testImplementation 'com.google.api-client:google-api-client:1.34.1' testImplementation 'com.google.apis:google-api-services-dataflow:v1b3-rev20210825-1.32.1' testImplementation 'com.google.cloud:google-cloud-core:2.3.2' - testImplementation 'com.google.cloud:google-cloud-storage:2.6.0' + testImplementation 'com.google.cloud:google-cloud-storage:2.6.1' testImplementation 'com.google.truth:truth:1.1.3' testImplementation 'org.hamcrest:hamcrest-all:1.3' } diff --git a/pubsublite/streaming-analytics/pom.xml b/pubsublite/streaming-analytics/pom.xml index 2423980be93..f56b2b6cb9e 100644 --- a/pubsublite/streaming-analytics/pom.xml +++ b/pubsublite/streaming-analytics/pom.xml @@ -34,7 +34,7 @@ 11 UTF-8 - 2.37.0 + 2.38.0 3.10.1 3.0.0 @@ -131,7 +131,7 @@ com.google.api-client google-api-client - 1.33.4 + 1.34.1 diff --git a/run/endpoints-v2-backend/pom.xml b/run/endpoints-v2-backend/pom.xml index 1513c7d953a..db48b1ce74f 100755 --- a/run/endpoints-v2-backend/pom.xml +++ b/run/endpoints-v2-backend/pom.xml @@ -58,7 +58,7 @@ limitations under the License. org.springdoc springdoc-openapi-ui - 1.6.7 + 1.6.8 org.springframework.boot diff --git a/run/idp-sql/pom.xml b/run/idp-sql/pom.xml index aff1644e257..bf350832b2e 100644 --- a/run/idp-sql/pom.xml +++ b/run/idp-sql/pom.xml @@ -45,7 +45,7 @@ limitations under the License. com.google.cloud spring-cloud-gcp-dependencies - 3.2.0 + 3.2.1 pom import @@ -80,7 +80,7 @@ limitations under the License. net.logstash.logback logstash-logback-encoder - 7.0.1 + 7.1 ch.qos.logback.contrib @@ -106,7 +106,7 @@ limitations under the License. org.projectlombok lombok - 1.18.22 + 1.18.24 com.squareup.okhttp3 diff --git a/run/image-processing/pom.xml b/run/image-processing/pom.xml index c7ee97c2643..75676505988 100644 --- a/run/image-processing/pom.xml +++ b/run/image-processing/pom.xml @@ -44,7 +44,7 @@ limitations under the License. com.google.cloud spring-cloud-gcp-dependencies - 3.2.0 + 3.2.1 pom import diff --git a/run/jobs/src/main/java/com/example/JobsExample.java b/run/jobs/src/main/java/com/example/JobsExample.java index 7b5c1488c48..e81dad0e8fa 100644 --- a/run/jobs/src/main/java/com/example/JobsExample.java +++ b/run/jobs/src/main/java/com/example/JobsExample.java @@ -14,13 +14,17 @@ * limitations under the License. */ +// [START cloudrun_jobs_quickstart] + package com.example; abstract class JobsExample { // [START cloudrun_jobs_env_vars] // These values are provided automatically by the Cloud Run Jobs runtime. - private static String TASK_NUM = System.getenv().getOrDefault("TASK_NUM", "0"); - private static String ATTEMPT_NUM = System.getenv().getOrDefault("ATTEMPT_NUM", "0"); + private static String CLOUD_RUN_TASK_INDEX = + System.getenv().getOrDefault("CLOUD_RUN_TASK_INDEX", "0"); + private static String CLOUD_RUN_TASK_ATTEMPT = + System.getenv().getOrDefault("CLOUD_RUN_TASK_ATTEMPT", "0"); // User-provided environment variables private static int SLEEP_MS = Integer.parseInt(System.getenv().getOrDefault("SLEEP_MS", "0")); @@ -30,11 +34,15 @@ abstract class JobsExample { // Start script public static void main(String[] args) { - System.out.println(String.format("Starting Task #%s, Attempt #%s...", TASK_NUM, ATTEMPT_NUM)); + System.out.println( + String.format( + "Starting Task #%s, Attempt #%s...", CLOUD_RUN_TASK_INDEX, CLOUD_RUN_TASK_ATTEMPT)); try { runTask(SLEEP_MS, FAIL_RATE); } catch (RuntimeException | InterruptedException e) { - System.err.println(String.format("Task #%s, Attempt #%s failed.", TASK_NUM, ATTEMPT_NUM)); + System.err.println( + String.format( + "Task #%s, Attempt #%s failed.", CLOUD_RUN_TASK_INDEX, CLOUD_RUN_TASK_ATTEMPT)); // [START cloudrun_jobs_exit_process] // Catch error and denote process-level failure to retry Task System.exit(1); @@ -59,6 +67,7 @@ static void runTask(int sleepTime, float failureRate) throws InterruptedExceptio if (Math.random() < failureRate) { throw new RuntimeException("Task Failed."); } - System.out.println(String.format("Completed Task #%s", TASK_NUM)); + System.out.println(String.format("Completed Task #%s", CLOUD_RUN_TASK_INDEX)); } } +// [END cloudrun_jobs_quickstart] diff --git a/run/jobs/src/test/java/com/example/JobsIntegrationTests.java b/run/jobs/src/test/java/com/example/JobsIntegrationTests.java index 4d9ded0e1e6..41ade762402 100644 --- a/run/jobs/src/test/java/com/example/JobsIntegrationTests.java +++ b/run/jobs/src/test/java/com/example/JobsIntegrationTests.java @@ -95,8 +95,8 @@ public void generatesLogs() throws Exception { calendar.add(Calendar.MINUTE, -5); DateFormat rfc3339 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); String logFilter = - "resource.type = \"cloud_run_revision\"" - + " resource.labels.service_name = \"" + "resource.type = \"cloud_run_job\"" + + " resource.labels.job_name = \"" + service + "\" resource.labels.location = \"us-central1\"" + " timestamp>=\"" diff --git a/run/logging-manual/pom.xml b/run/logging-manual/pom.xml index 7a492e3b549..3697a8d8365 100644 --- a/run/logging-manual/pom.xml +++ b/run/logging-manual/pom.xml @@ -44,7 +44,7 @@ limitations under the License. net.logstash.logback logstash-logback-encoder - 7.0.1 + 7.1 ch.qos.logback diff --git a/secretmanager/pom.xml b/secretmanager/pom.xml index 21001137c9e..341da9e1d2a 100644 --- a/secretmanager/pom.xml +++ b/secretmanager/pom.xml @@ -47,7 +47,7 @@ com.google.protobuf protobuf-java-util - 3.20.0 + 3.20.1 diff --git a/session-handling/pom.xml b/session-handling/pom.xml index 75f5678814f..bc5270bfd7b 100644 --- a/session-handling/pom.xml +++ b/session-handling/pom.xml @@ -66,7 +66,7 @@ Copyright 2019 Google LLC io.opencensus opencensus-contrib-http-util - 0.31.0 + 0.31.1 @@ -85,7 +85,7 @@ Copyright 2019 Google LLC org.seleniumhq.selenium selenium-chrome-driver - 4.1.3 + 4.1.4 test @@ -96,6 +96,11 @@ Copyright 2019 Google LLC ${project.build.directory}/${project.build.finalName}/WEB-INF/classes + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + org.eclipse.jetty jetty-maven-plugin diff --git a/spanner/jdbc/pom.xml b/spanner/jdbc/pom.xml index 51ed06262d3..cba5528504d 100644 --- a/spanner/jdbc/pom.xml +++ b/spanner/jdbc/pom.xml @@ -28,7 +28,7 @@ com.google.cloud libraries-bom - 25.0.0 + 25.1.0 pom import @@ -41,12 +41,6 @@ com.google.cloud google-cloud-spanner-jdbc - 2.5.11 - - - com.google.cloud - google-cloud-spanner - 6.17.3 diff --git a/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgBatchDmlSample.java b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgBatchDmlSample.java new file mode 100644 index 00000000000..e1c5cc6395e --- /dev/null +++ b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgBatchDmlSample.java @@ -0,0 +1,68 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.spanner.jdbc; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Arrays; + +class PgBatchDmlSample { + + static void pgBatchDml() throws SQLException { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + pgBatchDml(projectId, instanceId, databaseId); + } + + static void pgBatchDml(String projectId, String instanceId, String databaseId) + throws SQLException { + // Create a JDBC connection to the database. A connection can be reused to execute multiple + // statements. After completing all of your statements, call the "close" method on the + // connection to safely clean up any remaining resources. + try (Connection connection = + DriverManager.getConnection( + String.format( + "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s", + projectId, instanceId, databaseId))) { + // Spanner PostgreSQL supports BatchDML statements. This will batch multiple DML statements + // into one request, which reduces the number of round trips that is needed for multiple DML + // statements. Use the standard JDBC PreparedStatement batching feature to batch multiple DML + // statements together. + try (PreparedStatement statement = + connection.prepareStatement( + "INSERT INTO Singers (SingerId, FirstName, LastName) " + "VALUES (?, ?, ?)")) { + statement.setLong(1, 10L); + statement.setString(2, "Alice"); + statement.setString(3, "Henderson"); + statement.addBatch(); + + statement.setLong(1, 11L); + statement.setString(2, "Bruce"); + statement.setString(3, "Allison"); + statement.addBatch(); + + int[] updateCounts = statement.executeBatch(); + int totalUpdateCount = Arrays.stream(updateCounts).sum(); + System.out.printf("Inserted %d singers\n", totalUpdateCount); + } + } + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgCaseSensitivitySample.java b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgCaseSensitivitySample.java new file mode 100644 index 00000000000..caa496b8832 --- /dev/null +++ b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgCaseSensitivitySample.java @@ -0,0 +1,128 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.spanner.jdbc; + +import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.jdbc.CloudSpannerJdbcConnection; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collections; + +class PgCaseSensitivitySample { + + static void pgCaseSensitivity() throws SQLException { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + pgCaseSensitivity(projectId, instanceId, databaseId); + } + + static void pgCaseSensitivity(String projectId, String instanceId, String databaseId) + throws SQLException { + // Create a JDBC connection to the database. A connection can be reused to execute multiple + // statements. After completing all of your statements, call the "close" method on the + // connection to safely clean up any remaining resources. + try (Connection connection = + DriverManager.getConnection( + String.format( + "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s", + projectId, instanceId, databaseId))) { + + // Spanner PostgreSQL follows the case sensitivity rules of PostgreSQL. This means that: + // 1. Identifiers that are not double-quoted are folded to lower case. + // 2. Identifiers that are double-quoted retain their case and are case-sensitive. + // See https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS + // for more information. + + connection + .createStatement() + .execute( + "CREATE TABLE Singers (" + // SingerId will be folded to `singerid`. + + " SingerId bigint NOT NULL PRIMARY KEY," + // FirstName and LastName are double-quoted and will therefore retain their + // mixed case and are case-sensitive. This means that any statement that + // references any of these columns must use double quotes. + + " \"FirstName\" varchar(1024) NOT NULL," + + " \"LastName\" varchar(1024) NOT NULL" + + ")"); + + connection + .unwrap(CloudSpannerJdbcConnection.class) + .write( + Collections.singleton( + Mutation.newInsertBuilder("Singers") + .set("singerid") + .to(1L) + // Column names in mutations are always case-insensitive, regardless whether + // the columns were double-quoted or not during creation. + .set("firstname") + .to("Bruce") + .set("lastname") + .to("Allison") + .build())); + + try (ResultSet singers = connection + .createStatement() + .executeQuery("SELECT SingerId, \"FirstName\", \"LastName\" FROM Singers")) { + while (singers.next()) { + System.out.printf( + "SingerId: %d, FirstName: %s, LastName: %s\n", + // SingerId is automatically folded to lower case. Accessing the column by its name in + // a result set must therefore use all lower-case letters. + singers.getLong("singerid"), + // FirstName and LastName were double-quoted during creation, and retain their mixed + // case when returned in a result set. + singers.getString("FirstName"), + singers.getString("LastName")); + } + } + + // Aliases are also identifiers, and specifying an alias in double quotes will make the alias + // retain its case. + try (ResultSet singers = + connection + .createStatement() + .executeQuery( + "SELECT " + + "singerid AS \"SingerId\", " + + "\"FirstName\" || ' ' || \"LastName\" AS \"FullName\" " + + "FROM Singers")) { + while (singers.next()) { + System.out.printf( + "SingerId: %d, FullName: %s\n", + // The aliases are double-quoted and therefore retains their mixed case. + singers.getLong("SingerId"), singers.getString("FullName")); + } + } + + // DML statements must also follow the PostgreSQL case rules. + try (PreparedStatement statement = + connection.prepareStatement( + "INSERT INTO Singers (SingerId, \"FirstName\", \"LastName\") " + + "VALUES (?, ?, ?)")) { + statement.setLong(1, 2L); + statement.setString(2, "Alice"); + statement.setString(3, "Bruxelles"); + } + } + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgCastDataTypeSample.java b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgCastDataTypeSample.java new file mode 100644 index 00000000000..5b3036dc135 --- /dev/null +++ b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgCastDataTypeSample.java @@ -0,0 +1,75 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.spanner.jdbc; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.util.Base64; + +class PgCastDataTypeSample { + + static void pgCastDataType() throws SQLException { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + pgCastDataType(projectId, instanceId, databaseId); + } + + static void pgCastDataType(String projectId, String instanceId, String databaseId) + throws SQLException { + // Create a JDBC connection to the database. A connection can be reused to execute multiple + // statements. After completing all of your statements, call the "close" method on the + // connection to safely clean up any remaining resources. + try (Connection connection = + DriverManager.getConnection( + String.format( + "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s", + projectId, instanceId, databaseId))) { + // The `::` cast operator can be used to cast from one data type to another. + try (ResultSet resultSet = + connection + .createStatement() + .executeQuery( + "select 1::varchar as str, '2'::bigint as bigint, 3::numeric as num," + + "'4'::bytea as bytes, 5::float as float, 'true'::bool as bool, " + + "'2021-11-03T09:35:01UTC'::timestamptz as timestamp, " + + "'2022-04-25'::date as date")) { + while (resultSet.next()) { + System.out.printf("String: %s\n", resultSet.getString("str")); + System.out.printf("Bigint: %d\n", resultSet.getLong("bigint")); + System.out.printf("Numeric: %s\n", resultSet.getBigDecimal("num")); + System.out.printf( + "Bytes: %s\n", Base64.getEncoder().encodeToString(resultSet.getBytes("bytes"))); + System.out.printf("Float: %f\n", resultSet.getDouble("float")); + System.out.printf("Bool: %s\n", resultSet.getBoolean("bool")); + System.out.printf( + "Timestamp: %s\n", + OffsetDateTime.ofInstant( + Instant.ofEpochMilli(resultSet.getTimestamp("timestamp").getTime()), + ZoneId.of("UTC"))); + System.out.printf("Date: %s\n", resultSet.getDate("date")); + } + } + } + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgConnectToDatabaseSample.java b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgConnectToDatabaseSample.java new file mode 100644 index 00000000000..27e930af85e --- /dev/null +++ b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgConnectToDatabaseSample.java @@ -0,0 +1,55 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.spanner.jdbc; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; + +class PgConnectToDatabaseSample { + + static void pgConnectToDatabase() throws SQLException { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + pgConnectToDatabase(projectId, instanceId, databaseId); + } + + // Creates a JDBC connection to a Cloud Spanner database with the PostgreSQL dialect. + static void pgConnectToDatabase(String projectId, String instanceId, String databaseId) + throws SQLException { + // Connecting to a Cloud Spanner PostgreSQL database using the Spanner JDBC driver uses the same + // JDBC URL as for normal Spanner databases. The JDBC driver will automatically detect the + // dialect that is used by the underlying database. A connection can be reused to execute + // multiple statements. After completing all of your statements, call the "close" method on the + // connection to safely clean up any remaining resources. + try (Connection connection = + DriverManager.getConnection( + String.format( + "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s", + projectId, instanceId, databaseId))) { + try (ResultSet rs = connection.createStatement().executeQuery("SELECT now()")) { + while (rs.next()) { + System.out.printf( + "Connected to Cloud Spanner PostgreSQL at [%s]%n", rs.getTimestamp(1).toString()); + } + } + } + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgCreateInterleavedTableSample.java b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgCreateInterleavedTableSample.java new file mode 100644 index 00000000000..4a0a3f63cae --- /dev/null +++ b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgCreateInterleavedTableSample.java @@ -0,0 +1,69 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.spanner.jdbc; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; + +class PgCreateInterleavedTableSample { + + static void pgCreateInterleavedTable() throws SQLException { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + pgCreateInterleavedTable(projectId, instanceId, databaseId); + } + + static void pgCreateInterleavedTable(String projectId, String instanceId, String databaseId) + throws SQLException { + // Create a JDBC connection to the database. A connection can be reused to execute multiple + // statements. After completing all of your statements, call the "close" method on the + // connection to safely clean up any remaining resources. + try (Connection connection = + DriverManager.getConnection( + String.format( + "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s", + projectId, instanceId, databaseId))) { + try (Statement statement = connection.createStatement()) { + // The Spanner PostgreSQL dialect extends the PostgreSQL dialect with certain Spanner + // specific features, such as interleaved tables. + // See + // https://cloud.google.com/spanner/docs/postgresql/data-definition-language#create_table + // for the full CREATE TABLE syntax. The tables are created in one batch by adding the + // individual DDL statements to a JDBC batch and then executed as a single batch. + statement.addBatch( + "CREATE TABLE Singers (" + + " SingerId bigint NOT NULL PRIMARY KEY," + + " FirstName varchar(1024) NOT NULL," + + " LastName varchar(1024) NOT NULL" + + ")"); + statement.addBatch( + "CREATE TABLE Albums (" + + " SingerId bigint NOT NULL," + + " AlbumId bigint NOT NULL," + + " Title varchar(1024) NOT NULL," + + " PRIMARY KEY (SingerId, AlbumId)" + + ") INTERLEAVE IN PARENT Singers ON DELETE CASCADE"); + statement.executeBatch(); + System.out.println("Created Singers and Albums tables"); + } + } + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgDmlWithParametersSample.java b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgDmlWithParametersSample.java new file mode 100644 index 00000000000..d03187c03ed --- /dev/null +++ b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgDmlWithParametersSample.java @@ -0,0 +1,59 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.spanner.jdbc; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +class PgDmlWithParametersSample { + + static void pgDmlWithParameters() throws SQLException { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + pgDmlWithParameters(projectId, instanceId, databaseId); + } + + static void pgDmlWithParameters(String projectId, String instanceId, String databaseId) + throws SQLException { + // Create a JDBC connection to the database. A connection can be reused to execute multiple + // statements. After completing all of your statements, call the "close" method on the + // connection to safely clean up any remaining resources. + try (Connection connection = + DriverManager.getConnection( + String.format( + "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s", + projectId, instanceId, databaseId))) { + try (PreparedStatement statement = + connection.prepareStatement( + "INSERT INTO Singers (SingerId, FirstName, LastName) " + + "VALUES (?, ?, ?), (?, ?, ?)")) { + statement.setLong(1, 10L); + statement.setString(2, "Alice"); + statement.setString(3, "Henderson"); + statement.setLong(4, 11L); + statement.setString(5, "Bruce"); + statement.setString(6, "Allison"); + int updateCount = statement.executeUpdate(); + System.out.printf("Inserted %d singers\n", updateCount); + } + } + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgFunctionsSample.java b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgFunctionsSample.java new file mode 100644 index 00000000000..77209c7f211 --- /dev/null +++ b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgFunctionsSample.java @@ -0,0 +1,62 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.spanner.jdbc; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneId; + +class PgFunctionsSample { + + static void pgFunctions() throws SQLException { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + pgFunctions(projectId, instanceId, databaseId); + } + + static void pgFunctions(String projectId, String instanceId, String databaseId) + throws SQLException { + // Create a JDBC connection to the database. A connection can be reused to execute multiple + // statements. After completing all of your statements, call the "close" method on the + // connection to safely clean up any remaining resources. + try (Connection connection = + DriverManager.getConnection( + String.format( + "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s", + projectId, instanceId, databaseId))) { + // Use the PostgreSQL `to_timestamp` function to convert a number of seconds since epoch to a + // timestamp. 1284352323 seconds = Monday, September 13, 2010 4:32:03 AM. + try (ResultSet resultSet = + connection.createStatement().executeQuery("SELECT to_timestamp(1284352323) AS t")) { + while (resultSet.next()) { + Timestamp timestamp = resultSet.getTimestamp("t"); + System.out.printf( + "1284352323 seconds after epoch is %s\n", + OffsetDateTime.ofInstant( + Instant.ofEpochMilli(timestamp.getTime()), ZoneId.of("UTC"))); + } + } + } + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgInformationSchemaSample.java b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgInformationSchemaSample.java new file mode 100644 index 00000000000..a6b3988d234 --- /dev/null +++ b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgInformationSchemaSample.java @@ -0,0 +1,104 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.spanner.jdbc; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; + +class PgInformationSchemaSample { + + static void pgInformationSchema() throws SQLException { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + pgInformationSchema(projectId, instanceId, databaseId); + } + + static void pgInformationSchema(String projectId, String instanceId, String databaseId) + throws SQLException { + // Create a JDBC connection to the database. A connection can be reused to execute multiple + // statements. After completing all of your statements, call the "close" method on the + // connection to safely clean up any remaining resources. + try (Connection connection = + DriverManager.getConnection( + String.format( + "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s", + projectId, instanceId, databaseId))) { + connection + .createStatement() + .execute( + "CREATE TABLE Venues (" + + " VenueId bigint NOT NULL PRIMARY KEY," + + " Name varchar(1024) NOT NULL," + + " Revenues numeric," + + " Picture bytea" + + ")"); + + // The Spanner INFORMATION_SCHEMA tables can be used to query the metadata of tables and + // columns of PostgreSQL databases. The returned results will include additional PostgreSQL + // metadata columns. + + // Get all the user tables in the database. PostgreSQL uses the `public` schema for user + // tables. + try (ResultSet tables = + connection + .createStatement() + .executeQuery( + "SELECT table_catalog, table_schema, table_name, " + // The following columns are only available for PostgreSQL databases. + + "user_defined_type_catalog, " + + "user_defined_type_schema, " + + "user_defined_type_name " + + "FROM INFORMATION_SCHEMA.tables " + + "WHERE table_schema='public'")) { + while (tables.next()) { + String catalog = tables.getString("table_catalog"); + String schema = tables.getString("table_schema"); + String table = tables.getString("table_name"); + String userDefinedTypeCatalog = tables.getString("user_defined_type_catalog"); + String userDefinedTypeSchema = tables.getString("user_defined_type_schema"); + String userDefinedTypeName = tables.getString("user_defined_type_name"); + String userDefinedType = + userDefinedTypeName == null + ? null + : String.format( + "%s.%s.%s", + userDefinedTypeCatalog, userDefinedTypeSchema, userDefinedTypeName); + System.out.printf( + "Table: %s.%s.%s (User defined type: %s)\n", catalog, schema, table, userDefinedType); + } + } + + // The java.sql.DatabaseMetaData of the JDBC connection can also be used to retrieve + // information about tables, columns, indexes etc. These methods return the metadata as if it + // was a normal Spanner database. + try (ResultSet tables = + connection.getMetaData().getTables(null, null, null, new String[] {"TABLE"})) { + while (tables.next()) { + // Catalog and schema are empty. + String catalog = tables.getString("TABLE_CAT"); + String schema = tables.getString("TABLE_SCHEM"); + String table = tables.getString("TABLE_NAME"); + System.out.printf("Table in JDBC metadata: %s.%s.%s\n", catalog, schema, table); + } + } + } + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgNumericDataTypeSample.java b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgNumericDataTypeSample.java new file mode 100644 index 00000000000..9aa75821a24 --- /dev/null +++ b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgNumericDataTypeSample.java @@ -0,0 +1,159 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.spanner.jdbc; + +import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.Value; +import com.google.cloud.spanner.jdbc.CloudSpannerJdbcConnection; +import java.math.BigDecimal; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.Arrays; + +class PgNumericDataTypeSample { + + static void pgNumericDataType() throws SQLException { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + pgNumericDataType(projectId, instanceId, databaseId); + } + + static void pgNumericDataType(String projectId, String instanceId, String databaseId) + throws SQLException { + // Create a JDBC connection to the database. A connection can be reused to execute multiple + // statements. After completing all of your statements, call the "close" method on the + // connection to safely clean up any remaining resources. + try (Connection connection = + DriverManager.getConnection( + String.format( + "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s", + projectId, instanceId, databaseId))) { + // Create a table that includes a column with data type NUMERIC. As the database has been + // created with the PostgreSQL dialect, the data type that is used will be the PostgreSQL + // NUMERIC / DECIMAL data type. + connection + .createStatement() + .execute( + "CREATE TABLE Venues (" + + " VenueId bigint NOT NULL PRIMARY KEY," + + " Name varchar(1024) NOT NULL," + + " Revenues numeric" + + ")"); + System.out.print("Created Venues table\n"); + + // Insert a Venue using DML. + try (PreparedStatement statement = + connection.prepareStatement( + "INSERT INTO Venues (VenueId, Name, Revenues) " + "VALUES (?, ?, ?)")) { + statement.setLong(1, 1L); + statement.setString(2, "Venue 1"); + statement.setBigDecimal(3, new BigDecimal("3150.25")); + int updateCount = statement.executeUpdate(); + System.out.printf("Inserted %d venues\n", updateCount); + } + + // Insert a Venue with a NULL value for the Revenues column. + try (PreparedStatement statement = + connection.prepareStatement( + "INSERT INTO Venues (VenueId, Name, Revenues) " + "VALUES (?, ?, ?)")) { + statement.setLong(1, 2L); + statement.setString(2, "Venue 2"); + statement.setNull(3, Types.NUMERIC); + int updateCount = statement.executeUpdate(); + System.out.printf("Inserted %d venues with NULL revenues\n", updateCount); + } + + // Insert a Venue with a NaN (Not a Number) value for the Revenues column. + try (PreparedStatement statement = + connection.prepareStatement( + "INSERT INTO Venues (VenueId, Name, Revenues) " + "VALUES (?, ?, ?)")) { + statement.setLong(1, 3L); + statement.setString(2, "Venue 3"); + // Not a Number (NaN) can be set both using the Double.NaN constant or the String 'NaN'. + statement.setDouble(3, Double.NaN); + int updateCount = statement.executeUpdate(); + System.out.printf("Inserted %d venues with NaN revenues\n", updateCount); + } + + // Get all Venues and inspect the Revenues values. + try (ResultSet venues = + connection.createStatement().executeQuery("SELECT Name, Revenues FROM Venues")) { + while (venues.next()) { + String name = venues.getString("name"); + // Getting a PostgreSQL NUMERIC value as a Value is always supported, regardless whether + // the value is a number, NULL or NaN. + Value revenuesAsValue = venues.getObject("revenues", Value.class); + System.out.printf("Revenues of %s: %s\n", name, revenuesAsValue); + + // Getting a PostgreSQL NUMERIC value as a double is supported for all possible values. If + // the value is NULL, this method will return 0 and the wasNull() method will return true. + double revenuesAsDouble = venues.getDouble("revenues"); + boolean wasNull = venues.wasNull(); + if (wasNull) { + System.out.printf("\tRevenues of %s as double: null\n", name); + } else { + System.out.printf("\tRevenues of %s as double: %f\n", name, revenuesAsDouble); + } + + // Getting a PostgreSQL NUMERIC as a BigDecimal is supported for both NULL and non-NULL + // values, but not for NaN, as there is no BigDecimal representation of NaN. + if (!Double.valueOf(revenuesAsDouble).isNaN()) { + BigDecimal revenuesAsBigDecimal = venues.getBigDecimal("revenues"); + System.out.printf("\tRevenues of %s as BigDecimal: %s\n", name, revenuesAsBigDecimal); + } + + // A PostgreSQL NUMERIC value may also be retrieved as a String. + String revenuesAsString = venues.getString("revenues"); + System.out.printf("\tRevenues of %s as String: %s\n", name, revenuesAsString); + } + } + + // Mutations can also be used to insert/update NUMERIC values, including NaN values. + // Mutations can be used with the JDBC driver by unwrapping the + // com.google.cloud.spanner.jdbc.CloudSpannerJdbcConnection interface from the connection. + CloudSpannerJdbcConnection cloudSpannerJdbcConnection = + connection.unwrap(CloudSpannerJdbcConnection.class); + cloudSpannerJdbcConnection.write( + Arrays.asList( + Mutation.newInsertBuilder("Venues") + .set("VenueId") + .to(4L) + .set("Name") + .to("Venue 4") + .set("Revenues") + .to(Value.pgNumeric("125.10")) + .build(), + Mutation.newInsertBuilder("Venues") + .set("VenueId") + .to(5L) + .set("Name") + .to("Venue 5") + .set("Revenues") + .to(Value.pgNumeric(Value.NAN)) + .build())); + Timestamp commitTimestamp = cloudSpannerJdbcConnection.getCommitTimestamp(); + System.out.printf("Inserted 2 Venues using mutations at %s\n", commitTimestamp); + } + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgOrderNullsSample.java b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgOrderNullsSample.java new file mode 100644 index 00000000000..92ebc36c719 --- /dev/null +++ b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgOrderNullsSample.java @@ -0,0 +1,127 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.spanner.jdbc; + +import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.jdbc.CloudSpannerJdbcConnection; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Arrays; + +class PgOrderNullsSample { + + static void pgOrderNulls() throws SQLException { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + pgOrderNulls(projectId, instanceId, databaseId); + } + + static void pgOrderNulls(String projectId, String instanceId, String databaseId) + throws SQLException { + // Create a JDBC connection to the database. A connection can be reused to execute multiple + // statements. After completing all of your statements, call the "close" method on the + // connection to safely clean up any remaining resources. + try (Connection connection = + DriverManager.getConnection( + String.format( + "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s", + projectId, instanceId, databaseId))) { + + // Spanner PostgreSQL follows the ORDER BY rules for NULL values of PostgreSQL. This means + // that: + // 1. NULL values are ordered last by default when a query result is ordered in ascending + // order. + // 2. NULL values are ordered first by default when a query result is ordered in descending + // order. + // 3. NULL values can be order first or last by specifying NULLS FIRST or NULLS LAST in the + // ORDER BY clause. + connection + .createStatement() + .execute( + "CREATE TABLE Singers (" + + " SingerId bigint NOT NULL PRIMARY KEY," + + " Name varchar(1024)" + + ")"); + + connection + .unwrap(CloudSpannerJdbcConnection.class) + .write( + Arrays.asList( + Mutation.newInsertBuilder("Singers") + .set("SingerId") + .to(1L) + .set("Name") + .to("Alice") + .build(), + Mutation.newInsertBuilder("Singers") + .set("SingerId") + .to(2L) + .set("Name") + .to("Bruce") + .build(), + Mutation.newInsertBuilder("Singers") + .set("SingerId") + .to(3L) + .set("Name") + .to((String) null) + .build())); + + // This returns the singers in order Alice, Bruce, null + System.out.println("Singers ORDER BY Name"); + try (ResultSet singers = + connection.createStatement().executeQuery("SELECT * FROM Singers ORDER BY Name")) { + printSingerNames(singers); + } + + // This returns the singers in order null, Bruce, Alice + System.out.println("Singers ORDER BY Name DESC"); + try (ResultSet singers = + connection.createStatement().executeQuery("SELECT * FROM Singers ORDER BY Name DESC")) { + printSingerNames(singers); + } + + // This returns the singers in order null, Alice, Bruce + System.out.println("Singers ORDER BY Name NULLS FIRST"); + try (ResultSet singers = + connection + .createStatement() + .executeQuery("SELECT * FROM Singers ORDER BY Name NULLS FIRST")) { + printSingerNames(singers); + } + + // This returns the singers in order Bruce, Alice, null + System.out.println("Singers ORDER BY Name DESC NULLS LAST"); + try (ResultSet singers = + connection + .createStatement() + .executeQuery("SELECT * FROM Singers ORDER BY Name DESC NULLS LAST")) { + printSingerNames(singers); + } + } + } + + static void printSingerNames(ResultSet singers) throws SQLException { + while (singers.next()) { + System.out.printf( + "\t%s\n", singers.getString("name") == null ? "" : singers.getString("name")); + } + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgPartitionedDmlSample.java b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgPartitionedDmlSample.java new file mode 100644 index 00000000000..bd3b41bd6f3 --- /dev/null +++ b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgPartitionedDmlSample.java @@ -0,0 +1,58 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.spanner.jdbc; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +class PgPartitionedDmlSample { + + static void pgPartitionedDml() throws SQLException { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + pgPartitionedDml(projectId, instanceId, databaseId); + } + + static void pgPartitionedDml(String projectId, String instanceId, String databaseId) + throws SQLException { + // Create a JDBC connection to the database. A connection can be reused to execute multiple + // statements. After completing all of your statements, call the "close" method on the + // connection to safely clean up any remaining resources. + try (Connection connection = + DriverManager.getConnection( + String.format( + "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s", + projectId, instanceId, databaseId))) { + // Spanner PostgreSQL has the same transaction limits as normal Spanner. This includes a + // maximum of 20,000 mutations in a single read/write transaction. Large update operations can + // be executed using Partitioned DML. This is also supported on Spanner PostgreSQL. + // See https://cloud.google.com/spanner/docs/dml-partitioned for more information. + + // Switch to Partitioned DML. Note that we must prefix all Spanner specific session statements + // with `SPANNER.`. + connection.createStatement() + .execute("SET SPANNER.AUTOCOMMIT_DML_MODE='PARTITIONED_NON_ATOMIC'"); + // Execute the DML statement. + int deletedCount = connection.createStatement().executeUpdate("DELETE FROM Singers"); + // The returned update count is the lower bound of the number of records that was deleted. + System.out.printf("Deleted at least %d singers\n", deletedCount); + } + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgQueryParameterSample.java b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgQueryParameterSample.java new file mode 100644 index 00000000000..7568dbd34a0 --- /dev/null +++ b/spanner/jdbc/src/main/java/com/example/spanner/jdbc/PgQueryParameterSample.java @@ -0,0 +1,69 @@ +/* + * Copyright 2022 Google LLC + * + * 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. + */ + +package com.example.spanner.jdbc; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +class PgQueryParameterSample { + + static void pgQueryParameter() throws SQLException { + // TODO(developer): Replace these variables before running the sample. + String projectId = "my-project"; + String instanceId = "my-instance"; + String databaseId = "my-database"; + pgQueryParameter(projectId, instanceId, databaseId); + } + + static void pgQueryParameter(String projectId, String instanceId, String databaseId) + throws SQLException { + // Create a JDBC connection to the database. A connection can be reused to execute multiple + // statements. After completing all of your statements, call the "close" method on the + // connection to safely clean up any remaining resources. + try (Connection connection = + DriverManager.getConnection( + String.format( + "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s", + projectId, instanceId, databaseId))) { + try (PreparedStatement statement = + connection.prepareStatement( + "SELECT SingerId, FirstName, LastName " + + "FROM Singers " + + "WHERE LastName LIKE ?")) { + statement.setString(1, "A%"); + System.out.print("Listing all singers with a last name that starts with 'A'\n"); + try (ResultSet resultSet = statement.executeQuery()) { + while (resultSet.next()) { + // Note that the PostgreSQL dialect will return all column names in lower case, unless + // the + // columns have been created with case-sensitive column names. See + // https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS + // for more information on PostgreSQL identifiers. + System.out.printf( + "%d %s %s%n", + resultSet.getLong("singerid"), + resultSet.getString("firstname"), + resultSet.getString("lastname")); + } + } + } + } + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/test/java/com/example/spanner/jdbc/BaseJdbcPgExamplesIT.java b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/BaseJdbcPgExamplesIT.java new file mode 100644 index 00000000000..cc273c13795 --- /dev/null +++ b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/BaseJdbcPgExamplesIT.java @@ -0,0 +1,190 @@ +/* + * Copyright 2022 Google Inc. + * + * 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. + */ + +package com.example.spanner.jdbc; + +import com.google.cloud.ServiceOptions; +import com.google.cloud.spanner.Database; +import com.google.cloud.spanner.DatabaseAdminClient; +import com.google.cloud.spanner.DatabaseId; +import com.google.cloud.spanner.Dialect; +import com.google.cloud.spanner.Instance; +import com.google.cloud.spanner.Mutation; +import com.google.cloud.spanner.Spanner; +import com.google.cloud.spanner.SpannerOptions; +import com.google.cloud.spanner.connection.ConnectionOptions; +import com.google.cloud.spanner.jdbc.CloudSpannerJdbcConnection; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.math.BigDecimal; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Integration tests for Cloud Spanner PostgreSQL JDBC examples. */ +@RunWith(JUnit4.class) +@SuppressWarnings("checkstyle:AbbreviationAsWordInName") +public abstract class BaseJdbcPgExamplesIT { + // The instance needs to exist for tests to pass. + protected static String instanceId = System.getProperty("spanner.test.instance"); + protected static final String databaseId = + formatForTest(System.getProperty("spanner.sample.pgdatabase", "mypgsample")); + protected static DatabaseId dbId; + private static DatabaseAdminClient dbClient; + private boolean testTableCreated; + + protected interface JdbcRunnable { + void run() throws Exception; + } + + protected String runExample(JdbcRunnable example) { + PrintStream stdOut = System.out; + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + PrintStream out = new PrintStream(bout); + System.setOut(out); + try { + example.run(); + } catch (Exception e) { + e.printStackTrace(); + } + System.setOut(stdOut); + return bout.toString(); + } + + @BeforeClass + public static void createTestDatabase() throws Exception { + SpannerOptions options = SpannerOptions.newBuilder().build(); + Spanner spanner = options.getService(); + dbClient = spanner.getDatabaseAdminClient(); + if (instanceId == null) { + Iterator iterator = + spanner.getInstanceAdminClient().listInstances().iterateAll().iterator(); + if (iterator.hasNext()) { + instanceId = iterator.next().getId().getInstance(); + } + } + dbId = DatabaseId.of(options.getProjectId(), instanceId, databaseId); + dbClient.dropDatabase(dbId.getInstanceId().getInstance(), dbId.getDatabase()); + Database database = dbClient.newDatabaseBuilder(dbId).setDialect(Dialect.POSTGRESQL).build(); + dbClient.createDatabase(database, Collections.emptyList()).get(); + } + + @AfterClass + public static void dropTestDatabase() { + ConnectionOptions.closeSpanner(); + dbClient.dropDatabase(dbId.getInstanceId().getInstance(), dbId.getDatabase()); + } + + static class Singer { + final long singerId; + final String firstName; + final String lastName; + final BigDecimal revenues; + + Singer(long singerId, String firstName, String lastName, BigDecimal revenues) { + this.singerId = singerId; + this.firstName = firstName; + this.lastName = lastName; + this.revenues = revenues; + } + } + + static final List TEST_SINGERS = + Arrays.asList( + new Singer(1, "Marc", "Richards", new BigDecimal("104100.00")), + new Singer(2, "Catalina", "Smith", new BigDecimal("9880.99")), + new Singer(3, "Alice", "Trentor", new BigDecimal("300183")), + new Singer(4, "Lea", "Martin", new BigDecimal("20118.12")), + new Singer(5, "David", "Lomond", new BigDecimal("311399.26")), + new Singer(6, "Bruce", "Allison", null), + new Singer(7, "Alice", "Bruxelles", null)); + + protected boolean createTestTable() { + return false; + } + + @Before + public void insertTestData() throws SQLException { + if (createTestTable()) { + String connectionUrl = + String.format( + "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s", + ServiceOptions.getDefaultProjectId(), instanceId, databaseId); + try (Connection connection = DriverManager.getConnection(connectionUrl)) { + if (!testTableCreated) { + connection + .createStatement() + .execute( + "CREATE TABLE Singers (\n" + + " SingerId BIGINT NOT NULL PRIMARY KEY,\n" + + " FirstName VARCHAR(1024),\n" + + " LastName VARCHAR(1024),\n" + + " SingerInfo BYTEA,\n" + + " Revenues NUMERIC\n" + + ")\n"); + testTableCreated = true; + } + CloudSpannerJdbcConnection spannerConnection = + connection.unwrap(CloudSpannerJdbcConnection.class); + spannerConnection.setAutoCommit(false); + for (Singer singer : TEST_SINGERS) { + spannerConnection.bufferedWrite( + Mutation.newInsertBuilder("Singers") + .set("SingerId") + .to(singer.singerId) + .set("FirstName") + .to(singer.firstName) + .set("LastName") + .to(singer.lastName) + .set("Revenues") + .to(singer.revenues) + .build()); + } + connection.commit(); + } + } + } + + @After + public void removeTestData() throws SQLException { + if (createTestTable()) { + String connectionUrl = + String.format( + "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s", + ServiceOptions.getDefaultProjectId(), instanceId, databaseId); + try (Connection connection = DriverManager.getConnection(connectionUrl); + Statement statement = connection.createStatement()) { + statement.execute("DELETE FROM Singers WHERE 1=1"); + } + } + } + + static String formatForTest(String name) { + return name + "-" + UUID.randomUUID().toString().substring(0, 18); + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgBatchDmlSampleIT.java b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgBatchDmlSampleIT.java new file mode 100644 index 00000000000..1c72bdbd5aa --- /dev/null +++ b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgBatchDmlSampleIT.java @@ -0,0 +1,40 @@ +/* + * Copyright 2022 Google Inc. + * + * 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. + */ + +package com.example.spanner.jdbc; + +import static org.junit.Assert.assertTrue; + +import com.google.cloud.ServiceOptions; +import org.junit.Test; + +public class PgBatchDmlSampleIT extends BaseJdbcPgExamplesIT { + + @Override + protected boolean createTestTable() { + return true; + } + + @Test + public void testPgBatchDml() { + String out = + runExample( + () -> + PgBatchDmlSample.pgBatchDml( + ServiceOptions.getDefaultProjectId(), instanceId, databaseId)); + assertTrue(out.contains("Inserted 2 singers")); + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgCaseInsensitivitySampleIT.java b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgCaseInsensitivitySampleIT.java new file mode 100644 index 00000000000..c81ed068a99 --- /dev/null +++ b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgCaseInsensitivitySampleIT.java @@ -0,0 +1,36 @@ +/* + * Copyright 2022 Google Inc. + * + * 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. + */ + +package com.example.spanner.jdbc; + +import static org.junit.Assert.assertTrue; + +import com.google.cloud.ServiceOptions; +import org.junit.Test; + +public class PgCaseInsensitivitySampleIT extends BaseJdbcPgExamplesIT { + + @Test + public void testCaseInsensitivity() { + String out = + runExample( + () -> + PgCaseSensitivitySample.pgCaseSensitivity( + ServiceOptions.getDefaultProjectId(), instanceId, databaseId)); + assertTrue(out, out.contains("SingerId: 1, FirstName: Bruce, LastName: Allison")); + assertTrue(out, out.contains("SingerId: 1, FullName: Bruce Allison")); + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgCastDataTypeSampleIT.java b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgCastDataTypeSampleIT.java new file mode 100644 index 00000000000..3fe04b2572b --- /dev/null +++ b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgCastDataTypeSampleIT.java @@ -0,0 +1,42 @@ +/* + * Copyright 2022 Google Inc. + * + * 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. + */ + +package com.example.spanner.jdbc; + +import static org.junit.Assert.assertTrue; + +import com.google.cloud.ServiceOptions; +import org.junit.Test; + +public class PgCastDataTypeSampleIT extends BaseJdbcPgExamplesIT { + + @Test + public void testPgCastDataType() { + String out = + runExample( + () -> + PgCastDataTypeSample.pgCastDataType( + ServiceOptions.getDefaultProjectId(), instanceId, databaseId)); + assertTrue(out, out.contains("String: 1")); + assertTrue(out, out.contains("Bigint: 2")); + assertTrue(out, out.contains("Numeric: 3")); + assertTrue(out, out.contains("Bytes: NA==")); + assertTrue(out, out.contains("Float: 5.000000")); + assertTrue(out, out.contains("Bool: true")); + assertTrue(out, out.contains("Timestamp: 2021-11-03T09:35:01Z")); + assertTrue(out, out.contains("Date: 2022-04-25")); + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgConnectToDatabaseSampleIT.java b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgConnectToDatabaseSampleIT.java new file mode 100644 index 00000000000..09f2fa0adb7 --- /dev/null +++ b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgConnectToDatabaseSampleIT.java @@ -0,0 +1,35 @@ +/* + * Copyright 2022 Google Inc. + * + * 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. + */ + +package com.example.spanner.jdbc; + +import static org.junit.Assert.assertTrue; + +import com.google.cloud.ServiceOptions; +import org.junit.Test; + +public class PgConnectToDatabaseSampleIT extends BaseJdbcPgExamplesIT { + + @Test + public void testPgConnectToDatabase() { + String out = + runExample( + () -> + PgConnectToDatabaseSample.pgConnectToDatabase( + ServiceOptions.getDefaultProjectId(), instanceId, databaseId)); + assertTrue(out.contains("Connected to Cloud Spanner PostgreSQL at [")); + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgCreateInterleavedTableSampleIT.java b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgCreateInterleavedTableSampleIT.java new file mode 100644 index 00000000000..cf2ff3704ff --- /dev/null +++ b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgCreateInterleavedTableSampleIT.java @@ -0,0 +1,35 @@ +/* + * Copyright 2022 Google Inc. + * + * 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. + */ + +package com.example.spanner.jdbc; + +import static org.junit.Assert.assertTrue; + +import com.google.cloud.ServiceOptions; +import org.junit.Test; + +public class PgCreateInterleavedTableSampleIT extends BaseJdbcPgExamplesIT { + + @Test + public void testCreateInterleavedTable() { + String out = + runExample( + () -> + PgCreateInterleavedTableSample.pgCreateInterleavedTable( + ServiceOptions.getDefaultProjectId(), instanceId, databaseId)); + assertTrue(out.contains("Created Singers and Albums tables")); + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgDmlWithParametersSampleIT.java b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgDmlWithParametersSampleIT.java new file mode 100644 index 00000000000..86f0d6785e3 --- /dev/null +++ b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgDmlWithParametersSampleIT.java @@ -0,0 +1,40 @@ +/* + * Copyright 2022 Google Inc. + * + * 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. + */ + +package com.example.spanner.jdbc; + +import static org.junit.Assert.assertTrue; + +import com.google.cloud.ServiceOptions; +import org.junit.Test; + +public class PgDmlWithParametersSampleIT extends BaseJdbcPgExamplesIT { + + @Override + protected boolean createTestTable() { + return true; + } + + @Test + public void testPgDmlWithParameters() { + String out = + runExample( + () -> + PgDmlWithParametersSample.pgDmlWithParameters( + ServiceOptions.getDefaultProjectId(), instanceId, databaseId)); + assertTrue(out.contains("Inserted 2 singers")); + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgFunctionsSampleIT.java b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgFunctionsSampleIT.java new file mode 100644 index 00000000000..1f1b94f0706 --- /dev/null +++ b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgFunctionsSampleIT.java @@ -0,0 +1,35 @@ +/* + * Copyright 2022 Google Inc. + * + * 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. + */ + +package com.example.spanner.jdbc; + +import static org.junit.Assert.assertTrue; + +import com.google.cloud.ServiceOptions; +import org.junit.Test; + +public class PgFunctionsSampleIT extends BaseJdbcPgExamplesIT { + + @Test + public void testPgFunctions() { + String out = + runExample( + () -> + PgFunctionsSample.pgFunctions( + ServiceOptions.getDefaultProjectId(), instanceId, databaseId)); + assertTrue(out, out.contains("1284352323 seconds after epoch is 2010-09-13T04:32:03Z")); + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgInformationSchemaSampleIT.java b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgInformationSchemaSampleIT.java new file mode 100644 index 00000000000..98c130ded99 --- /dev/null +++ b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgInformationSchemaSampleIT.java @@ -0,0 +1,35 @@ +/* + * Copyright 2022 Google Inc. + * + * 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. + */ + +package com.example.spanner.jdbc; + +import static org.junit.Assert.assertTrue; + +import com.google.cloud.ServiceOptions; +import org.junit.Test; + +public class PgInformationSchemaSampleIT extends BaseJdbcPgExamplesIT { + + @Test + public void testPgInformationSchema() { + String out = + runExample( + () -> + PgInformationSchemaSample.pgInformationSchema( + ServiceOptions.getDefaultProjectId(), instanceId, databaseId)); + assertTrue(out, out.contains("public.venues (User defined type: null)")); + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgNumericDataTypeSampleIT.java b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgNumericDataTypeSampleIT.java new file mode 100644 index 00000000000..4ca48e8243e --- /dev/null +++ b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgNumericDataTypeSampleIT.java @@ -0,0 +1,52 @@ +/* + * Copyright 2022 Google Inc. + * + * 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. + */ + +package com.example.spanner.jdbc; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.google.cloud.ServiceOptions; +import org.junit.Test; + +public class PgNumericDataTypeSampleIT extends BaseJdbcPgExamplesIT { + + @Test + public void testNumericDataType() { + String out = + runExample( + () -> + PgNumericDataTypeSample.pgNumericDataType( + ServiceOptions.getDefaultProjectId(), instanceId, databaseId)); + + assertTrue(out, out.contains("Revenues of Venue 1: 3150.25")); + assertTrue(out, out.contains("Revenues of Venue 1 as double: 3150.25")); + assertTrue(out, out.contains("Revenues of Venue 1 as BigDecimal: 3150.25")); + assertTrue(out, out.contains("Revenues of Venue 1 as String: 3150.25")); + + assertTrue(out, out.contains("Revenues of Venue 2: null")); + assertTrue(out, out.contains("Revenues of Venue 2 as double: null")); + assertTrue(out, out.contains("Revenues of Venue 2 as BigDecimal: null")); + assertTrue(out, out.contains("Revenues of Venue 2 as String: null")); + + assertTrue(out, out.contains("Revenues of Venue 3: NaN")); + assertTrue(out, out.contains("Revenues of Venue 3 as double: NaN")); + assertFalse(out, out.contains("Revenues of Venue 3 as BigDecimal:")); + assertTrue(out, out.contains("Revenues of Venue 3 as String: NaN")); + + assertTrue(out, out.contains("Inserted 2 Venues using mutations")); + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgOrderNullsSampleIT.java b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgOrderNullsSampleIT.java new file mode 100644 index 00000000000..22b3b1a1c3a --- /dev/null +++ b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgOrderNullsSampleIT.java @@ -0,0 +1,39 @@ +/* + * Copyright 2022 Google Inc. + * + * 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. + */ + +package com.example.spanner.jdbc; + +import static org.junit.Assert.assertTrue; + +import com.google.cloud.ServiceOptions; +import org.junit.Test; + +public class PgOrderNullsSampleIT extends BaseJdbcPgExamplesIT { + + @Test + public void testOrderNulls() { + String out = + runExample( + () -> + PgOrderNullsSample.pgOrderNulls( + ServiceOptions.getDefaultProjectId(), instanceId, databaseId)); + assertTrue(out, out.contains("Singers ORDER BY Name\n\tAlice\n\tBruce\n\t")); + assertTrue(out, out.contains("Singers ORDER BY Name DESC\n\t\n\tBruce\n\tAlice")); + assertTrue(out, out.contains("Singers ORDER BY Name NULLS FIRST\n\t\n\tAlice\n\tBruce")); + assertTrue( + out, out.contains("Singers ORDER BY Name DESC NULLS LAST\n\tBruce\n\tAlice\n\t")); + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgPartitionedDmlSampleIT.java b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgPartitionedDmlSampleIT.java new file mode 100644 index 00000000000..8fffa1b837c --- /dev/null +++ b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgPartitionedDmlSampleIT.java @@ -0,0 +1,40 @@ +/* + * Copyright 2022 Google Inc. + * + * 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. + */ + +package com.example.spanner.jdbc; + +import static org.junit.Assert.assertTrue; + +import com.google.cloud.ServiceOptions; +import org.junit.Test; + +public class PgPartitionedDmlSampleIT extends BaseJdbcPgExamplesIT { + + @Override + protected boolean createTestTable() { + return true; + } + + @Test + public void testPgPartitionedDml() { + String out = + runExample( + () -> + PgPartitionedDmlSample.pgPartitionedDml( + ServiceOptions.getDefaultProjectId(), instanceId, databaseId)); + assertTrue(out.contains("Deleted at least 7 singers")); + } +} \ No newline at end of file diff --git a/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgQueryParameterSampleIT.java b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgQueryParameterSampleIT.java new file mode 100644 index 00000000000..0d7c7c285d3 --- /dev/null +++ b/spanner/jdbc/src/test/java/com/example/spanner/jdbc/PgQueryParameterSampleIT.java @@ -0,0 +1,42 @@ +/* + * Copyright 2022 Google Inc. + * + * 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. + */ + +package com.example.spanner.jdbc; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.google.cloud.ServiceOptions; +import org.junit.Test; + +public class PgQueryParameterSampleIT extends BaseJdbcPgExamplesIT { + + @Override + protected boolean createTestTable() { + return true; + } + + @Test + public void testPgQueryParameter() { + String out = + runExample( + () -> + PgQueryParameterSample.pgQueryParameter( + ServiceOptions.getDefaultProjectId(), instanceId, databaseId)); + assertTrue(out.contains("6 Bruce Allison")); + assertFalse(out.contains("7 Alice Bruxelles")); + } +} \ No newline at end of file diff --git a/spanner/opencensus/pom.xml b/spanner/opencensus/pom.xml index d331883aedc..209a55d7c5b 100644 --- a/spanner/opencensus/pom.xml +++ b/spanner/opencensus/pom.xml @@ -8,7 +8,7 @@ 1.8 1.8 - 0.31.0 + 0.31.1 6.13.0 @@ -53,7 +53,7 @@ com.google.protobuf protobuf-java - 3.20.0 + 3.20.1 io.opencensus @@ -82,7 +82,7 @@ com.google.cloud google-cloud-spanner - 6.16.0 + 6.23.1 test diff --git a/spanner/spring-data/pom.xml b/spanner/spring-data/pom.xml index e9692462a47..8f7c71ed594 100644 --- a/spanner/spring-data/pom.xml +++ b/spanner/spring-data/pom.xml @@ -41,7 +41,7 @@ limitations under the License. com.google.cloud spring-cloud-gcp-dependencies - 3.2.0 + 3.2.1 pom import diff --git a/storage-transfer/pom.xml b/storage-transfer/pom.xml index aa7b073f43e..7d6fa67172a 100644 --- a/storage-transfer/pom.xml +++ b/storage-transfer/pom.xml @@ -92,7 +92,7 @@ com.google.cloud google-cloud-storage - 2.6.0 + 2.6.1 test diff --git a/storage/cloud-client/pom.xml b/storage/cloud-client/pom.xml index 37cba9379ca..0469ef1fb11 100644 --- a/storage/cloud-client/pom.xml +++ b/storage/cloud-client/pom.xml @@ -39,7 +39,7 @@ com.google.cloud google-cloud-storage - 2.6.0 + 2.6.1 diff --git a/storage/s3-sdk/pom.xml b/storage/s3-sdk/pom.xml index a6a544e98f1..e1407fee304 100644 --- a/storage/s3-sdk/pom.xml +++ b/storage/s3-sdk/pom.xml @@ -56,7 +56,7 @@ com.google.cloud google-cloud-storage - 2.6.0 + 2.6.1 test diff --git a/storage/xml-api/cmdline-sample/pom.xml b/storage/xml-api/cmdline-sample/pom.xml index a152461599a..df7f4c9aecc 100644 --- a/storage/xml-api/cmdline-sample/pom.xml +++ b/storage/xml-api/cmdline-sample/pom.xml @@ -30,7 +30,7 @@ - 1.41.6 + 1.41.8 UTF-8 1.6.0 1.8 diff --git a/storage/xml-api/serviceaccount-appengine-sample/pom.xml b/storage/xml-api/serviceaccount-appengine-sample/pom.xml index 8b67732495f..130abe465cf 100644 --- a/storage/xml-api/serviceaccount-appengine-sample/pom.xml +++ b/storage/xml-api/serviceaccount-appengine-sample/pom.xml @@ -35,7 +35,7 @@ 1.8 1.8 - 1.33.4 + 1.34.1 ${project.build.directory}/${project.build.finalName} UTF-8 @@ -71,7 +71,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG gaeinfo diff --git a/trace/pom.xml b/trace/pom.xml index 5dd8061347e..8935ac1b029 100644 --- a/trace/pom.xml +++ b/trace/pom.xml @@ -43,12 +43,12 @@ io.opencensus opencensus-api - 0.31.0 + 0.31.1 io.opencensus opencensus-exporter-trace-stackdriver - 0.31.0 + 0.31.1 io.grpc @@ -59,7 +59,7 @@ io.opencensus opencensus-impl - 0.31.0 + 0.31.1 runtime @@ -95,7 +95,7 @@ com.google.http-client google-http-client-jackson2 - 1.41.6 + 1.41.8 test diff --git a/unittests/pom.xml b/unittests/pom.xml index 3966f3e2663..ae2dc445efd 100644 --- a/unittests/pom.xml +++ b/unittests/pom.xml @@ -71,7 +71,7 @@ com.google.api-client google-api-client-appengine - 1.33.4 + 1.34.0 test @@ -98,7 +98,7 @@ com.google.cloud.tools appengine-maven-plugin - 2.4.1 + 2.4.2 GCLOUD_CONFIG gaeinfo diff --git a/vision/face-detection/pom.xml b/vision/face-detection/pom.xml index 24a037bc9fe..910f65e060a 100644 --- a/vision/face-detection/pom.xml +++ b/vision/face-detection/pom.xml @@ -56,7 +56,7 @@ com.google.http-client google-http-client-jackson2 - 1.41.6 + 1.41.8 diff --git a/workflows/cloud-client/pom.xml b/workflows/cloud-client/pom.xml index de3aee2e25a..80d75224b57 100644 --- a/workflows/cloud-client/pom.xml +++ b/workflows/cloud-client/pom.xml @@ -59,7 +59,7 @@ limitations under the License. com.google.cloud google-cloud-workflow-executions - 2.1.6 + 2.1.7