From 13b12dab0042b174be06c03f56bf8422f6967095 Mon Sep 17 00:00:00 2001 From: Jack Dingilian Date: Thu, 7 Sep 2023 17:17:58 -0400 Subject: [PATCH 001/105] Add mechanism to override bigtable client for testing --- .../sdk/io/gcp/bigtable/BigtableConfig.java | 12 ++++ .../beam/sdk/io/gcp/bigtable/BigtableIO.java | 19 ++++++ .../dao/BigtableChangeStreamAccessor.java | 7 +++ .../dao/BigtableClientOverride.java | 58 +++++++++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigtable/changestreams/dao/BigtableClientOverride.java diff --git a/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigtable/BigtableConfig.java b/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigtable/BigtableConfig.java index 018e538f552c..15230c8adef9 100644 --- a/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigtable/BigtableConfig.java +++ b/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigtable/BigtableConfig.java @@ -25,6 +25,7 @@ import org.apache.beam.sdk.annotations.Internal; import org.apache.beam.sdk.extensions.gcp.auth.CredentialFactory; import org.apache.beam.sdk.extensions.gcp.options.GcpOptions; +import org.apache.beam.sdk.io.gcp.bigtable.changestreams.dao.BigtableClientOverride; import org.apache.beam.sdk.options.ValueProvider; import org.apache.beam.sdk.transforms.SerializableFunction; import org.apache.beam.sdk.transforms.display.DisplayData; @@ -49,6 +50,9 @@ public abstract class BigtableConfig implements Serializable { /** Returns the app profile being read from. */ public abstract @Nullable ValueProvider getAppProfileId(); + /** Returns the Bigtable client override. */ + public abstract @Nullable BigtableClientOverride getBigtableClientOverride(); + /** * Returns the Google Cloud Bigtable instance being written to, and other parameters. * @@ -113,6 +117,8 @@ abstract Builder setBigtableOptionsConfigurator( abstract Builder setChannelCount(int count); + abstract Builder setBigtableClientOverride(BigtableClientOverride clientOverride); + abstract BigtableConfig build(); } @@ -156,6 +162,12 @@ public BigtableConfig withEmulator(String emulatorHost) { return toBuilder().setEmulatorHost(emulatorHost).build(); } + @VisibleForTesting + BigtableConfig withBigtableClientOverride(BigtableClientOverride clientOverride) { + checkArgument(clientOverride != null, "clientOverride can not be null"); + return toBuilder().setBigtableClientOverride(clientOverride).build(); + } + void validate() { checkArgument( (getProjectId() != null diff --git a/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigtable/BigtableIO.java b/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigtable/BigtableIO.java index a655a29e92b2..9f3c627a89ef 100644 --- a/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigtable/BigtableIO.java +++ b/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigtable/BigtableIO.java @@ -49,6 +49,7 @@ import org.apache.beam.sdk.io.gcp.bigtable.changestreams.UniqueIdGenerator; import org.apache.beam.sdk.io.gcp.bigtable.changestreams.action.ActionFactory; import org.apache.beam.sdk.io.gcp.bigtable.changestreams.dao.BigtableChangeStreamAccessor; +import org.apache.beam.sdk.io.gcp.bigtable.changestreams.dao.BigtableClientOverride; import org.apache.beam.sdk.io.gcp.bigtable.changestreams.dao.DaoFactory; import org.apache.beam.sdk.io.gcp.bigtable.changestreams.dao.MetadataTableAdminDao; import org.apache.beam.sdk.io.gcp.bigtable.changestreams.dofn.DetectNewPartitionsDoFn; @@ -1988,6 +1989,24 @@ public ReadChangeStream withMetadataTableAppProfileId(String appProfileId) { .build(); } + /** + * Returns a new {@link BigtableIO.ReadChangeStream} that overrides the config of data and/or + * admin client for streaming changes and for managing the metadata. For testing purposes only. + * Not intended for use. + * + *

Does not modify this object. + */ + @VisibleForTesting + ReadChangeStream withBigtableClientOverride(BigtableClientOverride clientOverride) { + BigtableConfig config = getBigtableConfig(); + BigtableConfig metadataTableConfig = getMetadataTableBigtableConfig(); + return toBuilder() + .setBigtableConfig(config.withBigtableClientOverride(clientOverride)) + .setMetadataTableBigtableConfig( + metadataTableConfig.withBigtableClientOverride(clientOverride)) + .build(); + } + /** * Returns a new {@link BigtableIO.ReadChangeStream} that, if set to true, will create or update * metadata table before launching pipeline. Otherwise, it is expected that a metadata table diff --git a/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigtable/changestreams/dao/BigtableChangeStreamAccessor.java b/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigtable/changestreams/dao/BigtableChangeStreamAccessor.java index ecf9a7039598..cb296aef6c28 100644 --- a/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigtable/changestreams/dao/BigtableChangeStreamAccessor.java +++ b/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigtable/changestreams/dao/BigtableChangeStreamAccessor.java @@ -210,6 +210,13 @@ private static BigtableChangeStreamAccessor createAccessor(@NonNull BigtableConf .setMaxAttempts(10) .build()); + final BigtableClientOverride clientOverride = bigtableConfig.getBigtableClientOverride(); + if (clientOverride != null) { + clientOverride.updateTableAdminClientSettings(tableAdminSettingsBuilder); + clientOverride.updateInstanceAdminClientSettings(instanceAdminSettingsBuilder); + clientOverride.updateDataClientSettings(dataSettingsBuilder); + } + BigtableDataClient dataClient = BigtableDataClient.create(dataSettingsBuilder.build()); BigtableTableAdminClient tableAdminClient = BigtableTableAdminClient.create(tableAdminSettingsBuilder.build()); diff --git a/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigtable/changestreams/dao/BigtableClientOverride.java b/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigtable/changestreams/dao/BigtableClientOverride.java new file mode 100644 index 000000000000..72b3e39871ef --- /dev/null +++ b/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigtable/changestreams/dao/BigtableClientOverride.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.beam.sdk.io.gcp.bigtable.changestreams.dao; + +import com.google.cloud.bigtable.admin.v2.BigtableInstanceAdminSettings; +import com.google.cloud.bigtable.admin.v2.BigtableTableAdminSettings; +import com.google.cloud.bigtable.data.v2.BigtableDataSettings; +import java.io.IOException; +import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.annotations.VisibleForTesting; + +/** Override the configuration of Cloud Bigtable data and admin client. */ +@VisibleForTesting +public interface BigtableClientOverride { + /** + * Update {@link BigtableInstanceAdminSettings.Builder} with custom configurations. + * + *

For example, to update the admin api endpoint. + * + * @param builder builds the instance admin client + * @throws IOException when dependency initialization fails + */ + void updateInstanceAdminClientSettings(BigtableInstanceAdminSettings.Builder builder) + throws IOException; + + /** + * Update {@link BigtableTableAdminSettings.Builder} with custom configurations. + * + *

For example, to update the admin api endpoint. + * + * @param builder builds the table admin client + * @throws IOException when dependency initialization fails + */ + void updateTableAdminClientSettings(BigtableTableAdminSettings.Builder builder) + throws IOException; + + /** + * Update {@link BigtableDataSettings.Builder} with custom configurations. + * + * @param builder builds the data client + * @throws IOException when dependency initialization fails + */ + void updateDataClientSettings(BigtableDataSettings.Builder builder) throws IOException; +} From 1e60aee3e2916656f9b48e19e3835837bd243b5e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Sep 2023 14:19:09 -0400 Subject: [PATCH 002/105] Bump org.checkerframework:checkerframework-gradle-plugin (#28298) Bumps org.checkerframework:checkerframework-gradle-plugin from 0.6.30 to 0.6.33. --- updated-dependencies: - dependency-name: org.checkerframework:checkerframework-gradle-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- buildSrc/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index a8a760029069..0ca748e3eb04 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -57,7 +57,7 @@ dependencies { runtimeOnly("com.avast.gradle:gradle-docker-compose-plugin:0.16.12") // Enable docker compose tasks runtimeOnly("ca.cutterslade.gradle:gradle-dependency-analyze:1.8.3") // Enable dep analysis runtimeOnly("gradle.plugin.net.ossindex:ossindex-gradle-plugin:0.4.11") // Enable dep vulnerability analysis - runtimeOnly("org.checkerframework:checkerframework-gradle-plugin:0.6.30") // Enable enhanced static checking plugin + runtimeOnly("org.checkerframework:checkerframework-gradle-plugin:0.6.33") // Enable enhanced static checking plugin } // Because buildSrc is built and tested automatically _before_ gradle From fdcb08fdf7d4789dfc1b7dcc8aca3d27e35a2ac8 Mon Sep 17 00:00:00 2001 From: Andrey Devyatkin Date: Thu, 14 Sep 2023 20:19:22 +0200 Subject: [PATCH 003/105] Migrate "beam_PostCommit_Python_ValidatesRunner_*" Jenkins jobs to GitHub Actions (#28439) * added beam_PostCommit_Python_ValidatesRunner jobs to GitHub Actions * added beam_PostCommit_Python_ValidatesRunner jobs to GitHub Actions * added beam_PostCommit_Python_ValidatesRunner jobs to GitHub Actions --- .github/workflows/README.md | 4 + ...Commit_Python_ValidatesRunner_Dataflow.yml | 111 ++++++++++++++++++ ...ostCommit_Python_ValidatesRunner_Flink.yml | 105 +++++++++++++++++ ...ostCommit_Python_ValidatesRunner_Samza.yml | 103 ++++++++++++++++ ...ostCommit_Python_ValidatesRunner_Spark.yml | 103 ++++++++++++++++ .../beam_PreCommit_Python_PVR_Flink.yml | 3 - sdks/python/tox.ini | 2 +- 7 files changed, 427 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml create mode 100644 .github/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml create mode 100644 .github/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml create mode 100644 .github/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 8636f513b71b..90bf3d8500c8 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -207,6 +207,10 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | [ PostCommit Python Examples Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml) | N/A |`Run Python Examples_Direct`| [![PostCommit Python Examples Direct](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml) | | [ PostCommit Python Examples Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml) | N/A |`Run Python Examples_Flink`| [![PostCommit Python Examples Flink](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml) | | [ PostCommit Python Examples Spark ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Spark.yml) | N/A |`Run Python Examples_Spark`| [![PostCommit Python Examples Spark](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Spark.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Spark.yml) | +| [ PostCommit Python ValidatesRunner Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml) | ['3.8','3.11'] |`Run Python Dataflow ValidatesRunner (matrix_element)`| [![PostCommit Python ValidatesRunner Dataflow](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml) | +| [ PostCommit Python ValidatesRunner Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml) | ['3.8','3.11'] |`Run Python Flink ValidatesRunner (matrix_element)`| [![PostCommit Python ValidatesRunner Flink](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml) | +| [ PostCommit Python ValidatesRunner Samza ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml) | ['3.8','3.11'] |`Run Python Samza ValidatesRunner (matrix_element)`| [![PostCommit Python ValidatesRunner Samza](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml) | +| [ PostCommit Python ValidatesRunner Spark ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml) | ['3.8','3.9','3.11'] |`Run Python Spark ValidatesRunner (matrix_element)`| [![PostCommit Python ValidatesRunner Spark](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml) | | [ PostCommit Website Publish](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml) | N/A | N/A | [![.github/workflows/beam_PostCommit_Website_Publish](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml) | | [ PostCommit XVR GoUsingJava Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml) | N/A |`Run XVR_GoUsingJava_Dataflow PostCommit`| [![.github/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml) | | [ PostCommit Sickbay Python ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Sickbay_Python.yml) | ['3.8','3.9','3.10','3.11'] |`Run Python PostCommit Sickbay tests (matrix_element)Upda`| [![PostCommit Sickbay Python ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Sickbay_Python.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Sickbay_Python.yml) | diff --git a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml new file mode 100644 index 000000000000..7e43717cb507 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml @@ -0,0 +1,111 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit Python ValidatesRunner Dataflow + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Python_ValidatesRunner_Dataflow: + if: | + github.event_name == 'schedule' || + github.event_name == 'workflow_dispatch' || + startsWith(github.event.comment.body, 'Run Python Dataflow ValidatesRunner') + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 200 + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + strategy: + fail-fast: false + matrix: + job_name: ["beam_PostCommit_Python_ValidatesRunner_Dataflow"] + job_phrase: ["Run Python Dataflow ValidatesRunner"] + python_version: ['3.8', '3.11'] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python_version }} + - name: Install Java + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: '8' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + with: + cache-read-only: false + - name: Set PY_VER_CLEAN + id: set_py_ver_clean + run: | + PY_VER=${{ matrix.python_version }} + PY_VER_CLEAN=${PY_VER//.} + echo "py_ver_clean=$PY_VER_CLEAN" >> $GITHUB_OUTPUT + - name: Run validatesRunnerBatchTests script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:python:test-suites:dataflow:py${{steps.set_py_ver_clean.outputs.py_ver_clean}}:validatesRunnerBatchTests + arguments: | + -PuseWheelDistribution \ + -PpythonVersion=${{ matrix.python_version }} \ + - name: Run validatesRunnerStreamingTests script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:python:test-suites:dataflow:py${{steps.set_py_ver_clean.outputs.py_ver_clean}}:validatesRunnerStreamingTests + arguments: | + -PuseWheelDistribution \ + -PpythonVersion=${{ matrix.python_version }} \ + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + with: + name: python-code-coverage-report + path: "**/pytest*.xml" \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml new file mode 100644 index 000000000000..dfb19f50737f --- /dev/null +++ b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml @@ -0,0 +1,105 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit Python ValidatesRunner Flink + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Python_ValidatesRunner_Flink: + if: | + github.event_name == 'schedule' || + github.event_name == 'workflow_dispatch' || + startsWith(github.event.comment.body, 'Run Python Flink ValidatesRunner') + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 100 + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + strategy: + fail-fast: false + matrix: + job_name: ["beam_PostCommit_Python_ValidatesRunner_Flink"] + job_phrase: ["Run Python Flink ValidatesRunner"] + python_version: ['3.8', '3.11'] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python_version }} + - name: Install Java + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: '8' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + with: + cache-read-only: false + - name: Set PY_VER_CLEAN + id: set_py_ver_clean + run: | + PY_VER=${{ matrix.python_version }} + PY_VER_CLEAN=${PY_VER//.} + echo "py_ver_clean=$PY_VER_CLEAN" >> $GITHUB_OUTPUT + - name: Run flinkValidatesRunner script + env: + CLOUDSDK_CONFIG: ${{ env.KUBELET_GCLOUD_CONFIG_PATH}} + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:python:test-suites:portable:py${{steps.set_py_ver_clean.outputs.py_ver_clean}}:flinkValidatesRunner + arguments: | + -PpythonVersion=${{ matrix.python_version }} \ + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + with: + name: python-code-coverage-report + path: "**/pytest*.xml" \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml new file mode 100644 index 000000000000..5878e1c34a53 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml @@ -0,0 +1,103 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit Python ValidatesRunner Samza + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Python_ValidatesRunner_Samza: + if: | + github.event_name == 'schedule' || + github.event_name == 'workflow_dispatch' || + startsWith(github.event.comment.body, 'Run Python Samza ValidatesRunner') + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 100 + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + strategy: + fail-fast: false + matrix: + job_name: ["beam_PostCommit_Python_ValidatesRunner_Samza"] + job_phrase: ["Run Python Samza ValidatesRunner"] + python_version: ['3.8', '3.11'] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python_version }} + - name: Install Java + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: '8' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + with: + cache-read-only: false + - name: Set PY_VER_CLEAN + id: set_py_ver_clean + run: | + PY_VER=${{ matrix.python_version }} + PY_VER_CLEAN=${PY_VER//.} + echo "py_ver_clean=$PY_VER_CLEAN" >> $GITHUB_OUTPUT + - name: Run samzaValidatesRunner script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:python:test-suites:portable:py${{steps.set_py_ver_clean.outputs.py_ver_clean}}:samzaValidatesRunner + arguments: | + -PpythonVersion=${{ matrix.python_version }} \ + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + with: + name: python-code-coverage-report + path: "**/pytest*.xml" \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml new file mode 100644 index 000000000000..20bc343cfcca --- /dev/null +++ b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml @@ -0,0 +1,103 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit Python ValidatesRunner Spark + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Python_ValidatesRunner_Spark: + if: | + github.event_name == 'schedule' || + github.event_name == 'workflow_dispatch' || + startsWith(github.event.comment.body, 'Run Python Spark ValidatesRunner') + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 100 + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + strategy: + fail-fast: false + matrix: + job_name: ["beam_PostCommit_Python_ValidatesRunner_Spark"] + job_phrase: ["Run Python Spark ValidatesRunner"] + python_version: ['3.8', '3.9', '3.11'] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python_version }} + - name: Install Java + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: '8' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + with: + cache-read-only: false + - name: Set PY_VER_CLEAN + id: set_py_ver_clean + run: | + PY_VER=${{ matrix.python_version }} + PY_VER_CLEAN=${PY_VER//.} + echo "py_ver_clean=$PY_VER_CLEAN" >> $GITHUB_OUTPUT + - name: Run sparkValidatesRunner script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:python:test-suites:portable:py${{steps.set_py_ver_clean.outputs.py_ver_clean}}:sparkValidatesRunner + arguments: | + -PpythonVersion=${{ matrix.python_version }} \ + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + with: + name: python-code-coverage-report + path: "**/pytest*.xml" \ No newline at end of file diff --git a/.github/workflows/beam_PreCommit_Python_PVR_Flink.yml b/.github/workflows/beam_PreCommit_Python_PVR_Flink.yml index 840abc69a86a..3dabf84fd505 100644 --- a/.github/workflows/beam_PreCommit_Python_PVR_Flink.yml +++ b/.github/workflows/beam_PreCommit_Python_PVR_Flink.yml @@ -98,9 +98,6 @@ jobs: uses: actions/setup-python@v4 with: python-version: 3.11 - - name: Configure passenv for tox - run: | - sed -i '/^\[testenv\]$/,/^\[/ s/^passenv=TERM/passenv=TERM,CLOUDSDK_CONFIG/' sdks/python/tox.ini - name: run Python PVR Flink PreCommit script uses: ./.github/actions/gradle-command-self-hosted-action env: diff --git a/sdks/python/tox.ini b/sdks/python/tox.ini index 1ce1c23c0600..1caf25caf080 100644 --- a/sdks/python/tox.ini +++ b/sdks/python/tox.ini @@ -31,7 +31,7 @@ select = E3 # https://github.com/apache/beam/issues/25668 pip_pre = True # allow apps that support color to use it. -passenv=TERM +passenv=TERM,CLOUDSDK_CONFIG # Set [] options for pip installation of apache-beam tarball. extras = test,dataframe # Don't warn that these commands aren't installed. From ec422dbfd217218554ad5f0cbf28716930470fed Mon Sep 17 00:00:00 2001 From: Andrey Devyatkin Date: Thu, 14 Sep 2023 20:46:31 +0200 Subject: [PATCH 004/105] Migrate "beam_PostCommit_Java_ValidatesRunner_*" Jenkins jobs to GitHub Actions (#28390) * # This is a combination of 2 commits. # This is the 1st commit message: added beam_PostCommit_Java_ValidatesRunner jobs to GitHub Actions # This is the commit message #2: Don't improperly filter newly-added elements that overlap with a delete. * merge master into beam_PostCommit_Java_ValidatesRunner branch * updated README file --- .github/workflows/README.md | 59 +++++---- ...stCommit_Java_ValidatesRunner_Dataflow.yml | 94 +++++++++++++++ ..._ValidatesRunner_Dataflow_JavaVersions.yml | 114 ++++++++++++++++++ ...ava_ValidatesRunner_Dataflow_Streaming.yml | 94 +++++++++++++++ ...ommit_Java_ValidatesRunner_Dataflow_V2.yml | 94 +++++++++++++++ ..._ValidatesRunner_Dataflow_V2_Streaming.yml | 94 +++++++++++++++ ...PostCommit_Java_ValidatesRunner_Direct.yml | 93 ++++++++++++++ ...va_ValidatesRunner_Direct_JavaVersions.yml | 109 +++++++++++++++++ ..._PostCommit_Java_ValidatesRunner_Flink.yml | 99 +++++++++++++++ ...mmit_Java_ValidatesRunner_Flink_Java11.yml | 112 +++++++++++++++++ ..._PostCommit_Java_ValidatesRunner_Samza.yml | 91 ++++++++++++++ ..._PostCommit_Java_ValidatesRunner_Spark.yml | 91 ++++++++++++++ ...lidatesRunner_SparkStructuredStreaming.yml | 91 ++++++++++++++ ...mmit_Java_ValidatesRunner_Spark_Java11.yml | 112 +++++++++++++++++ ...stCommit_Java_ValidatesRunner_Twister2.yml | 91 ++++++++++++++ ...am_PostCommit_Java_ValidatesRunner_ULR.yml | 95 +++++++++++++++ ...am_PostCommit_Python_Examples_Dataflow.yml | 1 - 17 files changed, 1510 insertions(+), 24 deletions(-) create mode 100644 .github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml create mode 100644 .github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_JavaVersions.yml create mode 100644 .github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml create mode 100644 .github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml create mode 100644 .github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml create mode 100644 .github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml create mode 100644 .github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_JavaVersions.yml create mode 100644 .github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml create mode 100644 .github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml create mode 100644 .github/workflows/beam_PostCommit_Java_ValidatesRunner_Samza.yml create mode 100644 .github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark.yml create mode 100644 .github/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml create mode 100644 .github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark_Java11.yml create mode 100644 .github/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml create mode 100644 .github/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 90bf3d8500c8..5513604f5cfc 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -180,40 +180,53 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex ```Run Python PreCommit (3.8)``` | Workflow name | Matrix | Trigger Phrase | Cron Status | |:-------------:|:------:|:--------------:|:-----------:| -| [ PostCommit BeamMetrics Publish ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_BeamMetrics_Publish.yml) | N/A |`Run Beam Metrics Deployment`| [![.github/workflows/beam_PostCommit_BeamMetrics_Publish](https://github.com/apache/beam/actions/workflows/beam_PostCommit_BeamMetrics_Publish.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_Dataflow_ARM.yml) -| [ PostCommit TransformService Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_TransformService_Direct.yml) | N/A |`Run TransformService_Direct PostCommit`| [![.github/workflows/beam_PostCommit_TransformService_Direct](https://github.com/apache/beam/actions/workflows/beam_PostCommit_TransformService_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_TransformService_Direct.yml) -| [ PostCommit Go ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go.yml) | N/A |`Run Go PostCommit`| [![.github/workflows/beam_PostCommit_Go](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go.yml) | -| [ PostCommit Go Dataflow ARM](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_Dataflow_ARM.yml) | N/A |`Run Go PostCommit Dataflow ARM`| [![.github/workflows/beam_PostCommit_Go_Dataflow_ARM](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_Dataflow_ARM.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_Dataflow_ARM.yml) | -| [ PostCommit Go VR Flink](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Flink.yml) | N/A |`Run Go Flink ValidatesRunner`| [![.github/workflows/beam_PostCommit_Go_VR_Flink](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Flink.yml) | -| [ PostCommit Go VR Samza](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Samza.yml) | N/A |`Run Go Samza ValidatesRunner`| [![.github/workflows/beam_PostCommit_Go_VR_Samza](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Samza.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Samza.yml) | -| [ PostCommit Go VR Spark](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Spark.yml) | N/A |`Run Go Spark ValidatesRunner`| [![.github/workflows/beam_PostCommit_Go_VR_Spark](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Spark.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Spark.yml) | -| [ PostCommit Java ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java.yml) | N/A |`Run Java PostCommit`| [![PostCommit Java](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java.yml) | -| [ PostCommit Java Examples Dataflow Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml) | N/A |`Run Java examples on Dataflow Java 11`| [![PostCommit Java Examples Dataflow Java11](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml) | -| [ PostCommit Java Avro Versions ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Avro_Versions.yml) | N/A |`Run Java Avro Versions PostCommit`| [![PostCommit Java Avro Versions](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Avro_Versions.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Avro_Versions.yml) | -| [ PostCommit Java Dataflow V1 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV1.yml) | N/A |`Run PostCommit_Java_Dataflow`| [![PostCommit Java Dataflow V1](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV1.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV1.yml) | -| [ PostCommit Java Dataflow V2 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV2.yml) | N/A |`Run PostCommit_Java_DataflowV2`| [![PostCommit Java Dataflow V2](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV2.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV2.yml) | -| [ PostCommit Java Examples Dataflow Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml) | N/A |`Run Java examples on Dataflow Java 11`| [![PostCommit Java Examples Dataflow Java11](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml) | -| [ PostCommit Java Examples Dataflow ARM ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml) | N/A |`Run Java_Examples_Dataflow_ARM PostCommit`| [![PostCommit Java Examples Dataflow ARM](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml) | -| [ PostCommit Java Examples Dataflow Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml) | N/A |`Run Java examples on Dataflow Java 11`| [![PostCommit Java Examples Dataflow Java11](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml) | -| [ PostCommit Java Examples Dataflow Java17 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java17.yml) | N/A |`Run Java examples on Dataflow Java 17`| [![PostCommit Java Examples Dataflow Java17](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java17.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java17.yml) | +| [ PostCommit BeamMetrics Publish ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_BeamMetrics_Publish.yml) | N/A |`Run Beam Metrics Deployment`| [![.github/workflows/beam_PostCommit_BeamMetrics_Publish.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_BeamMetrics_Publish.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_BeamMetrics_Publish.yml) +| [ PostCommit Go ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go.yml) | N/A |`Run Go PostCommit`| [![.github/workflows/beam_PostCommit_Go.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go.yml) | +| [ PostCommit Go Dataflow ARM](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_Dataflow_ARM.yml) | N/A |`Run Go PostCommit Dataflow ARM`| [![.github/workflows/beam_PostCommit_Go_Dataflow_ARM.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_Dataflow_ARM.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_Dataflow_ARM.yml) |[label](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_Dataflow_ARM.yml) +| [ PostCommit Go VR Flink](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Flink.yml) | N/A |`Run Go Flink ValidatesRunner`| [![.github/workflows/beam_PostCommit_Go_VR_Flink.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Flink.yml) | +| [ PostCommit Go VR Samza](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Samza.yml) | N/A |`Run Go Samza ValidatesRunner`| [![.github/workflows/beam_PostCommit_Go_VR_Samza.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Samza.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Samza.yml) | +| [ PostCommit Go VR Spark](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Spark.yml) | N/A |`Run Go Spark ValidatesRunner`| [![.github/workflows/beam_PostCommit_Go_VR_Spark.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Spark.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Spark.yml) | +| [ PostCommit Java ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java.yml) | N/A |`Run Java PostCommit`| [![.github/workflows/beam_PostCommit_Java.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java.yml) | +| [ PostCommit Java Avro Versions ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Avro_Versions.yml) | N/A |`Run Java Avro Versions PostCommit`| [![.github/workflows/beam_PostCommit_Java_Avro_Versions.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Avro_Versions.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Avro_Versions.yml) | +| [ PostCommit Java Dataflow V1 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV1.yml) | N/A |`Run PostCommit_Java_Dataflow`| [![.github/workflows/beam_PostCommit_Java_DataflowV1.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV1.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV1.yml) | +| [ PostCommit Java Dataflow V2 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV2.yml) | N/A |`Run PostCommit_Java_DataflowV2`| [![.github/workflows/beam_PostCommit_Java_DataflowV2.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV2.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV2.yml) | +| [ PostCommit Java Examples Dataflow ARM ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml) | N/A |`Run Java_Examples_Dataflow_ARM PostCommit`| [![.github/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml) | +| [ PostCommit Java Examples Dataflow Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml) | N/A |`Run Java examples on Dataflow Java 11`| [![.github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml) | +| [ PostCommit Java Examples Dataflow Java17 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java17.yml) | N/A |`Run Java examples on Dataflow Java 17`| [![.github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java17.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java17.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java17.yml) | | [ PostCommit Java Jpms Dataflow Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11.yml) | N/A |`Run Jpms Dataflow Java 11 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11.yml) | | [ PostCommit Java Jpms Dataflow Java17 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17.yml) | N/A |`Run Jpms Dataflow Java 17 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17.yml) | | [ PostCommit Java Jpms Direct Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java11.yml) | N/A |`Run Jpms Direct Java 11 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java11](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java11.yml) | | [ PostCommit Java Jpms Direct Java17 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml) | N/A |`Run Jpms Direct Java 17 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java17](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml) | | [ PostCommit Java Jpms Flink Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml) | N/A |`Run Jpms Flink Java 11 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Flink_Java11](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml) | | [ PostCommit Java Jpms Spark Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml) | N/A |`Run Jpms Spark Java 11 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Spark_Java11](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml) | -| [ PostCommit Java Sickbay ](https://github.com/apache/beam/actions/workflows/bbeam_PostCommit_Java_Sickbay.yml) | N/A |`Run Java Sickbay`| [![PostCommit Java Sickbay](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Sickbay.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/bbeam_PostCommit_Java_Sickbay.yml) | -| [ PostCommit Python Examples Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Dataflow.yml) | N/A |`Run Python Examples_Dataflow`| [![PostCommit Python Examples Dataflow](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Dataflow.yml) | -| [ PostCommit Python Examples Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml) | N/A |`Run Python Examples_Direct`| [![PostCommit Python Examples Direct](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml) | -| [ PostCommit Python Examples Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml) | N/A |`Run Python Examples_Flink`| [![PostCommit Python Examples Flink](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml) | -| [ PostCommit Python Examples Spark ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Spark.yml) | N/A |`Run Python Examples_Spark`| [![PostCommit Python Examples Spark](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Spark.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Spark.yml) | +| [ PostCommit Java Sickbay ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Sickbay.yml) | N/A |`Run Java Sickbay`| [![.github/workflows/beam_PostCommit_Java_Sickbay.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Sickbay.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Sickbay.yml) | +| [ PostCommit Java ValidatesRunner Dataflow JavaVersions ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Java.yml) | ['11','17'] |`Run Dataflow ValidatesRunner Java (matrix_element)`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Java.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Java.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Java.yml) | +| [ PostCommit Java ValidatesRunner Dataflow Streaming ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml) | N/A |`Run Dataflow Streaming ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml) | +| [ PostCommit Java ValidatesRunner Dataflow V2 Streaming ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml) | N/A |`Run Java Dataflow V2 ValidatesRunner Streaming`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml) | +| [ PostCommit Java ValidatesRunner Dataflow V2 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml) | N/A |`Run Java Dataflow V2 ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml) | +| [.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml) | N/A |`Run Dataflow ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml) | +| [ PostCommit Java ValidatesRunner Direct JavaVersions ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_Java.yml) | ['11','17'] |`Run Direct ValidatesRunner Java (matrix_element)`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_Java.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_Java.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_Java.yml) | +| [ PostCommit Java ValidatesRunner Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml) | N/A |`Run Direct ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml) | +| [ PostCommit Java ValidatesRunner Flink Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml) | N/A |`Run Flink ValidatesRunner Java 11`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml) | +| [ PostCommit Java ValidatesRunner Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml) | N/A |`Run Flink ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml) | +| [ PostCommit Java ValidatesRunner Samza ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Samza.yml) | N/A |`Run Samza ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Samza.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Samza.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Samza.yml) | +| [ PostCommit Java ValidatesRunner Spark Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Spark_Java11.yml) | N/A |`Run Spark ValidatesRunner Java 11`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark_Java11.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Spark_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Spark_Java11.yml) | +| [ PostCommit Java ValidatesRunner Spark ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Spark.yml) | N/A |`Run Spark ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Spark.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Spark.yml) | +| [ PostCommit Java ValidatesRunner SparkStructuredStreaming ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml) | N/A |`Run Spark StructuredStreaming ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml) | +| [ PostCommit Java ValidatesRunner Twister2 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml) | N/A |`Run Twister2 ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml) | +| [ PostCommit Java ValidatesRunner ULR ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml) | N/A |`Run ULR Loopback ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml) | +| [ PostCommit Python Examples Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Dataflow.yml) | N/A |`Run Python Examples_Dataflow`| [![.github/workflows/beam_PostCommit_Python_Examples_Dataflow.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Dataflow.yml) | +| [ PostCommit Python Examples Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml) | ['3.8','3.9','3.10','3.11'] |`Run Python Examples_Direct (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_Examples_Direct.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml) | +| [ PostCommit Python Examples Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml) | ['3.8','3.11'] |`Run Python Examples_Flink (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_Examples_Flink.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml) | +| [ PostCommit Python Examples Spark ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Spark.yml) | ['3.8','3.11'] |`Run Python Examples_Spark (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_Examples_Spark.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Spark.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Spark.yml) | | [ PostCommit Python ValidatesRunner Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml) | ['3.8','3.11'] |`Run Python Dataflow ValidatesRunner (matrix_element)`| [![PostCommit Python ValidatesRunner Dataflow](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml) | | [ PostCommit Python ValidatesRunner Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml) | ['3.8','3.11'] |`Run Python Flink ValidatesRunner (matrix_element)`| [![PostCommit Python ValidatesRunner Flink](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml) | | [ PostCommit Python ValidatesRunner Samza ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml) | ['3.8','3.11'] |`Run Python Samza ValidatesRunner (matrix_element)`| [![PostCommit Python ValidatesRunner Samza](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml) | | [ PostCommit Python ValidatesRunner Spark ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml) | ['3.8','3.9','3.11'] |`Run Python Spark ValidatesRunner (matrix_element)`| [![PostCommit Python ValidatesRunner Spark](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml) | -| [ PostCommit Website Publish](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml) | N/A | N/A | [![.github/workflows/beam_PostCommit_Website_Publish](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml) | +| [ PostCommit Sickbay Python ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Sickbay_Python.yml) | ['3.8','3.9','3.10','3.11'] |`Run Python (matrix_element) PostCommit Sickbay`| [![.github/workflows/beam_PostCommit_Sickbay_Python.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Sickbay_Python.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Sickbay_Python.yml) | +| [ PostCommit TransformService Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_TransformService_Direct.yml) | N/A |`Run TransformService_Direct PostCommit`| [![.github/workflows/beam_PostCommit_TransformService_Direct.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_TransformService_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_TransformService_Direct.yml) +| [ PostCommit Website Publish ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml) | N/A | N/A | [![.github/workflows/beam_PostCommit_Website_Publish.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml) | | [ PostCommit XVR GoUsingJava Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml) | N/A |`Run XVR_GoUsingJava_Dataflow PostCommit`| [![.github/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml) | -| [ PostCommit Sickbay Python ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Sickbay_Python.yml) | ['3.8','3.9','3.10','3.11'] |`Run Python PostCommit Sickbay tests (matrix_element)Upda`| [![PostCommit Sickbay Python ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Sickbay_Python.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Sickbay_Python.yml) | | [ PreCommit Community Metrics ](https://github.com/apache/beam/actions/workflows/beam_PreCommit_CommunityMetrics.yml) | N/A |`Run CommunityMetrics PreCommit`| [![.github/workflows/beam_PreCommit_CommunityMetrics.yml](https://github.com/apache/beam/actions/workflows/beam_PreCommit_CommunityMetrics.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PreCommit_CommunityMetrics.yml) | | [ PreCommit Go ](https://github.com/apache/beam/actions/workflows/beam_PreCommit_Go.yml) | N/A |`Run Go PreCommit`| [![.github/workflows/beam_PreCommit_Go.yml](https://github.com/apache/beam/actions/workflows/beam_PreCommit_Go.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PreCommit_Go.yml) | | [ PreCommit Java ](https://github.com/apache/beam/actions/workflows/beam_PreCommit_Java.yml) | N/A |`Run Java PreCommit`| [![.github/workflows/beam_PreCommit_Java.yml](https://github.com/apache/beam/actions/workflows/beam_PreCommit_Java.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PreCommit_Java.yml) | diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml new file mode 100644 index 000000000000..b7f87fe20133 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml @@ -0,0 +1,94 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java ValidatesRunner Dataflow + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_ValidatesRunner_Dataflow: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 480 + strategy: + matrix: + job_name: [beam_PostCommit_Java_ValidatesRunner_Dataflow] + job_phrase: [Run Dataflow ValidatesRunner] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Dataflow ValidatesRunner' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Java + uses: actions/setup-java@v3.8.0 + with: + distribution: 'zulu' + java-version: '8' + - name: run validatesRunner script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:google-cloud-dataflow-java:validatesRunner + max-workers: 12 + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_JavaVersions.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_JavaVersions.yml new file mode 100644 index 000000000000..08740fad64ad --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_JavaVersions.yml @@ -0,0 +1,114 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java ValidatesRunner Dataflow JavaVersions + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_ValidatesRunner_Dataflow_JavaVersions: + name: ${{matrix.job_name}} (${{matrix.job_phrase}} ${{matrix.java_version}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 480 + strategy: + fail-fast: false + matrix: + job_name: [beam_PostCommit_Java_ValidatesRunner_Dataflow_JavaVersions] + job_phrase: [Run Dataflow ValidatesRunner Java] + java_version: ['11','17'] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + startswith(github.event.comment.body, 'Run Dataflow ValidatesRunner Java') + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} ${{matrix.java_version}} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) ${{matrix.java_version}} + - name: Set up Java${{ matrix.java_version }} + uses: actions/setup-java@v3.8.0 + with: + distribution: 'temurin' + java-version: | + ${{ matrix.java_version }} + 8 + - name: run jar Java${{ matrix.java_version }} script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:google-cloud-dataflow-java:testJar :runners:google-cloud-dataflow-java:worker:shadowJar + arguments: | + -Dorg.gradle.java.home=$JAVA_HOME_8_X64 \ + - name: run validatesRunner Java${{ matrix.java_version }} script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:google-cloud-dataflow-java:validatesRunner + arguments: | + -x shadowJar \ + -x shadowTestJar \ + -x compileJava \ + -x compileTestJava \ + -x jar \ + -x testJar \ + -x classes \ + -x testClasses \ + -Dorg.gradle.java.home=$JAVA_HOME_${{ matrix.java_version }}_X64 \ + max-workers: 12 + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml new file mode 100644 index 000000000000..b58ffb72ed76 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml @@ -0,0 +1,94 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java ValidatesRunner Dataflow Streaming + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 720 + strategy: + matrix: + job_name: [beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming] + job_phrase: [Run Dataflow Streaming ValidatesRunner] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Dataflow Streaming ValidatesRunner' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Java + uses: actions/setup-java@v3.8.0 + with: + distribution: 'zulu' + java-version: '8' + - name: run validatesRunnerStreaming script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:google-cloud-dataflow-java:validatesRunnerStreaming + max-workers: 12 + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml new file mode 100644 index 000000000000..eef664fdf0aa --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml @@ -0,0 +1,94 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java ValidatesRunner Dataflow V2 + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */8 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_ValidatesRunner_Dataflow_V2: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 390 + strategy: + matrix: + job_name: [beam_PostCommit_Java_ValidatesRunner_Dataflow_V2] + job_phrase: [Run Java Dataflow V2 ValidatesRunner] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Java Dataflow V2 ValidatesRunner' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Java + uses: actions/setup-java@v3.8.0 + with: + distribution: 'zulu' + java-version: '8' + - name: run validatesRunnerV2 script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:google-cloud-dataflow-java:validatesRunnerV2 + max-workers: 12 + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml new file mode 100644 index 000000000000..df71c3079a5c --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml @@ -0,0 +1,94 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java ValidatesRunner Dataflow V2 Streaming + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */8 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 510 + strategy: + matrix: + job_name: [beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming] + job_phrase: [Run Java Dataflow V2 ValidatesRunner Streaming] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Java Dataflow V2 ValidatesRunner Streaming' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Java + uses: actions/setup-java@v3.8.0 + with: + distribution: 'zulu' + java-version: '8' + - name: run validatesRunnerV2Streaming script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:google-cloud-dataflow-java:validatesRunnerV2Streaming + max-workers: 12 + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml new file mode 100644 index 000000000000..6a260ad4595e --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml @@ -0,0 +1,93 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java ValidatesRunner Direct + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_ValidatesRunner_Direct: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 180 + strategy: + matrix: + job_name: [beam_PostCommit_Java_ValidatesRunner_Direct] + job_phrase: [Run Direct ValidatesRunner] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Direct ValidatesRunner' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Java + uses: actions/setup-java@v3.8.0 + with: + distribution: 'zulu' + java-version: '8' + - name: run validatesRunner script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:direct-java:validatesRunner + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_JavaVersions.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_JavaVersions.yml new file mode 100644 index 000000000000..bb73a15b71b9 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_JavaVersions.yml @@ -0,0 +1,109 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java ValidatesRunner Direct JavaVersions + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_ValidatesRunner_Direct_JavaVersions: + name: ${{matrix.job_name}} (${{matrix.job_phrase}} ${{matrix.java_version}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 480 + strategy: + fail-fast: false + matrix: + job_name: [beam_PostCommit_Java_ValidatesRunner_Direct_JavaVersions] + job_phrase: [Run Direct ValidatesRunner Java] + java_version: ['11','17'] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + startswith(github.event.comment.body, 'Run Direct ValidatesRunner Java') + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} ${{matrix.java_version}} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) ${{matrix.java_version}} + - name: Set up Java${{ matrix.java_version }} + uses: actions/setup-java@v3.8.0 + with: + distribution: 'temurin' + java-version: | + ${{ matrix.java_version }} + 8 + - name: run jar Java${{ matrix.java_version }} script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:direct-java:shadowJar :runners:direct-java:shadowTestJar + arguments: | + -Dorg.gradle.java.home=$JAVA_HOME_8_X64 \ + - name: run validatesRunner Java${{ matrix.java_version }} script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:direct-java:validatesRunner + arguments: | + -x shadowJar \ + -x shadowTestJar \ + -x compileJava \ + -x compileTestJava \ + -Dorg.gradle.java.home=$JAVA_HOME_${{ matrix.java_version }}_X64 \ + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml new file mode 100644 index 000000000000..0c273713def3 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml @@ -0,0 +1,99 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit Java ValidatesRunner Flink + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_ValidatesRunner_Flink: + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 100 + strategy: + matrix: + job_name: [beam_PostCommit_Java_ValidatesRunner_Flink] + job_phrase: [Run Flink ValidatesRunner] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Flink ValidatesRunner' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Java + uses: actions/setup-java@v3.8.0 + with: + distribution: 'zulu' + java-version: '8' + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: '3.8' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + with: + cache-read-only: false + - name: run validatesRunner script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:flink:1.15:validatesRunner + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml new file mode 100644 index 000000000000..68486868756b --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml @@ -0,0 +1,112 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java ValidatesRunner Flink Java11 + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_ValidatesRunner_Flink_Java11: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 270 + strategy: + matrix: + job_name: [beam_PostCommit_Java_ValidatesRunner_Flink_Java11] + job_phrase: [Run Flink ValidatesRunner Java 11] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + startswith(github.event.comment.body, 'Run Flink ValidatesRunner Java 11') + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Set up Java + uses: actions/setup-java@v3.8.0 + with: + distribution: 'temurin' + java-version: | + 11 + 8 + - name: run jar Java8 script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:flink:1.15:jar :runners:flink:1.15:testJar + arguments: | + -Dorg.gradle.java.home=$JAVA_HOME_8_X64 \ + - name: run validatesRunner Java11 script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:flink:1.15:validatesRunner + arguments: | + -x shadowJar \ + -x shadowTestJar \ + -x compileJava \ + -x compileTestJava \ + -x jar \ + -x testJar \ + -x classes \ + -x testClasses \ + -Dorg.gradle.java.home=$JAVA_HOME_11_X64 \ + max-workers: 12 + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Samza.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Samza.yml new file mode 100644 index 000000000000..5d17fbc61346 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Samza.yml @@ -0,0 +1,91 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit Java ValidatesRunner Samza + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_ValidatesRunner_Samza: + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 100 + strategy: + matrix: + job_name: [beam_PostCommit_Java_ValidatesRunner_Samza] + job_phrase: [Run Samza ValidatesRunner] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Samza ValidatesRunner' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Java + uses: actions/setup-java@v3.8.0 + with: + distribution: 'zulu' + java-version: '8' + - name: run validatesRunner script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:samza:validatesRunner + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark.yml new file mode 100644 index 000000000000..3204d9ad6ed5 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark.yml @@ -0,0 +1,91 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit Java ValidatesRunner Spark + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_ValidatesRunner_Spark: + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 120 + strategy: + matrix: + job_name: [beam_PostCommit_Java_ValidatesRunner_Spark] + job_phrase: [Run Spark ValidatesRunner] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Spark ValidatesRunner' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Java + uses: actions/setup-java@v3.8.0 + with: + distribution: 'zulu' + java-version: '8' + - name: run validatesRunner script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:spark:3:validatesRunner + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml new file mode 100644 index 000000000000..7e97f50dcac3 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml @@ -0,0 +1,91 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit Java ValidatesRunner SparkStructuredStreaming + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming: + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 120 + strategy: + matrix: + job_name: [beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming] + job_phrase: [Run Spark StructuredStreaming ValidatesRunner] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Spark StructuredStreaming ValidatesRunner' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Java + uses: actions/setup-java@v3.8.0 + with: + distribution: 'zulu' + java-version: '8' + - name: run validatesStructuredStreamingRunnerBatch script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:spark:3:validatesStructuredStreamingRunnerBatch + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark_Java11.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark_Java11.yml new file mode 100644 index 000000000000..ce63b24d2c33 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark_Java11.yml @@ -0,0 +1,112 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java ValidatesRunner Spark Java11 + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_ValidatesRunner_Spark_Java11: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 270 + strategy: + matrix: + job_name: [beam_PostCommit_Java_ValidatesRunner_Spark_Java11] + job_phrase: [Run Spark ValidatesRunner Java 11] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + startswith(github.event.comment.body, 'Run Spark ValidatesRunner Java 11') + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Set up Java + uses: actions/setup-java@v3.8.0 + with: + distribution: 'temurin' + java-version: | + 11 + 8 + - name: run jar Java8 script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:spark:3:jar :runners:spark:3:testJar + arguments: | + -Dorg.gradle.java.home=$JAVA_HOME_8_X64 \ + - name: run validatesRunner Java11 script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:spark:3:validatesRunner + arguments: | + -x shadowJar \ + -x shadowTestJar \ + -x compileJava \ + -x compileTestJava \ + -x jar \ + -x testJar \ + -x classes \ + -x testClasses \ + -Dorg.gradle.java.home=$JAVA_HOME_11_X64 \ + max-workers: 12 + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml new file mode 100644 index 000000000000..1f13269e7b95 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml @@ -0,0 +1,91 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit Java ValidatesRunner Twister2 + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_ValidatesRunner_Twister2: + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 120 + strategy: + matrix: + job_name: [beam_PostCommit_Java_ValidatesRunner_Twister2] + job_phrase: [Run Twister2 ValidatesRunner] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Twister2 ValidatesRunner' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Java + uses: actions/setup-java@v3.8.0 + with: + distribution: 'zulu' + java-version: '8' + - name: run validatesRunner script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:twister2:validatesRunner + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml new file mode 100644 index 000000000000..65b20d5e2a66 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml @@ -0,0 +1,95 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit Java ValidatesRunner ULR + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_ValidatesRunner_ULR: + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 180 + strategy: + matrix: + job_name: [beam_PostCommit_Java_ValidatesRunner_ULR] + job_phrase: [Run ULR Loopback ValidatesRunner] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run ULR Loopback ValidatesRunner' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Java + uses: actions/setup-java@v3.8.0 + with: + distribution: 'zulu' + java-version: '8' + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: '3.8' + - name: run ulrLoopbackValidatesRunner script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:portability:java:ulrLoopbackValidatesRunner + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Python_Examples_Dataflow.yml b/.github/workflows/beam_PostCommit_Python_Examples_Dataflow.yml index 2aba1760606c..811f1f098ebf 100644 --- a/.github/workflows/beam_PostCommit_Python_Examples_Dataflow.yml +++ b/.github/workflows/beam_PostCommit_Python_Examples_Dataflow.yml @@ -58,7 +58,6 @@ jobs: runs-on: [self-hosted, ubuntu-20.04, main] timeout-minutes: 180 strategy: - fail-fast: false matrix: job_name: ["beam_PostCommit_Python_Examples_Dataflow"] job_phrase: ["Run Python Examples_Dataflow"] From 65fc174a8ab444237b17e3c0aeecf1af0e03cae4 Mon Sep 17 00:00:00 2001 From: Yi Hu Date: Thu, 14 Sep 2023 15:36:04 -0400 Subject: [PATCH 005/105] Remove more Jenkins PreCommit (#28427) --- .../job_PreCommit_CommunityMetrics.groovy | 27 -------------- .test-infra/jenkins/job_PreCommit_Go.groovy | 31 ---------------- .../jenkins/job_PreCommit_Go_Portable.groovy | 31 ---------------- .../job_PreCommit_Kotlin_Examples.groovy | 35 ------------------- .../job_PreCommit_PythonAutoformatter.groovy | 29 --------------- .../jenkins/job_PreCommit_Typescript.groovy | 29 --------------- 6 files changed, 182 deletions(-) delete mode 100644 .test-infra/jenkins/job_PreCommit_CommunityMetrics.groovy delete mode 100644 .test-infra/jenkins/job_PreCommit_Go.groovy delete mode 100644 .test-infra/jenkins/job_PreCommit_Go_Portable.groovy delete mode 100644 .test-infra/jenkins/job_PreCommit_Kotlin_Examples.groovy delete mode 100644 .test-infra/jenkins/job_PreCommit_PythonAutoformatter.groovy delete mode 100644 .test-infra/jenkins/job_PreCommit_Typescript.groovy diff --git a/.test-infra/jenkins/job_PreCommit_CommunityMetrics.groovy b/.test-infra/jenkins/job_PreCommit_CommunityMetrics.groovy deleted file mode 100644 index 2bf63df9cd5a..000000000000 --- a/.test-infra/jenkins/job_PreCommit_CommunityMetrics.groovy +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import PrecommitJobBuilder - -PrecommitJobBuilder builder = new PrecommitJobBuilder( - scope: this, - nameBase: 'CommunityMetrics', - gradleTask: ':communityMetricsPreCommit', - triggerPathPatterns: ['^.test-infra/metrics/.*$']) -builder.build() - diff --git a/.test-infra/jenkins/job_PreCommit_Go.groovy b/.test-infra/jenkins/job_PreCommit_Go.groovy deleted file mode 100644 index 73b6cf4c9384..000000000000 --- a/.test-infra/jenkins/job_PreCommit_Go.groovy +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import PrecommitJobBuilder - -PrecommitJobBuilder builder = new PrecommitJobBuilder( - scope: this, - nameBase: 'Go', - gradleTask: ':goPreCommit', - triggerPathPatterns: [ - '^model/.*$', - '^sdks/go.*$', - '^release/.*$', - ] - ) -builder.build() diff --git a/.test-infra/jenkins/job_PreCommit_Go_Portable.groovy b/.test-infra/jenkins/job_PreCommit_Go_Portable.groovy deleted file mode 100644 index 12c762e5eb37..000000000000 --- a/.test-infra/jenkins/job_PreCommit_Go_Portable.groovy +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import PrecommitJobBuilder - -PrecommitJobBuilder builder = new PrecommitJobBuilder( - scope: this, - nameBase: 'GoPortable', - gradleTask: ':goPortablePreCommit', - triggerPathPatterns: [ - '^model/.*$', - '^sdks/go.*$', - '^release/.*$', - ] - ) -builder.build() diff --git a/.test-infra/jenkins/job_PreCommit_Kotlin_Examples.groovy b/.test-infra/jenkins/job_PreCommit_Kotlin_Examples.groovy deleted file mode 100644 index c3ce31f1f6d1..000000000000 --- a/.test-infra/jenkins/job_PreCommit_Kotlin_Examples.groovy +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import PrecommitJobBuilder - -PrecommitJobBuilder builder = new PrecommitJobBuilder( - scope: this, - nameBase: 'Kotlin_Examples', - gradleTask: ':examples:kotlin:preCommit', - triggerPathPatterns: [ - '^model/.*$', - '^sdks/java/.*$', - '^runners/flink/.*$', - '^runners/spark/.*$', - '^runners/direct-java/.*$', - '^examples/kotlin/.*$', - '^release/.*$', - ] - ) -builder.build() diff --git a/.test-infra/jenkins/job_PreCommit_PythonAutoformatter.groovy b/.test-infra/jenkins/job_PreCommit_PythonAutoformatter.groovy deleted file mode 100644 index 90e037edf2af..000000000000 --- a/.test-infra/jenkins/job_PreCommit_PythonAutoformatter.groovy +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import PrecommitJobBuilder - -PrecommitJobBuilder builder = new PrecommitJobBuilder( - scope: this, - nameBase: 'PythonFormatter', - gradleTask: ':pythonFormatterPreCommit', - triggerPathPatterns: [ - '^sdks/python/apache_beam/.*$', - ] - ) -builder.build() diff --git a/.test-infra/jenkins/job_PreCommit_Typescript.groovy b/.test-infra/jenkins/job_PreCommit_Typescript.groovy deleted file mode 100644 index 29cb93ede8b6..000000000000 --- a/.test-infra/jenkins/job_PreCommit_Typescript.groovy +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import PrecommitJobBuilder - -PrecommitJobBuilder builder = new PrecommitJobBuilder( - scope: this, - nameBase: 'Typescript', - gradleTask: ':typescriptPreCommit', - triggerPathPatterns: [ - '^sdks/python/apache_beam/runners/interactive/extensions/.*$', - ] - ) -builder.build() From 9bd5fbfdb8a1aa61ba5c85ca4a432034e26e1b7c Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Thu, 14 Sep 2023 16:23:51 -0400 Subject: [PATCH 006/105] Consolidate env blocks for stage website precommit (#28458) --- .github/workflows/beam_PreCommit_Website_Stage_GCS.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/beam_PreCommit_Website_Stage_GCS.yml b/.github/workflows/beam_PreCommit_Website_Stage_GCS.yml index da2ba216994e..119ece802164 100644 --- a/.github/workflows/beam_PreCommit_Website_Stage_GCS.yml +++ b/.github/workflows/beam_PreCommit_Website_Stage_GCS.yml @@ -38,6 +38,8 @@ env: GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + gcsbucket: apache-beam-website-pull-requests + ghprbPullId: #Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event permissions: @@ -55,10 +57,6 @@ permissions: security-events: read statuses: read -env: - gcsbucket: apache-beam-website-pull-requests - ghprbPullId: - jobs: beam_PreCommit_Website_Stage_GCS: name: ${{matrix.job_name}} (${{matrix.job_phrase}}) From 8f60924a3d5303d966ddaf80ee6ee7806edc346f Mon Sep 17 00:00:00 2001 From: Svetak Sundhar Date: Thu, 14 Sep 2023 16:42:22 -0400 Subject: [PATCH 007/105] Bump Healthcare API (#28457) * Create HealthcareUtils file with shared resources * revert * Bump HCLS API --- .../main/groovy/org/apache/beam/gradle/BeamModulePlugin.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/groovy/org/apache/beam/gradle/BeamModulePlugin.groovy b/buildSrc/src/main/groovy/org/apache/beam/gradle/BeamModulePlugin.groovy index a3c5bcf226fc..9f341c5673fd 100644 --- a/buildSrc/src/main/groovy/org/apache/beam/gradle/BeamModulePlugin.groovy +++ b/buildSrc/src/main/groovy/org/apache/beam/gradle/BeamModulePlugin.groovy @@ -724,7 +724,7 @@ class BeamModulePlugin implements Plugin { // Keep version consistent with the version in google_cloud_resourcemanager, managed by google_cloud_platform_libraries_bom google_api_services_cloudresourcemanager : "com.google.apis:google-api-services-cloudresourcemanager:v1-rev20230129-$google_clients_version", google_api_services_dataflow : "com.google.apis:google-api-services-dataflow:v1b3-rev20220920-$google_clients_version", - google_api_services_healthcare : "com.google.apis:google-api-services-healthcare:v1-rev20230817-$google_clients_version", + google_api_services_healthcare : "com.google.apis:google-api-services-healthcare:v1-rev20230830-$google_clients_version", google_api_services_pubsub : "com.google.apis:google-api-services-pubsub:v1-rev20220904-$google_clients_version", // Keep version consistent with the version in google_cloud_nio, managed by google_cloud_platform_libraries_bom google_api_services_storage : "com.google.apis:google-api-services-storage:v1-rev20230617-$google_clients_version", From 5df59a943d18723942cd0531c12bef256ef90e20 Mon Sep 17 00:00:00 2001 From: Sam sam Date: Thu, 14 Sep 2023 14:21:24 -0700 Subject: [PATCH 008/105] Add flag to control automatic exception sampling in Java (disabled) (#28403) * Add flag to control automatic exception sampling in Java (disabled) * Fix NPE in FnHarness with DataSampler * trigger tests * trigger tests * trigger tests * trigger tests --------- Co-authored-by: Sam Rohde --- .../org/apache/beam/fn/harness/FnHarness.java | 17 +-- .../beam/fn/harness/debug/DataSampler.java | 63 ++++++++++- .../beam/fn/harness/debug/OutputSampler.java | 9 +- .../fn/harness/debug/DataSamplerTest.java | 106 +++++++++++++++++- .../fn/harness/debug/OutputSamplerTest.java | 47 ++++++-- 5 files changed, 216 insertions(+), 26 deletions(-) diff --git a/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/FnHarness.java b/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/FnHarness.java index 3f018c376f08..448c8d42df75 100644 --- a/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/FnHarness.java +++ b/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/FnHarness.java @@ -97,7 +97,6 @@ public class FnHarness { private static final String PIPELINE_OPTIONS_FILE = "PIPELINE_OPTIONS_FILE"; private static final String PIPELINE_OPTIONS = "PIPELINE_OPTIONS"; private static final String RUNNER_CAPABILITIES = "RUNNER_CAPABILITIES"; - private static final String ENABLE_DATA_SAMPLING_EXPERIMENT = "enable_data_sampling"; private static final Logger LOG = LoggerFactory.getLogger(FnHarness.class); private static Endpoints.ApiServiceDescriptor getApiServiceDescriptor(String descriptor) @@ -248,7 +247,8 @@ public static void main( options.as(ExecutorOptions.class).getScheduledExecutorService(); ExecutionStateSampler executionStateSampler = new ExecutionStateSampler(options, System::currentTimeMillis); - final DataSampler dataSampler = new DataSampler(); + + final @Nullable DataSampler dataSampler = DataSampler.create(options); // The logging client variable is not used per se, but during its lifetime (until close()) it // intercepts logging and sends it to the logging service. @@ -276,10 +276,6 @@ public static void main( FinalizeBundleHandler finalizeBundleHandler = new FinalizeBundleHandler(executorService); - // Create the sampler, if the experiment is enabled. - boolean shouldSample = - ExperimentalOptions.hasExperiment(options, ENABLE_DATA_SAMPLING_EXPERIMENT); - // Retrieves the ProcessBundleDescriptor from cache. Requests the PBD from the Runner if it // doesn't exist. Additionally, runs any graph modifications. Function getProcessBundleDescriptor = @@ -314,7 +310,7 @@ private BeamFnApi.ProcessBundleDescriptor loadDescriptor(String id) { metricsShortIds, executionStateSampler, processWideCache, - shouldSample ? dataSampler : null); + dataSampler); logging.setProcessBundleHandler(processBundleHandler); BeamFnStatusClient beamFnStatusClient = null; @@ -363,7 +359,12 @@ private BeamFnApi.ProcessBundleDescriptor loadDescriptor(String id) { InstructionRequest.RequestCase.HARNESS_MONITORING_INFOS, processWideHandler::harnessMonitoringInfos); handlers.put( - InstructionRequest.RequestCase.SAMPLE_DATA, dataSampler::handleDataSampleRequest); + InstructionRequest.RequestCase.SAMPLE_DATA, + request -> + dataSampler == null + ? BeamFnApi.InstructionResponse.newBuilder() + .setSampleData(BeamFnApi.SampleDataResponse.newBuilder()) + : dataSampler.handleDataSampleRequest(request)); JvmInitializers.runBeforeProcessing(options); diff --git a/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/debug/DataSampler.java b/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/debug/DataSampler.java index b03c453475bd..29011b82a4dc 100644 --- a/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/debug/DataSampler.java +++ b/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/debug/DataSampler.java @@ -23,9 +23,12 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import javax.annotation.Nullable; import org.apache.beam.model.fnexecution.v1.BeamFnApi; import org.apache.beam.model.fnexecution.v1.BeamFnApi.SampleDataResponse.ElementList; import org.apache.beam.sdk.coders.Coder; +import org.apache.beam.sdk.options.ExperimentalOptions; +import org.apache.beam.sdk.options.PipelineOptions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,19 +40,66 @@ */ public class DataSampler { private static final Logger LOG = LoggerFactory.getLogger(DataSampler.class); + private static final String ENABLE_DATA_SAMPLING_EXPERIMENT = "enable_data_sampling"; + private static final String ENABLE_ALWAYS_ON_EXCEPTION_SAMPLING_EXPERIMENT = + "enable_always_on_exception_sampling"; + private static final String DISABLE_ALWAYS_ON_EXCEPTION_SAMPLING_EXPERIMENT = + "disable_always_on_exception_sampling"; + + /** + * Optionally returns a DataSampler if the experiment "enable_data_sampling" is present or + * "enable_always_on_exception_sampling" is present. Returns null is data sampling is not enabled + * or "disable_always_on_exception_sampling" experiment is given. + * + * @param options the pipeline options given to this SDK Harness. + * @return the DataSampler if enabled or null, otherwise. + */ + public static @Nullable DataSampler create(PipelineOptions options) { + boolean disableAlwaysOnExceptionSampling = + ExperimentalOptions.hasExperiment(options, DISABLE_ALWAYS_ON_EXCEPTION_SAMPLING_EXPERIMENT); + boolean enableAlwaysOnExceptionSampling = + ExperimentalOptions.hasExperiment(options, ENABLE_ALWAYS_ON_EXCEPTION_SAMPLING_EXPERIMENT); + boolean enableDataSampling = + ExperimentalOptions.hasExperiment(options, ENABLE_DATA_SAMPLING_EXPERIMENT); + // Enable exception sampling, unless the user specifies for it to be disabled. + enableAlwaysOnExceptionSampling = + enableAlwaysOnExceptionSampling && !disableAlwaysOnExceptionSampling; + + // If no sampling is enabled, don't create the DataSampler. + if (enableDataSampling || enableAlwaysOnExceptionSampling) { + // For performance reasons, sampling all elements should only be done when the user requests + // it. + // But, exception sampling doesn't need to worry about performance implications, since the SDK + // is already in a bad state. Thus, enable only exception sampling when the user does not + // request for the sampling of all elements. + boolean onlySampleExceptions = enableAlwaysOnExceptionSampling && !enableDataSampling; + return new DataSampler(onlySampleExceptions); + } else { + return null; + } + } /** * Creates a DataSampler to sample every 1000 elements while keeping a maximum of 10 in memory. */ public DataSampler() { - this(10, 1000); + this(10, 1000, false); + } + + /** + * Creates a DataSampler to sample every 1000 elements while keeping a maximum of 10 in memory. + * + * @param onlySampleExceptions If true, only samples elements from exceptions. + */ + public DataSampler(Boolean onlySampleExceptions) { + this(10, 1000, onlySampleExceptions); } /** * @param maxSamples Sets the maximum number of samples held in memory at once. * @param sampleEveryN Sets how often to sample. */ - public DataSampler(int maxSamples, int sampleEveryN) { + public DataSampler(int maxSamples, int sampleEveryN, Boolean onlySampleExceptions) { checkArgument( maxSamples > 0, "Expected positive number of samples, did you mean to disable data sampling?"); @@ -58,6 +108,7 @@ public DataSampler(int maxSamples, int sampleEveryN) { "Expected positive number for sampling period, did you mean to disable data sampling?"); this.maxSamples = maxSamples; this.sampleEveryN = sampleEveryN; + this.onlySampleExceptions = onlySampleExceptions; } // Maximum number of elements in buffer. @@ -66,6 +117,9 @@ public DataSampler(int maxSamples, int sampleEveryN) { // Sampling rate. private final int sampleEveryN; + // If true, only takes samples when exceptions in UDFs occur. + private final Boolean onlySampleExceptions; + // The fully-qualified type is: Map[PCollectionId, OutputSampler]. In order to sample // on a PCollection-basis and not per-bundle, this keeps track of shared samples between states. private final Map> outputSamplers = new ConcurrentHashMap<>(); @@ -86,7 +140,10 @@ public DataSampler(int maxSamples, int sampleEveryN) { public OutputSampler sampleOutput(String pcollectionId, Coder coder) { return (OutputSampler) outputSamplers.computeIfAbsent( - pcollectionId, k -> new OutputSampler<>(coder, this.maxSamples, this.sampleEveryN)); + pcollectionId, + k -> + new OutputSampler<>( + coder, this.maxSamples, this.sampleEveryN, this.onlySampleExceptions)); } /** diff --git a/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/debug/OutputSampler.java b/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/debug/OutputSampler.java index f1e710d3ec7e..f7fabae0cc21 100644 --- a/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/debug/OutputSampler.java +++ b/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/debug/OutputSampler.java @@ -60,14 +60,19 @@ public class OutputSampler { // Index into the buffer of where to overwrite samples. private int resampleIndex = 0; + // If true, only takes samples when exceptions in UDFs occur. + private final Boolean onlySampleExceptions; + @Nullable private final Coder valueCoder; @Nullable private final Coder> windowedValueCoder; - public OutputSampler(Coder coder, int maxElements, int sampleEveryN) { + public OutputSampler( + Coder coder, int maxElements, int sampleEveryN, boolean onlySampleExceptions) { this.maxElements = maxElements; this.sampleEveryN = sampleEveryN; this.buffer = new ArrayList<>(this.maxElements); + this.onlySampleExceptions = onlySampleExceptions; // The samples taken and encoded should match exactly to the specification from the // ProcessBundleDescriptor. The coder given can either be a WindowedValueCoder, in which the @@ -103,7 +108,7 @@ public ElementSample sample(WindowedValue element) { ElementSample elementSample = new ElementSample<>(ThreadLocalRandom.current().nextInt(), element); - if (samples > 10 && samples % sampleEveryN != 0) { + if (onlySampleExceptions || (samples > 10 && samples % sampleEveryN != 0)) { return elementSample; } diff --git a/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/debug/DataSamplerTest.java b/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/debug/DataSamplerTest.java index a05efca120ab..1cad5210380b 100644 --- a/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/debug/DataSamplerTest.java +++ b/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/debug/DataSamplerTest.java @@ -21,6 +21,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.ByteArrayOutputStream; @@ -32,10 +33,13 @@ import java.util.Map; import java.util.concurrent.CountDownLatch; import org.apache.beam.model.fnexecution.v1.BeamFnApi; +import org.apache.beam.model.fnexecution.v1.BeamFnApi.SampledElement; import org.apache.beam.sdk.coders.ByteArrayCoder; import org.apache.beam.sdk.coders.Coder; import org.apache.beam.sdk.coders.StringUtf8Coder; import org.apache.beam.sdk.coders.VarIntCoder; +import org.apache.beam.sdk.options.ExperimentalOptions; +import org.apache.beam.sdk.options.PipelineOptionsFactory; import org.apache.beam.sdk.util.WindowedValue; import org.apache.beam.vendor.grpc.v1p54p0.com.google.protobuf.ByteString; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.ImmutableList; @@ -117,6 +121,21 @@ void assertHasSamples( assertTrue(elementList.getElementsList().containsAll(expectedSamples)); } + void assertHasSamples( + BeamFnApi.InstructionResponse response, + String pcollection, + List elements) { + Map elementSamplesMap = + response.getSampleData().getElementSamplesMap(); + + assertFalse(elementSamplesMap.isEmpty()); + + BeamFnApi.SampleDataResponse.ElementList elementList = elementSamplesMap.get(pcollection); + assertNotNull(elementList); + + assertTrue(elementList.getElementsList().containsAll(elements)); + } + /** * Smoke test that a samples show in the output map. * @@ -203,7 +222,7 @@ void generateStringSamples(DataSampler sampler) { */ @Test public void testFiltersSinglePCollectionId() throws Exception { - DataSampler sampler = new DataSampler(10, 10); + DataSampler sampler = new DataSampler(10, 10, false); generateStringSamples(sampler); BeamFnApi.InstructionResponse samples = getSamplesForPCollection(sampler, "a"); @@ -219,7 +238,7 @@ public void testFiltersSinglePCollectionId() throws Exception { public void testFiltersMultiplePCollectionIds() throws Exception { List pcollectionIds = ImmutableList.of("a", "c"); - DataSampler sampler = new DataSampler(10, 10); + DataSampler sampler = new DataSampler(10, 10, false); generateStringSamples(sampler); BeamFnApi.InstructionResponse samples = getSamplesForPCollections(sampler, pcollectionIds); @@ -275,4 +294,87 @@ public void testConcurrentNewSampler() throws Exception { sampleThread.join(); } } + + /** + * Tests that including the "enable_always_on_exception_sampling" can sample. + * + * @throws Exception + */ + @Test + public void testEnableAlwaysOnExceptionSampling() throws Exception { + ExperimentalOptions experimentalOptions = PipelineOptionsFactory.as(ExperimentalOptions.class); + experimentalOptions.setExperiments( + Collections.singletonList("enable_always_on_exception_sampling")); + DataSampler sampler = DataSampler.create(experimentalOptions); + assertNotNull(sampler); + + VarIntCoder coder = VarIntCoder.of(); + OutputSampler outputSampler = sampler.sampleOutput("pcollection-id", coder); + ElementSample elementSample = outputSampler.sample(globalWindowedValue(1)); + outputSampler.exception(elementSample, new RuntimeException(), "", ""); + + outputSampler.sample(globalWindowedValue(2)); + + BeamFnApi.InstructionResponse samples = getAllSamples(sampler); + List expectedSamples = + ImmutableList.of( + SampledElement.newBuilder() + .setElement(ByteString.copyFrom(encodeInt(1))) + .setException( + SampledElement.Exception.newBuilder() + .setError(new RuntimeException().toString())) + .build()); + assertHasSamples(samples, "pcollection-id", expectedSamples); + } + + /** + * Tests that "disable_always_on_exception_sampling" overrides the always on experiment. + * + * @throws Exception + */ + @Test + public void testDisableAlwaysOnExceptionSampling() throws Exception { + ExperimentalOptions experimentalOptions = PipelineOptionsFactory.as(ExperimentalOptions.class); + experimentalOptions.setExperiments( + ImmutableList.of( + "enable_always_on_exception_sampling", "disable_always_on_exception_sampling")); + DataSampler sampler = DataSampler.create(experimentalOptions); + assertNull(sampler); + } + + /** + * Tests that the "enable_data_sampling" experiment overrides + * "disable_always_on_exception_sampling". + * + * @throws Exception + */ + @Test + public void testDisableAlwaysOnExceptionSamplingWithEnableDataSampling() throws Exception { + ExperimentalOptions experimentalOptions = PipelineOptionsFactory.as(ExperimentalOptions.class); + experimentalOptions.setExperiments( + ImmutableList.of( + "enable_data_sampling", + "enable_always_on_exception_sampling", + "disable_always_on_exception_sampling")); + DataSampler sampler = DataSampler.create(experimentalOptions); + assertNotNull(sampler); + + VarIntCoder coder = VarIntCoder.of(); + OutputSampler outputSampler = sampler.sampleOutput("pcollection-id", coder); + ElementSample elementSample = outputSampler.sample(globalWindowedValue(1)); + outputSampler.exception(elementSample, new RuntimeException(), "", ""); + + outputSampler.sample(globalWindowedValue(2)); + + BeamFnApi.InstructionResponse samples = getAllSamples(sampler); + List expectedSamples = + ImmutableList.of( + SampledElement.newBuilder() + .setElement(ByteString.copyFrom(encodeInt(1))) + .setException( + SampledElement.Exception.newBuilder() + .setError(new RuntimeException().toString())) + .build()); + assertHasSamples(samples, "pcollection-id", expectedSamples); + } } diff --git a/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/debug/OutputSamplerTest.java b/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/debug/OutputSamplerTest.java index 5ca562e1c241..26285205bd34 100644 --- a/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/debug/OutputSamplerTest.java +++ b/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/debug/OutputSamplerTest.java @@ -90,7 +90,7 @@ public BeamFnApi.SampledElement encodeException( @Test public void testSamplesFirstN() throws IOException { VarIntCoder coder = VarIntCoder.of(); - OutputSampler outputSampler = new OutputSampler<>(coder, 10, 10); + OutputSampler outputSampler = new OutputSampler<>(coder, 10, 10, false); // Purposely go over maxSamples and sampleEveryN. This helps to increase confidence. for (int i = 0; i < 15; ++i) { @@ -112,7 +112,7 @@ public void testWindowedValueSample() throws IOException { WindowedValue.WindowedValueCoder coder = WindowedValue.FullWindowedValueCoder.of(VarIntCoder.of(), GlobalWindow.Coder.INSTANCE); - OutputSampler outputSampler = new OutputSampler<>(coder, 10, 10); + OutputSampler outputSampler = new OutputSampler<>(coder, 10, 10, false); outputSampler.sample(WindowedValue.valueInGlobalWindow(0)); // The expected list is only 0..9 inclusive. @@ -125,7 +125,7 @@ public void testWindowedValueSample() throws IOException { public void testNonWindowedValueSample() throws IOException { VarIntCoder coder = VarIntCoder.of(); - OutputSampler outputSampler = new OutputSampler<>(coder, 10, 10); + OutputSampler outputSampler = new OutputSampler<>(coder, 10, 10, false); outputSampler.sample(WindowedValue.valueInGlobalWindow(0)); // The expected list is only 0..9 inclusive. @@ -142,7 +142,7 @@ public void testNonWindowedValueSample() throws IOException { @Test public void testActsLikeCircularBuffer() throws IOException { VarIntCoder coder = VarIntCoder.of(); - OutputSampler outputSampler = new OutputSampler<>(coder, 5, 20); + OutputSampler outputSampler = new OutputSampler<>(coder, 5, 20, false); for (int i = 0; i < 100; ++i) { outputSampler.sample(WindowedValue.valueInGlobalWindow(i)); @@ -171,7 +171,7 @@ public void testActsLikeCircularBuffer() throws IOException { @Test public void testCanSampleExceptions() throws IOException { VarIntCoder coder = VarIntCoder.of(); - OutputSampler outputSampler = new OutputSampler<>(coder, 5, 20); + OutputSampler outputSampler = new OutputSampler<>(coder, 5, 20, false); WindowedValue windowedValue = WindowedValue.valueInGlobalWindow(1); ElementSample elementSample = outputSampler.sample(windowedValue); @@ -197,7 +197,7 @@ public void testCanSampleExceptions() throws IOException { @Test public void testNoDuplicateExceptions() throws IOException { VarIntCoder coder = VarIntCoder.of(); - OutputSampler outputSampler = new OutputSampler<>(coder, 5, 20); + OutputSampler outputSampler = new OutputSampler<>(coder, 5, 20, false); ElementSample elementSampleA = outputSampler.sample(WindowedValue.valueInGlobalWindow(1)); @@ -227,7 +227,7 @@ public void testNoDuplicateExceptions() throws IOException { @Test public void testExceptionOnlySampledIfNonNullProcessBundle() throws IOException { VarIntCoder coder = VarIntCoder.of(); - OutputSampler outputSampler = new OutputSampler<>(coder, 5, 20); + OutputSampler outputSampler = new OutputSampler<>(coder, 5, 20, false); WindowedValue windowedValue = WindowedValue.valueInGlobalWindow(1); ElementSample elementSample = outputSampler.sample(windowedValue); @@ -244,15 +244,14 @@ public void testExceptionOnlySampledIfNonNullProcessBundle() throws IOException } /** - * Tests that multiple samples don't push out exception samples. TODO: test that the exception - * metadata is set. + * Tests that multiple samples don't push out exception samples. * * @throws IOException when encoding fails (shouldn't happen). */ @Test public void testExceptionSamplesAreNotRemoved() throws IOException { VarIntCoder coder = VarIntCoder.of(); - OutputSampler outputSampler = new OutputSampler<>(coder, 5, 20); + OutputSampler outputSampler = new OutputSampler<>(coder, 5, 20, false); WindowedValue windowedValue = WindowedValue.valueInGlobalWindow(0); ElementSample elementSample = outputSampler.sample(windowedValue); @@ -281,6 +280,32 @@ public void testExceptionSamplesAreNotRemoved() throws IOException { assertThat(samples, containsInAnyOrder(expected.toArray())); } + /** + * Test that elements the onlySampleExceptions flag works. + * + * @throws IOException when encoding fails (shouldn't happen). + */ + @Test + public void testOnlySampleExceptions() throws IOException { + VarIntCoder coder = VarIntCoder.of(); + OutputSampler outputSampler = new OutputSampler<>(coder, 5, 20, true); + + WindowedValue windowedValue = WindowedValue.valueInGlobalWindow(1); + outputSampler.sample(WindowedValue.valueInGlobalWindow(2)); + ElementSample elementSample = outputSampler.sample(windowedValue); + + Exception exception = new RuntimeException("Test exception"); + String ptransformId = "ptransform"; + String processBundleId = "processBundle"; + outputSampler.exception(elementSample, exception, ptransformId, processBundleId); + + List expected = new ArrayList<>(); + expected.add(encodeException(1, exception.toString(), ptransformId, processBundleId)); + + List samples = outputSampler.samples(); + assertThat(samples, containsInAnyOrder(expected.toArray())); + } + /** * Test that sampling a PCollection while retrieving samples from multiple threads is ok. * @@ -289,7 +314,7 @@ public void testExceptionSamplesAreNotRemoved() throws IOException { @Test public void testConcurrentSamples() throws IOException, InterruptedException { VarIntCoder coder = VarIntCoder.of(); - OutputSampler outputSampler = new OutputSampler<>(coder, 10, 2); + OutputSampler outputSampler = new OutputSampler<>(coder, 10, 2, false); CountDownLatch startSignal = new CountDownLatch(1); CountDownLatch doneSignal = new CountDownLatch(2); From 19fec19bf939fb27bca22b6aa32895cc97387f5d Mon Sep 17 00:00:00 2001 From: Jack McCluskey <34928439+jrmccluskey@users.noreply.github.com> Date: Fri, 15 Sep 2023 09:26:35 -0400 Subject: [PATCH 009/105] Update website to have Beam College banners (#28459) --- website/www/site/layouts/partials/header.html | 6 +++--- .../images/college_2023_banner_desktop.png | Bin 0 -> 159958 bytes .../images/college_2023_banner_mobile.png | Bin 0 -> 594273 bytes 3 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 website/www/site/static/images/college_2023_banner_desktop.png create mode 100644 website/www/site/static/images/college_2023_banner_mobile.png diff --git a/website/www/site/layouts/partials/header.html b/website/www/site/layouts/partials/header.html index 3fa8bdc9455c..957e3de2b1ed 100644 --- a/website/www/site/layouts/partials/header.html +++ b/website/www/site/layouts/partials/header.html @@ -208,9 +208,9 @@

diff --git a/website/www/site/static/images/college_2023_banner_desktop.png b/website/www/site/static/images/college_2023_banner_desktop.png new file mode 100644 index 0000000000000000000000000000000000000000..09e0cdfe24b9748834d8eb0afe0f6c6b3bce6fb4 GIT binary patch literal 159958 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�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
0;%JUUx1_NLI;oH~p=YP?-{AF`l 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ΞeE*VhjyyT?vx_;z`r&cFj5)btn3~T< zluh<4Lu0a?(ge8CgrTvosb`Wct<3A589#$idYcUZDOK&LAIowov{w9eBERj(fA(-d zPPTy_%fB4D-)d;SxOyb{(xCggKr9y=;T3ZoFyd({Q!YNJfPz?@L(l?N**&7r zCu8k_wgH+K+y5bNCGz`Apv$8$rzv)b{32v7Jg%^^E)k1d`Mn*jAtdOP>&@Ai!**O zFEJ!q9p9^yh?8tn}KJp^}3=PuH1Y z3!#eic?6pG<5Eu5(+zkolf(y6W|%7uz8NO`2%>naU3)?N`|x9JUU8qe zsjRHQe*GYR`?$~2HY-nc;S%-DF&vQOVKFFUH420VK1n<13V%;eqplRcqkNB%pnv&=5ePaOUaW** zoDKZC+blxo@dfr}vq#UjZl`NdFpt-cW%WvTD;i34!9`d^_dY$yw?d4m1t_be|H_(z zcn!*FpI|kD#Y?*MJF24ulI7daIk?%Efo3Ky2OU+8gk{N7u{I~rM;Pjk1FWKJ>>?jK z1+a@imGEc&at(ic^~y642LvDKZ%@h068XjO{AV))NMkJCP+P2 zwo-Ol;*(?_VY3dqDa|m5`>?}Az)Ia{9Jl zf#{?o4FUUs>y(Vverz;E=GA**aLBf=HqRe=IQt=U@$te_DRA913+|4G1G3$u+@|tD z(Rm~RKhTe#?cC!yPH})_?10PP8_P(Zr&4sL0jMat_cO|^;Il6f?TJC)xwlJmRcgP3 zcYTQ)6IU|EbJ~LUAo}=iwr>*1;wCd-5udI z^#G#{MRm!bm#MajI=h;YJ&o*0$lP+71U&5kvtBApT@MCgi?#r~) zp~vas<`NInA_jT*RO=|L?oSTK%Ui*Hn%dRHY|{y<`w=JS*WAGD$e`u?X^4_$9@-aeuSx*`Vs?#}No zOo#54U~!Tn!+FL$IDb1NZnwjY9-iKV=}MZ9P3O{9EBoKfD!aQ3@YYT~;-fcSD-I9f z8p@I``-#Y72OsfuTuA&2tq#3zHJk;sMX&H;Z)YV0UlEd2+}Ei1aUc(4w7<&DD`fX^ z*}+MD+=b!|TJoUgZI`9PG3qDJzG*bLBF+=NA^KC4TfF?;_~BN+S5e_8KI8lcM^Ef= zbyNTcga=!g7ZEXPM_#|5*O~H`Jm{onN~7#O+VO~!kvPwBKTU@J{sI%(I@uPMU(z4q zd-R!>v1?e(q0L!3F8-dWh8sjcVwH{7 zhV^e(OUx-=ink45obgUN(}}AwHbP9$Eb=h^OQOqtXNs{A`cvkRm?2aX@`Qv2o)$$e zL$L$#PnO**&{_bs6Zve6vBw8k72ptCO=5%WY=F>G{)PXeG5#Vgs0jxSaxXeBMjKT`RTE+EoQAczW^yS8}yGRIhL}tG} z4*<{1+o&&ji;J4xs8W05r;ju_z%w7y(!mwM(-O&MVcOZf`sF*E%K}065*Sq3h2hk5 z&5upHqwkF-IV#HQd%q!L>&fS9f&-_h0W-{y&TPssM%__RmV%&xs++w5QEiGHAPshv zoIM;z0g=BDP2zYXa2Rj4+n@txv&~*6^y1>N_(8Sy@g9lJb4!ei{1%)IPRB(4nyW-i zBm0Sg)^2VYI?96KUm~{!jVkGV{4Tsnug8`T2v~9%>%B{uB`4>|cR}2Piqsr>2_7f4 z)0;R%?AHnp)y!tzcP(w_T?RF)cobjE%Mo~f!Jy8d7xs3PXY)W zz67eT{lo!);99dp-u$fp(KK6TMX5_^d#&kO!xbBFxPNNvs~TQNSA7$1m!O7;f>xVKB4g>v20Tlukd=CqJ&d! z48Y!BizJ%RD*W10NL=N8Ka!9bsMEqk5y3G7-% z+s$43^!^q$k<~0eM$;jqAJ<*gOdq4U%E>vwMRk8UJit9lij6i;ky+_JR_p%5m+Is8 zS+DbD8<}v;CrCio0d!x=%Ki_9aDOHASytZuxetqDhZrWhJ{`3OGThq@LbGpmp@*;2 z-nXy!q2x{0eMi>4zg$sBDB0k@2rF|-inys+eg^88ZocW{5drr2ySIztHKT{5$Ojst zFiS(*f7x<#F4q%5^eQ2-Niw0Foh_xQmlLAc)mxm^^~$*9d`oJhff)X59aZ@=)r8$| zhfTX{`qx;}f&ZnvM;df%O%0?9Tqa9yzNf+CbfXORFYbO*h6Zv>&o$BAKy+rFUp(AkZ*rO5lvJ+qa(?k2-! zBnLL!5uXuyZk0yWO;fVxtIvBFD7)e^ASj``e@3KQ7Tcaj;%PiIt`8qqU;Eg&rlXui zDJ=3IY3quxvp9<4V;cVEWg_j6j6W7XQ1OIU?83Vu)7~=e^kwGjXp=F=y#AxBNT0DH zF&*TBp6J~hO8&yHaT~4tbHSD{<(GAWuV(C^9`*2}Ci=Nw^UqANvSXxAX6?E87)cI(RZdUB}TV;+j?UCrd4 z0x+`Lv*ecN-l#m7!&WV+=-gHeF1~m6di}=FM(mSC=T(G~L_&qcdkF3QDY0vvYL*w# zKz@#3puqGq;iIAt&@2~-bv+!xz|r#+6Ymv^Axo5W9MGwIKUU_2P2Qn+!Q_YSIQi}x zdA?YFWw^_Jk<12@=zIt)KPh<`6FXFhpDBXN^u%j$^W~{BWF1tYEh~As4a5yz6{AK2 zm;0vX`_T)97Jn4OV?>CA;)PP@?kaLdjfthPCT~9F!LIB;<0qQ|cO*Om)<;28{h64xZKqGt`^?J=^diN56; zLz+4sPEOM^{zumXh~*5ZS%Ffb{bMjQPI1k@>itjgmd)0d1RCs%P7^_v`wPM5u(-Ig z&oE4R(S5lEWBVn4oHF5kdCRWbzWwqQFupABVf;N!Zbx*ql`3$?|Ex#Xwc-)>vHW*nzfrF> z9=q{7I5O(4A4o?TNT{Qr)%jil)n&Cpb-j4STdmFExQZHIu5bwRU-StJcG}#gU)qS` zmaiynNui^WwEBI~*dW~tqT136@XTlT^A)nt4`>@uD9X`+ zWkwk-5R}>c?jRBke@WQj0G{~Z{Uu)$S#T7-M@~q7mn?fCy%{Lm;`>&FGC0aA`AJ+Y zszr`XG2Ls2VpSdF1x3D5>r~qQK$4&nX@KZoa3pTqn%>45@zRxO|_8j;9)aT zk&6QDx#V46BtzYhdXe1E)seDC=&_%n{ri%mN!4MrIPR?{BO#eVdk3=Y;)QY`%Dsi5 zWa(T(3Jz3eD4(v-|0{b;^H7}G%PW$VPxH*V0mJvLpJcY5lRwqr?|WS>DmUHr6*Y|L zkUuI%wmYo%aYex2Tyfgc&I7tbC`?iwtcK3G<(GHtI+Gu{3OxSkl{x$IzJV&;jj~ez zH23Z5<(e@96up@iS<-SqCmo_?t_EGW{k#IF?<&^QXIDRPrM~uzr!@LNT0HXQJm)yc zGftrkrUi+pp*4DWQZR^x7NrEo9c&cc=y^42wsjaX+{W%;Ds2q5k~_I|(ueFJAVG}S zf_I*4L+r7g4&HLmVeVP-_~L`IlQeR&A^Vwipgu`7os}G9o3rq%@ci8_&y1#imqNB6 zB5mcI$o{vU!ySXV-U|zPgT3cVt&sG%O}5l;8|oCKens;&6ZMVjqr8?yRY29>b~iTD zlb;B!cu-po1A`{JSj(+|_eAz_$$)-gGZFVS;7?ro3s=57aznNcY(o)u=S2d0<_L;+ zz>o^Jcz zggIHM{GicYVRde1s9)_^lb!NUA3PCNzOD*Sc@kE%Dkv9knEpk{elythE+=g6$saHR&P^E2}S?*O*Jf)&fn2d}WU zXsRe9q{SR7F2+~TE{_#R^o(-<<}s5&X28QW8hDMG9nkY=-LPvxlJ49)KC^XV_05XbxnF&a7RGREf<>kUCPd(r0c?NI)Ytr5nIcP5Vo*2snxSHKwDSCTum8p-dmr};|0r` zkIA$mjdtMy_xCvcf~Fo%LIyCA>UvIA({QD-Au{8GLZLiXBDo5~2)QoF=q5bW5F^}J?c86$1wyy5q6|$G)#|co3n_xgFNcWtv^X&B{5u^-s` zpRR*&o7l-y&fP9YAqLkAcK*fJ?3(bmzkg}GnXEe7YUwz0rlhx)>N~mqkrON;!-xtz ze=uxb>x#?*8ia?t&~V?A2^8|~)kqAYv53b?ja0PBq($>ua2}_GyVEEE<^Gnl=3eHP zxF!p}ka((wXXgVRHOS`!4QhY&>4*Do{PvX-G~0GT{frNI4L1I6I zl&$y?aU{MZ%6A+Kk7#`i=J}KY*Xenk_lWO>)XOSz$EIa)nsO4zD{PCVvk>p6mQPbE zGS%Zz#R7y>@E(dhJJgQ&;b#VPRXdigM^Y2QVo*5I_P(0n27kj0p`|W=BA>2ASzudPE zGuolV(2Ddy4kJf%W>WAl9UVA38$9n3 zJ9qvdeYWvy%?FZ-kf7$5BeVS7*%O8te$*w!B3fsk*tEjnQJbAJzEUk)(PZKA!lVZ% zIT><7Tkddm8?_heUuetncN2>>_pIK$)H!9fS0>e7+c$s+k3$&y#-{Ogg{i8X$@mmX z#6efn17}4BYQgEiJsYV)U<5uQ!u_fUp)=t`j4PSI;~Y;2K1gG6Y2^0}K5h zwXs!Q)@;_{;CLI&u0B6!5tTnZdLNqY9z4~EqUksgCTjSyx4GKmFZ*6WhbB@3aoZy4s<8*Elaum|Q}6 zf~Uf9m7TA}X#OJRZ{oMnkmJvQL5=P z6H(mOV+h9)K+)0I2Hx2R=x3X(ZCz&!nN@^1qti({XGrkXi~{_?1)o+?f{3EExtUCj zD9XZK;dj8T{%n#VUB;a`Hx;`u9lH?6%Ddrgq1s)~T|kdKP8lkx+dYg}O+{f)AZqV# z_(R9fpgAT-o-`JreH6X9qC{RZ2Nn*q5WSzeE4ahrQ=2NdYXPLV0-?u=r?u>-d2(e1 z0){mIdKr7(LZi`CNJ@1`mR!=XnmuHOil4Wipw_`_=*+21nc8i+9-X?Cech$SL zFegp5AvLob{3Ho-qt~!SgPH+GHdPH}3RO%uuQ%eidJ?|(1=dYtC>>(hb)o=e0mSVv zZCI?pn`J-z=J#3RBS>(f&UgW|!fHjLK!=|w()FLn@nB^7KShs5!-y7cj?$7Ke@1o? zXW;gwcKg6yhjuwz$w^DQ;n4A~VHBYmo0a;J$qRoorxI(7sICB!1T}F0LliAsLJ04$ zWFFQlxrcR0iu^hH(&mBqefNz=e+OczovAetC6U{SBza4Y4z=Z=t>wi^iy8UMztfH{ zDj&umiyOSW@X8tty%>@hxzwNA7;<-cICfA0D%;2)({98%Na&8DFWbI)g0ClnQ*@&X z%o#b!Ue5WdO9+Ym*f*&Ik}jw7Ltl&72) zE)Q?^qQ#0=nCjyAW)h|@_hUZ=mnIt!&SFNoF1b4Ow&df^4yg5fM^fl|7mP9rb-Zsnh|>1O9{Jfo z)Ei3XefG!C4<#g5K6BjIDvKBTZIf`{%4~sA;&Fe{55v<(e>2;Y2V`e92tU@O@o7CK z;}?&=!!`97hLk}DKRhVR-&pl8k3T3ABP=jfq!KUl4Cgu(1F=+OH~s@=Ch)H(QzBRc{iQ$ZqXDd_ z7R9d04lz;}u^AeK9vrdP+gX_4y|Cg@+n))es@wa?1A0m$3Pa^4JHJL>swg9v9~rzp zjGBo!#7KnD7E98PmQ*6CtaMaY_#~q zqvwv@bZ<*M!P%IGwd6xS^Ump}=bi(dnG(dMiZ*>Y{?-?9eCo~Y0iGZ;OQO%rSlsfn ze@(ylDBHVE<6t|3JpX$1;}c8FJ5R@vw>dC=E5P`r9?mINd8j) zIvN?Xzgxai9l`s3TJmrZ#SF?V?O0@vJuK}L6WwMY3DEPtp5Co59N32QvF!`@WJFK& z(!Aj_G3(=5B`(%w^JgJ{=VH%@;(uGc)FO5kU>dq(6_9YmGT|Z!#=b234BrunInJjC zH#Kg+z{WWyY&XnZF8k|F9lMGNd7wT6qwL0dbect{2j>@)AVSEA-dPjBRh|j! zzPpt1x(gwa;Cp_;eIvBtpwpg*3Om+;xaYQ{(^QB#-6a&T>uJ|^kRsKU|9Z3Gr>vc& zroA)AQqtDc&Fu>N9LK9!vw!N8rfLsD%&o^a+#`M*Pz>QWCG|3*V*B0i1r{xKMAZq~ zVT%$qJE(=eG}#MW`N>BVBB_;!w%bBeM0sy2#zpBD>eSrC%(K%Yy8d`dIs^Y&kqN;< z$&2P_VV-4QU_;4Ab`ukwVzL%N)x~!~_?e>NVnZEm_haX3rH9lEW3U&-99a0-Y4m)m>(acG~ zt!8F1*lITY;Up@YNT18tyHhZbKfP(7jBF!ehaQ0ehgA%s<;;ryVfhueSQV`VTcm|F zS3M3j!BJE5?h?2T*&+-rFQx@RC>fyq9CAa(3d+XE@e{CsxvO7_3(70gVn66(RBR7LU?A3GN8F zI?fzH*Q){i2|OCYD=nXvlOCL8H)y==^<%{mDnitVk1$J;KGCy%S*3ca^~Z^WSDv*O zG>R76t+We>S=u<1Yd~ZzPa>`}WU1JHKHcD@;VLWofxnEfGo@#|?u)R=O^u@$(2YeD z!tRlG=Jj?{8Q}hkIy3tIgYP%N91`7MNE5KfqVY2h5j^)9dDHJMoXn=Cr!{oh}CNx`kI}Pn!I$3Gv6?}>ICg)cWfQx$Um)9wGuej z_Tkrh)KM826z3b%|0nKP0}yY2RV9o_y=(cM72SrK}y@9bko)(Zy``BdNQcF4y*Z z%-F!XH;meK93ROq0~}c^F`;UN@Wl_{uvlnH&$=!4v5;Bm-yRq>-TJ7l*OEb%ox_d9 z#^zfL(j5=TPHWuZ+~&BlLT{5iP_gB0jl@qAQO0Vv z!iKx)ow+Lh;GFNZE|W4{@z~rOBtiNTK7>RhWzg8s(u_(Y^`xsW(fjeXl$8CX?6Hy= z>vWji0MUl3|H{ug_I5)<{zZ|bZ5}4KtZYTDJDOe!d?#SatfYTy zMch6oD%;#9i@H<%YXxlZ=!D*s1dSp!ui-mH9%0&_7N_$L4Wdixj^J>!S$c0g;C?I~ z6Frvpke$^d$&4i8NTqKAvHu(@qF+ z--nz@uf2fsYQRA2e8z@kY+t&sQ>n!J1^Qwub(%?=f6BPt)6sX!WEx`rslcGeh$xX+Ei9u z%YvC?KjF-MJi|1;{R1s(@n=otBEHsT8UaA3{N#PZJ_iWp!wLlmD!vE>v%RUj*Jc?X?}i%O^s_?fzR%W@Th zK?zjB;|2s+EgU)MmqLqi0? zVJD?8C1F;x({9gG;{D>ynYnrSF8XPle94(Ix?`NC-Ju$d==0ZbC=Qbpoz%AL7Ad=( zU-|s}(7Y0Cza9>;6~A@EIPp<{`aG;|)QKcN+!6UB+FRgE=9NwLjra({IYc*VpaeF1`@5GTws9um`L4T=bAL)`GvaA_yaBZ6{CAdZ&mxh}>Or;*syrS@K1L^mTeN zQ0-Ms2{#-5%jCj(fWe7=DhgT-pKC1@!_gC!i{{KZHZ&V70; z8Z&IwR`E$&;V!J}k*gr7GXxaljXY8T)i@Iw z7((i}xQOO`X~wfRDWCgG1BQ4Y4nn!9Lc(;^67Y!b8~7yT49@qgLxPCwOiRyB%s4BI z(Gx!T0mll35&W+6^~CCVlb&baj?5A}UR$(|8UCGQO0MS#Jxsv_L?xxrI`7HGNQKR| zxPgcYPsz%{gSJ7J4j)`@xov%7NCA3S*DIvzsQ$%tW4R^O5jk)h36D4U1mCNbSg8fp zgUN98Ka0MI(_L>wZL`ST;iMxMPuPzJOW%0QTRu%w5gB5yABAc8q61$=9gj#8*6uil z@&{_D_T3Y__x0`fi}f$qYG5g;B}{ib&7WS#s?)xBG}>j)O~vUn;tuas*QY^r7D7eHG@<+Dyk2BD}71zLG_@&}A)W;f?JwM4?*ke#t)Y>jH%&qp7&opnyNA zE5V95N)BVuHp<3tQ35q*ABvf>cf_9qZ!6)h+la*P?i8t?oQqy2Yow|y@?)QYQfLB! zF5zFoxfp{pKBEn*d8+-zLz^Zs_A!xWWLn(QJKi0;PIijAF#wLa`@wOTAkI>CEE=1d z|6jrnK&iOBbt0E4{0bnb)5#07t-pZ!4ei3pE)w&weW32MYKUm)8hU1b+t8$@f^#)6 zMfEm_;_xak(3(M>N?q>{!?nuaqa%#9=NUjYYvODF{3o`C_%ej+diXOedt0wI@#}B^ zkN6Ylb|>=7sHSi&$x86rrCM(2wQv_~Vb!em)8zs`RqIQ<7}l5@jO8qKtw$+Mv$|LKGWmXpSrb3qM&}#^Les6uFVx4)^+2@^m1st+B9%40raJLF5r+{_ z7U*|JdY*-jctn(Ag4LA9w|m8hAA=S1_3iY*BL_`EVas!hnG|Z3WNMn3`fBw22n%9W z#8Qk6UGADhlvQdr(IrkNb?BcQq_rD^&g8GcHCKWuzg!g-9Vq6%8dM=ChsiCBWG2V^jXPb8BKKO@>1LFerlO}kv3(EH8 zG!F*6N`naWcEJKZ%(sr`&Z*2cF)goNyOLP*!YYFjNOj`hab#8*VhB)W!R)GAhaWn7 zMkjF6TrtaEx|T>d=g`kRvHRj4;&2>Kd&bpFa;4irRQ@K1caoiBl$+V9;V#MoL!ZLhtk>bod{piwG<342#j`wNYs2 zF&t|2Nj$|{7he1e`Zg7SBXtMXKMKEQhOKVHtDuv0Ip(JV^LIzilT0u;=;X`ETW zR(C9G6We7ebloCyjkV+Fnqa5d${Kc)4h9=W`JI1HlB>^$Oh#iXiCs3eVPO7prkaH70Bn;><{V)|@iY${Y`p zmmkQLoVpSGD3^+V1^$KZ&wocLx5fW=%R_Ms+{AyknL~|Y>O=^rZVy9q;O1ZQkD?nP z0>eg|)nN7b=|&1kvzdDD$3IFVtocx0!OQD*P7-q$?<9beFmBss&tUb+#PjdjP%Gdr z@=_&p&~KtA=OdQymNVq(Sp3E zTxB>2Xwrw-`UY6auF&6IdE9H~JPvdh0ycx`Smf;Lxs=t3 z(BjyKFDGP8PuBIVQ1RW*MD8T7Zq187#lyMp0|-7DKV#v3e{~z%pa@=weRX3?az~y} z6N|cS!+SJlNs+rr!H^XH0yKed)~iJXhCl;dd{omN+{FIw;c?dEl=HaPtp3_av5Ng8 z1s)@kD}Z^vbM*#NsEcImk-}OiznSAdhx*DH+$Y*u(bF56yG*&1EmX)XogaG87 zhb#_e+AbA} zh@TV`fkJm534^zn7b@s!j%~vu3X$)W30{oFY2o_FUi;-n#**}hZdb;EwG$EU=T@}Z>%uJD?Z8K8!pM7}?3y+R znal5x&0qObW8#!7^f-Z+k+s>m!h+d9uoXlg_A+mGMwgP10)~y<<#UwL-QX^Jn(f4Y zCpMH(-c{(V-Om^@Z%2`sk%lHLzusfMCmFR8^5nXYQ+#v*12jbTj4CbAHk3 z$vYfi;HHpB?U_8V>qXsO4=6(h655XZ!wBIl1=Pd1+`3+Wn^tjb#Ce}?JEuw+<_89S z)?wt{|HFSIrEl9Hs*mM=cudCZ$9SE+6<>|q3xyiZmsP`Y z+mh@V%uyCoUVfo{d0(NX_ss#FKRRP?h!a}Rs;G#$+gC1xEde1D)MYRJCkrs9`P$-S{;wf z*aJ??x@}gQhi;1EC;fLcZ`f4>gDT><&6jWmHaBOsa}sW>n2ER6xAlu&cL|OL(fR3_ z+sjVE+fL)<<9wLTCGi7j#jOjGpH+vY6E6-}owtFMsK3M4q_+c& zP@e&Y4iX434P4LwL+ubL$xhCb`stW8!UwSa2t<)b-H{Mn)^a<(-Gt}+$5Uok$x^$*^xjB)93ZP?t3FOtm1b*ONQed?Pr|s058c%ahperaKSLeq* zHT92+vEd5|<({+(%$5Abb3taD^rMwFTZn)Uwcp$lKeJ~T?M~GD-a$glNP{WwBF)_> z4PxNa39vLcQ^@Z7m~IV8u%2(&4VHCPw$=OJE^$%=b^7Hrv(3;=SaJ zg41_@giW{~JbsEyz5D^#b=Gf(1q|f=a0^;86i;yTYyLQy|Jb+S9;^*}EtEXavOHR2 zbxRjnt^cROJ6NWWNB^sUjiG=l%?^0qs2)eE(#4JMe#NSMdo|jZaG+!{!fv_!OqKLF;3-f z@&op&GL?mk)I<=eG|(+qNa=mWNF`0)q-Zz3>IlrDTfHjs0Kjr}R15987u0B~-f4&; zDT3uTYRS6qs;lv&Rq$WUzjQ=&{DIk`RCUw0iyH#Wz*7R+Jl3gKqCHRU>Z?nU-o*ge ziNy7b7Itzz;D0Tfl>46>Xi0yw)Gj)L=S;aC2_010yk=?7My~(z4`IM!W!RijYF3fw zb#L*%>J@CA6B}9cQu{N6t8h%r;%A^!z>8->Ebv*QGqVZUzAt}Ud^c0wxfpm`W}D}k zewdyjBS^8OxtjcW8J`>0^Whmmrcn|o#v+%RI>+;XJ$lgPhCph@`%K*R))g%TYoY4m+u&1-fzx;IxVvW$snwPR)SNx!I z-=@MVwfj2ueF7}Ujq(CW)-BD01mgAK)i`9tGPaQ7bX(MQ(!##^@oGbom=F89+%;}Q z0M=?k{&a~d@|B6S10v3Gb?m>HyivX2wC?4x5QS{sGMz>MQ*q=;HMw0~8Qq6l+cWN6 z-9P{|b2I5iv8?}B>B?+pP;*o*)zoZ952GsOAQzR{(+>A+6ElF5E0eMMBeAe1*qH~;UAPS zvIx&EVojWUqeM7FElk6tTnxj5Rgkb)^kq&}QKG)*W_*`KEXocpF_qdBaM^Szr$bZR zq`LmM9H?^udZ1nf#uB@5>!YrBZP8@EE8j zQ&{ay_BQ5R?;$Ftf#lumkU4Z8+Te~aBhbV64YISv5&roiHKY=vMzog-3On9n6PO>v zwd-uqsI5PTW^c}O8Gof_k7Q#?zu0@s(uWV%b_&-OLx29}eJ@Lh;3JUCJ@rL_Jpj3j z$;}NJ9x7;lEHK#P*v2V~yTDO5p@+9wy4(yW3^O7Q#Gq90V=G&&)g(_W=E3~^Mic?h^v*QhLM@rH~5IjO(B~F?I z$kRIy=ee++$2u9`|FTnQapi4(U_k@`&i?$@P)C3u2Y<&Q%Pj&oS4(bQllXSQvrA;P zf{sexho>^wE+;HdVh})%bCsv;O{+-y(BA)WAFOIoypG!WXdYKI$03josBumCWUoig zRKM%|%xV?@B5;0^oo=dbJkzWLx)zCzZWw?WP=)|n{BjFXf!a#E{v47ULtT#nDsa+s z6c3aO(b+(C<#-lZx3iTRqX>YitT=dGJKFl*W{I}VE}HNS$YgT{14kAVx*p9iUSs78 zrP0V{#MclU@9{4-nGmj~+F<<@iBf(wx#1`XWw{xxOsdpoA5&>*-P^FJWug~$9Cti7 zP5&tSK**3t{d8d5D$EeoT=1CKEwj)ZXlkztbO`Y$Azb5TBp@#RMX9westb{Jdxqm` z)@g`Sq_c?r=5N10x-LbB*)|_Zq-^0rGK|I?)V{g$ygrJ7r9G#ZlxA+ro;e->%%{y7 zic;WWGz2TPZd;w_Z6nbT3n&$jlj07IuHLs^I&hXj4$thZ=f#*rC#WE^8c`IF1Nucj zrOtQs)DdnhAmvZ-H<>K`&v2xy2(uTvLlH<2MQm9;x!i=2y!^uftp4qS!X){5scNFM z8*)lY64f8e?F?#er&=-ffz-ygN$|_B>h;H_ zj#~j}g2^#&$L`0hNC36?)^0AE^tm24nM54Qb((|T7k~glE`v5RYx9;`V^{u2RI6htj%ev;vE$67_S{0&7WeR*WEPw zfe1eYhmOx&DcPWHSk#(P_I4x^{JQ3w<$sNadBtAyilN-$Lm_bc#2Gcfz{3r8f>SdY z{507nCEPNyU(CR^v$eG~Uvh{g<;Rv6RaW*2{-JTevU~_OP(l*4znbl<9xFI3USfCE{TiIw(W$rOF%!zHN ziNBbzieF)Qe}}6FpoCf=iyM2vP?Af?lte}0hFZiZMHdaq*9x8F#kS3lOPOW1V3cmPn^G zpl{|)37>^F9Lj(B!gOWngkaP=DmhCNds3Ca=odJWMD=QtlZVGIxIp)ACZCoqHvVx1 z4f*42fA5u4C%W+>n{^hw7$0c+V<&**zbH4Xxxb=nf4U0qc%wwnAw=P^){eOD zg^Vlm%irr0e>2KB0pTxMA(pr2HtXuKV{qS?qr z1uSXU9H#;XqT&^UdsxdK<5s_m+u%Jy+o$R`8!625_>*;U>*ND>D#bxbMsin5j@8j~!+!P6Acj!ttEM+b`B?@%JiOXkwTa-1M-=LeJ3J$}XhP8OBpi zYE0W|?^pnWB>d)1dH46@rAiL^1}&edgDQl|eQgNS_n0a}^u{G+cl)4Fkdkl9RSb}k zT&q^3Ui%h&-HFq^`o0Z2uu#E|DF2yf-UrJ%U}^c=sc!g@7I6lSguhg8E8I~_n5)nAq1C_9_S^As z8n`ON|9k_17)B5cNB9_EK!CH=Z|plLoA_HM{|P3wqP~YEnA}ltITCQ{k`4Ax1=_p+ zJmR80t=BxS#A8Syem3%kzFJ%}-Z)M8cCvJ^tzAW=68LJIAvy{X2c%XHzZG?gzstF0 zHR7m(w4Fn=YN^l9K%=%OJ@Aj_C+l`Vl~V3+ayS0mw7MBbvS_M~eDC#cs;M|%4{6Xp zK|E9Fy601JZjT*F$DtbrNUfi%XTNpABYC0>`}ucfGdo42|4Uc~)g&01EPrR%%e9^+ z-yoLmmA|bdI*1|!S8?d}!HA!wwHi`wMzaVc^0?F1{|Y$89Dhn-^MN^(KPfo>hx+c` zbl*Sg^7TEI?EmTpp#TlzPS1Y9IkEl*yopDeZ(z4*8|LECsXc*1m_ab|cn(90nK5`k zG@jyrh8BS9GlnZNeo!*SYIqa5Z-$8T#!~(uimIUh%!A`PAp>+kkK<>jGp5;2hb6-ayB*3#4u@}o&9-Dg#%6S5u8Y8g#JLS^e63yTd$x08MgkZOB( zWNd=jptwVY_R}&mNgFs6=n;`|C86M$*me$>=wUx7BkE?>+$`nbUVg0)BVz(85jj$9 zsN%A!n}b?M<10+hmhL`GK8grJcaOQkaO80T#$HTV0j-J4aO9RL91S(HOkU`)E2cNP zSaRgJwX8L5FwB%76N#2G>bUdS7AGcC2YGRj%hv97NYKtQ-mS32=VOFguuPyMCi$91 z&jOnv2h#R15@>2wNq$@XaCHOblV)yUo~o4oJN=8!dLG_4ldu~&N|j8MokOFf{az%- zQ%z(bD`-)G`YX)akTdK08(It#UUAPQ#J5mA=uQI{Xp=th3k&QABxgux;Evs2zl^hn zO0hHtd=@)ues^+~6i)P8KpS(zPE}+Lpze+%n&Gc8!v|Kgsf&sv(x^px7wS#_meCmd z>*UAFiA;<-;7fmrEmt~1fInb2hV_3(Ct>>6t@cX5ZqZbl9^F5Ou@cC%0F_|g9oFSc zGRv}Ig73OTOZ4_gA$3imw!3AzI&c|$&4Yds>3xkT6EQJ!ovFClS4tV3{^D;NDNqu@ z6bJ&MVkkw1srTZuud^P|g+7eCNOM^=sXhmINiqR$blUftV@A?U#RYNB*Hut%8tzto zN@3zp-Y#O{BSu-A?BbGHs?6j3@tw|qDEgv8sg&i76gDb34mplU6Gg@l6!nP`y`roP z@Ddjon6kLY_a)k97h7R5b0(iGruY|X!4N#MPG0(gsl)=Hlc6K&v}S-ep5mF8weS(E z?Fs(}rSp5vm=InnQ^S$14()~?(%_YJXU4`i0<6qV(CqN%eV)9C^B8qLjH%HMJ0u{5jytXo!fsbUj{=Y-=J=`VKU%m6nis{t z51$_9HcESBbiU~k9|ycUWGod~-xv{`D(L)~sNnqwd@yVQFQ~)cjH)vLt{!kA6{O`+ z6(otMdg=c`gS6c`WAhrMK3FtWNgn<_$s8YGz8~UK?Tw`QuXF%(trbh|vI{X9#>>2s z@eIagKVD8ayQO&8R7@K?IWp)7?Pn&Or57Eju87!5lU*bh8T9{3$Hn`9rGv|mx3?E3 z`{<(iBLOt)Ia7{+G8A%p&Vk&sg4fq~2iqo&o#-(yFRxp6E_z8V-sz(3s7iRVU(}rO zL}aX$(V137Oy&i$yh&UiFqQP*w>t9iOAF54{^G${7R!l!p-khF1lLmV7_w=E2`88^ znSkB(7}Qgw>*)#xunV$wMcNZQEI;GM!|FOk>5>qrGhxNu;fynJa`HQvDGmQVb92_9 z!ZIK(%*1V|Wu-Fljt#>@q9Atfsm*cBz%NwLM2R#fB2}}&6hJ3MK>P*GMLk8ejiY{^ z0me(>tej6wrVBvDU?If5rlLf`d<_0Oh0$5_L`V}X!jx{9z_&?DWT5D+YFm-+ z`V#KrI)gzHCO?I1(&!e9{a|H)E{6F#W17p9bREuo>Z#f>3eNw4czBL296Zug@f*$d z7B@I&aUa+J|MC z9JZrJ1ym^TWa?4_DWZS4XE_$8=@&+i?nZkbBi@pooHC5hs1!TNYi}+@6qI;xV~(49 zc<+tHEEr&5JHt=4tW&0Hw;d>>muv)7EZ!)0UZGMOzd+S@irA*RWgRS&D=;U_e=|_6 z`C3DzWKO?~eWULp_F_*h?B>4TsPk8a-EFB!n;-`4 zy+A$un2Z2?Z}}G%3mbYgAYk18O8dVsaF+pgzOZ*!@iaKm>93;6pT_YfAdd68W1`n* z$Y0TR6V>==EPeR2x_qq-eQE428$c~V?k{(pR@Ua1p=C3dYDQhVN$f(ss{J&+zW^Eu z+(>eL!Hg^RP%dKFE-(dRIrLvCJ-23=&GhSDg^M?F?>n|yetY6sXK76JhBvZ|rWlzt zbyo5#I`TsH=rSsO0-q7G)D5N_u`mJSM*q+2mF!g8p~^vt4!b3O>Q#JhT>m!$LrA%O zl^4o(O#nD*l=YSd0n?%ZueWql25a8^!r$8Ejd#2Wu)hl_Rt~b+$rL+YnWZv}P%fns zo4V>Wdl334D`eQcv>9KZD zC+4{tLDbKBs|UCTkUy9?I04Cm68o#hIFy^81M^PA728irS<#84lBn>_=w0LvfEOmO zSHp$AjXfLw#1BP%iBW7!Zb8yTF9#*W@;jXQC)W8VqzD#R$zXh~3tATmiGn0=Ysu)nsGAoN zgC4nRWzUtH%7_CA^K@;DHYKd-hzvY;xX!kY>VJa#P}7lXTWms-Glj8nJ7z+@jP@;R z<@%Sgrpeh*aRy_F&&5{W1+r=?X!)R)+g}-K{-f!5o2YtLS0}h`CwdfkKFo8XuL@cb zl}G<@4mRmk7)*I{!Z>UM+=v}NMuOloVuwUr3Au_3P$@=K4*&9&hp%^;-sX~+kc)y` zWv2eLN;Hj^sNAr5SJD+6SzK8ELwkdBV5oxzeLy+lcf;yGBL3?vtAS*p z{XyPe`D$+AwDNF5d*%=1S%JNlQW8I3cdeH&+||cBI=8 zO|1wVS;l&HbJg5{13Y;(GHX}y`T#g7{@;xx-lwThRE#hd17CIjFKG*SgQok1N57~V z0{;9bH4LEe<9}X{HKX)PN>e4~4?5IT8zcSP!maO2Q9Iiu36uva8n9HAA#bs4fDDVl z^uK_p!pW{^{#SGUh(Bvst&V4xChUD*Zi!I@s50mbgEh-nf|rg)jP=0E)xXj}{LN+l zh+MjtG^8A4bTFM$Kz77?LERZ#S5tJJ_Q`tUkDU{&Tz#qanfVtwj>uOr9#yi?wzWdn z4m7S4og(0%J&U&%ia66V&F6kcu?t+pq@!eXx@yfyG=z8eVW6 zFtCNWs4VjdCR=tU^)J@k@09TrMKspR;0D`5Ci5JX4dYGFr3gZJaZ>APeewY)s~Eg; z_7PVgrXZ+ZiW7GjIo>5`c&lntDX5I_oQA^7+kjd_W(8@qA-V%L{8T)=%vx4=rNICe z9S|&~)ZvmXH`Uha=)0#Ex2M)&U##IwR&M=P!VH6ec+d6>W!1R&jQi)O@`$GP9}7Mz zAP!sYhruHJZTv_a;>0h>(xH;czXeJZwMFC@B!<$9ur}i+cjC}bRc>_I)s@k8|C2^+ zCZ@nBnbc^&#y8b|&nM($82Ay`1+G81W~L#%JJ7OHmf!lecw9d3Oq;cBWLOP+|9yX4 zvHQrt4wMq+7i{KNF6x*lDoKin490TwVZ;$?XzE0hlc^wDO3FHfe-N_uhyFhE0!;x? zNi227??hxlICPhP6jW{Nsc~ORD6fko@CihHGN3EI0)hW*3(B9!(~TjyHNMOtAF+Wb zV&dD^?^N;mXs!aQa{GM!>M4{7gc$z!ZDPwohPAiF0sVdOa2gjSrBJN`y(7pECP+R9 zAKHyd2#!kytk(F1o1kr{TDNuijdu4oN&R^^nA5NCWh~oChO{yJxNz|-MC7Yd;s_9^^+eK8MrYDE zA*O{=F2tGsUTCU;;q_hW&y6gP6S>f(?KeAr$h3YNRE%4 zri!f9ga&L)SJsG8mkH^E0+GTvPBj~S9B{70X^h3Z5y!AOgEVO`evn}jS_S9OwqxO=y90W9JkEx9 zM(DmD>drnN!1}iwDlTI3qMh{sqQ)9x!;-EOe2*Z(w zB7sybCxP0pCQbrR8-v30m0mVR?!m`a0p5wcMd>HRZ*knK*TDs`dAQ_`6E&}&6PI0P z(+lF->|@)}J$ioEHBCJuX!|#rT6fT*6CW2b9DwhghAJrDJ~HIv4mpJEOf>o&u z-fsvlM#8h!AO>xMKf=qK5%HAVX;Wnd6Uhv@0$wH|b5=R#?=AU&ydsSv!hK;KddXlj zgCGezyhN%^Fcr3z5-c5cxZ+gczwA_DmP2R|{$z9;NKZe-D!)B!TSOnZ=de$#volj! z=aLi?pK%c3L!7h!l)vttdd8M|*>x1Li2Ijn<0CKGG#7zVSAo(iFJb2Y&`2yUPH{6c zqyGL|@K$(;>u^&%ktqsJD1J5j;??JrF#hj!Rk8Iit*G7~!*efwISP^PoSg~n0U3{n>p%jmVZQW>gJLaAyxm zd}?0HPco>z#tg^Z*p8rm%f4{USi=w=jOtb9I-Jbwc&yEK+?*n(JIews!|I}BmOs%P z@184!Z<>2%QXMc#=>hZ}FiH)AZofR6;pPICX}*8fcu-p&Cn5h(aIQ$QVbZWbEikNT z(c}q@9^~r6ufROwUfB!`TDpjpQbNNTM(P3)lJw>*24|LaJ^@ye#oIQUSOn!6-@M8> z+~U(hI_sAw+6qlQ_EQdXHwkjQAW|3R(p~-`L{bT9a%yaL=M}z|xTcr%Aw~4qEcM?|ELj z$GBst!v8u$_TC`4ENaMhQCAXyzH@t}D#p}nh(a0}!|sRjR#ERrR~(V>L;M{XxC@)0 zb`BWVl7*4*1Buy(*=el**mgwXJ#h)wkEgSSKHYTth0I&({a$zL@56G*LSL5jbRqNL4Y1-L4g-+in>&*NY7!4mbJI(+dzYSCY`-)^|moNdg51MJ?6;dQuC*Ly4e1X49Q2 zA=z*|P0rnnrdz?4s?zcH^WZ#ZYa%K@Lw^gZ{a~W2`=_=xO8myUSzF1`2h$Q6tJN*8;z1GQV>Rl@I}&pVM}D>kf1};@9Qk?fxO%0EwpDV`2WbLo|}({rfC9 z%dko+OoT;%4;d#j=Btu;pz-8}`}p+3csCR9ACF1tg?}EQ-gd5%j?}Z9MnP;=|F#|~ z@`I6qX+U7g<9JyclM=lfB)3L>|H=%SZ~O>c@jv{|UThX%qlHW)Ni|?xFJ0*ME3FFJ zaKV#`TqQF*S!a!8{_ii`Cu+R{UqG{P=2@ryIAy*eT;wq1lVuWFoQV^e8Ww*A*z+>< zxAU_C5aMm3ITw61D)}MzX+(pYI6dKku@&#MfU^|6sI|4@|F1Cs<1n`?mt1P z5>j~h2%v!h1Uz(oL7Xtujh_H?)}7H0+$^-823N%l4Q-<1QpA9Z(Ycu%sd=dc+6r%` z^qRqo>b`>BcJBVi@EnwSb?SCC{Slc&{^eP)^&apqen8@!8|5b*emXySXSmMAK=e|v?0N{h_aUao zUESE!b1z=jk#n5gWc?BQfxi~EwJcD!kPRT8L;Z0B#1Xlg z_`{RvPei_>2}^^EB5fJNoK>KF`V56O1%)inDOE^#N<(oY>Xzld$Id||52P307csf#2k^(-`LhI2|3!WBm4*o~G zA(h6!CLj2?(r(5JiipIdbp>+y`hmVPL=oVIAa*%`RTV1=eMz7l)W^{VXsivYzt&lO z=%3L9B{Zic95Zu`8AC^Psx)i&ArU_FOIH!0s5ZGG=Tl1Qxu7)sVv%T!`z3+^yS`&; zG8=j4#G5zH>Lz*m{%0Ni@5lF?|2FW~TS2R5`@6X>F6bF3<+`IA9^%4N1!E4erT<8a z6}mWzziU}i=yN-QJX^fV3+qVS2BzI0s?Rg`j)0klMUA)l0;Ny#f|CC-rbj$R9#nF% z0cCgSy*DJKwxP7zMXse1stK*Um}1A!O2gP^nNP4X84tFCbeI|wb0YzZ!^j(M%#xG=yL zeZl?W#*T1dO~QTX(QYVMCsc2Wrz94D@gPwyuMdC@05#kr;@zMx4D8kDj`!MDb=ZYf z&>iBS*_wS=Lrn#4d2ig+2Rsn!cc>`U;`4uD>1FT37WRMZ6FHw@=6Nz51q%-@OARj7 zzQbHPT=>M#4LKsg3dS#c!E=QIRtut3Re8j+)&`1^w*VYPLIH3_fpiuZPJH0or*$`f zVZ^<(q=*CyzzjH2L3MM@7{ss&jcZ{2A7z-b^8*MgJjT{sEAuy_8FwRqq(*@YT}-Rj zqDDC{dKh49A=~D;enLI?0z##=pUq6YcV_gx?u`8Y{{vn+JyXdR{R?&izN@AAKrD!$aIgETBnH|1f8Y1?ctm+&0vCLmyBYIw{C;#QSDBS+JhF6i>+*dAr zuS!M}H$@?c5)mGg7DSySV^?q!i+8>zZWL+*O;%~teXMiQ`J5|VG}H3wUZ>fq+`YTK zmVpZeKXANBlB_6;Ucm~VY8A7!Q8UF}_K$LJi8$J~F^S?B!HDbDu2}C}-p_2sGyUOO zF%nGBdN|?_8dwV9Qd=JhYkUmY+TuQmRHqrT;Z^34v1q#Z%Q2Hn59o{2cT@*}k&bs+ z)p1-9d$5s1SElyoFVKF-utWcjD4Nnn)s4w1JZ!@Kseui1SdZsvVV8n(^P^~^dPH`d z-a`exUv?fmCKcAjP|EbGo zCdCP5+v2~NyrSiTvkEF--waae~T+rP(gAiQqgo2NeC)U*kwp>BR%Bwsx!IPStM1( zMFXxU%VcFhDhW!k#iZm+D(R2EZZ~3prbD!O*+vf5F!-R=s#{Kt$%va^Tl?Xp)XoXS0Ex7>${1ZvAGS;FQqTl0z79S1#cMnyN7sDT!PBd@&R|1 z(Ps@L{$nmpXG6pHa_dbm5BW4hHIw<5Ru(~?5WLgoi{37C`4W}*JSdIlb+x^U?wCAh zhJyCU$r8N-(Mu2i?z{7YC4)Ta zduef~YPG8ee0LTe%pN_G{>JMjxT-v7>}z`}zCpWN3T$0Iwf{0|#`a|GY-lc0@w z7A{o!Tw7lI32A+HGa-^=OZ+~X< zIMnKEj>mWDGGHDqk@MRl(x4n=$xUC^89HmKt@^C~g~j9hgJt#W%*7VpVV>!_2>&@^ zAiJ25GKUzpn~d851-|dVF(J)N10R{Y>*|jdYvZAI7joZEEy3!#d>^|W=my`*p|;60kC=>yus4jUfU1Pu=LyVlQNiJ03^Gep<3%Q>{FG_L8S z2bkgbds42+mg7hm|4FDS+E_U?C}NKoSX(O1+W+lmx1WO&)Z8Mh{g=F}eAq}kMkYI` zwNyl(Q3snjAI6;krnEE5p4VD5k75#3LGh3cVG2$FU!XFeYxZVVaF^jS+WjUl{=3Tz zwfQmlCv0`)hy-(Rfe_t~tboB9=T#f1Xbs+x5#@F?n)0K1I`5zO?<);v{h?G-o8O$( zZ~ve;tslg9rj=|ChYds*DJELhkE@d&LR#Bbt>xJ$&1uU3W8)jlS`6!QkU3l|!=OVK zZ+g-8#)?&`Mylw@u~tu$Lyw^$R9=X>n#%}p%PBIOH$F|59Bw`IaXVd1oJQZc80CtcrQXkOYH?DK;d>=YdViUDp~QC~ z9uo?k{?t*4m&^Y;*KbshE22P zvKqXyWoJjJAYRyi^F0k7r{doK+G;hDJ_U?@ZfZjd=K?#w@?`vy6F8b~?brV55q6R5 z&DsDSL~gh9%+)d$i_0M#I6VK(?`XFuih@5{^7R}gZPND!?FHrFc#2V`y(vAsTMe15 z+c5A08YLkbI|`rglk*Fm?W-fI7P^1_3xYV2om5fnhgshf0vaOG*Cl)Ya3z-56fI*B zEgdn_+Vsep?l*mF;_Q2=$~506*vik6B~rH=QYJNPGItL%LT+=kIIL+%v5kJblM#-E zv&>Vaq&vr_d(w%KIHRDW&U@}R>vt#93w+^nsg?uPLS{=Dp?{*moo0>rqdZV~&6$hQz&ESyOc z+gc;5iA!yi1&F=CM@UY|i%FmNger?cnsM`eVD;Dr2~1+Qs*Hwurrzvqt{yz-yKWAY z{SHVtWlLw~8Aaa6z94K69JMjKE&7)Jz;U1%THaC1gri(RaYHfdv( z?KV6i@nMzC(0gv(NVh=cC`aNag;9oD`$28V$Q4oKYe%7o71}=_YC) zloZ56DY5l)<#I<4I#B6)o8>aKM|1Pz-Q-j-a00PUPertil{Plh{`!QU-jb#iW#o$B zR8|N%iwU?l6QP(q?m0w*@-kIIIW1L+3Qrk6WJJPcAr$4)KsmeDFNZodnpV-hdNCXq zU!_@d7UEw*(IKooO?G#JmJhe9F^zfy(IJ~MGrRHE@rTtJ_a*FJ^>!wROQ}dp$ruk< zW7gD|aR`m#3?11vR^v6Vo(ni}PxL7on+WkU3QTZLl&)oW!V#DTY%K1$(|Ef3untNI+~pdm3^jyr*V)f*+mIXkmvVj@e1b#Ihow6 z%fj5W`H40$412pA4a@zi((D_+KUlL`;TPI|@}1XGe-nLhIkdiB*pW{j7+Er_=z4=5Zwh z>db$U2IeA_>f19;-|oM)-^;r?wbRT7#QE`me7_Pu7|rOG;ozHHqIB*}aA7}}^$Ng7 z_%jSXY>?{`9#kL>!_?(YnBwbgk~a4J;k1G2RzR$Bo%!;%@hxx@W{JfqwjsCCg2-R| z&h6H3ms`t7OzP;djH%lXcCVFF7FjVr(dO%2?Jk8s63zG62E(oB3l7Lc%>eGF=WnMg zNy`8DHwqq3&3Dgz8E3!B(SKa*qbNT4Ls9nS8gEoSujsaydwAq3liHEaU1gq!!VpD2 zauCIGb1=)e0bvpid%``=;)h}OKC0BWeq<|bcl7nN#%%o0vBQ@8PX`Kr=oB7`XJ}E4 znXA(cj5@V9`(1*Y>6wAu9B9FT5B+#cyadl#9}LWJCbFB-TQWBCY6}}o1(LCY`p5e5 zWQ+K08LO=SkF2*0imPj)g>iRxcXxMp2(H21A;I09;2PW^xVyVM1c%`6&OOh2@AvB~ z*i%ze%%)&YpI&SA+TG-`utRx;+PjNp7!zuVlx`+a@u`CtOkWJG0y(qH4Y z>{L*;?%~FG1(_JBq;TRYW^kvFw;#*toN>jCrnZkxHg;b6Gv-WJkF{22m^6^hkdf@2H=}7c0eRj` z$y=!zfF9UaT(J{n>$HZMU6ZbVi?QMp46F4mK_wxm9z&f{-CU8bQKd*VM$31&Qm9l~ zo(97pZs^u0D%Mx0nR(3BTRE&RTfD_I!CiqZH4<|ip|}Q@BU4i<{yNhG73FrnGbm$(Gdm&`d<6q$iLl&W$iBhI)eZh|72DS*L#2~#s|3` ztZW(kw^JNi@YzzKpJ?a3wvz z%SasZjPd)kZ>I2te~{34$EdD{c?LK@jVWT7e>J3v*QKn~=Y*CCTm}2_ zeK#lF{`Naw`4oShSj11qJ{DQWHASxp2QT3d$qm=ivTk4>^pUb0zor4~!^)#o^yPSK z-5iLxxXi2wX__P)i&K~+`V8wFBPiXT@@X==$PvGJ5Ht$*455>rr2{tC{C{kCiklev zxjnp`gGr9DE?$k=NuD&J*@cXpcc$PYDjNx@KlGx$%h&Ny>X^tw*-7^N(Kgxs^-~`> zcf@jWWZko3AaV|4)1LtZOKf>NyFE<%XK5)!aUK;xxtyp59hHx$XJ*KOrh%g3Asc#P z=Bl|+a*$CNG^}dZ+l$zANHVETMq+Z2(JH!rStbHBRjA0$>x!w@Q@2lfx)UaID;2Xc z-hpBkr}Y?km(Y{j+xaM@nPTYDV7nFEXhu|U7ZLw0C^3V#kMHR2Y{<~dcnA0$rhuS> zYp2=mQz2Kb;rD;jKD}(x^NuoJjH}s*zhJ=rkp`-gcy!oYyAAEw4eh9}?T9VyTvYV^ z(%Y7J&uVMifX|u9s0+B;H}|_fgxQQ6s;K68sy;QIsdwPIH$Rb=_J{d1XQdl1v591} zjy}*ky7B}X3>SptpO8Pt0ZqOjd0Guk?jMuq6MrhNOiJBfL%@xFSw~%awHn#wphv-) zGPfDua3rWs^ks1bL(7o~K}KGc!%diQCU(&NZ9n!zo&#VC;Cd8cAaScp_@0N7dZe7do9i$l@BRE~O!%D@<<6fWdA%FDp)}q|;JwL#nw&gYP4yD? zeCcfTyeoe3vBPP(3fbM@ZvoCr^{lnIGaInmpSUg;#iHiTsLHa7?QSOcDbQ^IE~eY9Z!-Q34hf5)9kW6 zelo6$kzeR+WMOwMDIYs`{EV6PG4@MjyVT!U^o@01hppo=VNJtZ6E7xqF$CQ)JMVTq zy7fqzn{0zNQA6!j_$j)G`zof>;3U-2B%D3Ud-=hYm_|Dgr7{Kw`SYO18o zfTk*b^Zdttu2d@U_U&H5JG`YNW;MYFoA+qaMuD-@5sr(%YK<|f@A(R3!ciHFc{&v* zc1_%2`?_YiQv$qAUJbu@Ju)q?Gr}Rp6!1vWtnKQ)Z~sLI%A=8hyDSml%tYGnHwz_C zqw;vY_%jDuQ~7zxf3#z0P=nF{)zzI46{oPfqE}>4Y;#xqb&V*$hY5;lOOd^lY&{3+!+TVY!RYo_HtwX=4X&{?=^&!b~ z`fdt#DzVjoz&<~_^>oZtUI*q1U5wTwabn<#*RfHqrJ~0`2bq8XV}CO(oyu*T#xV6= z5ScU)!@4p>tVs2-At7ltKsH2FZ^4HviKLlg8#vBt1#(UPisbNnHB_C;?cxk8Zl4}2 zNfcUZpt+uvTHA&g8y%ctjAM+L8bInKaiG2QHvZHRan?O+Hr*z2`1$VyLTh?I*Aw!e z=IO*U)xiPGCB3rgpLXyBIAI;hRod(+?;E9_2>;Kfu)+D}V~dpxhIvmd^DZ(QiXYzD z;W}_-iW7{t#!X(l$IsZ||6|~~A*<1Uba1UQ6k{c0q%o<)N;&JVaz@h|O>7BvPfHWV zL$(St2@IXo^I=_0lEtv7S;eZq+ut(>;)Jq+EMeyizs~w|JMww&yT#~h9gNkJ{T^Dw zaHtpg_fkrB!LJXp;qpC3q59^90(2s$3$NLFgcN?iwWbS4S2r05%X$^6E^py}*-$w> zUN;&|(T)Uo;W|Q*I4)@ne!ZXYR3l??Z*2n8Czxlmi zU3a$3>~_6CJzn4+D$+A#P5L5#dw%4`OY+Q$Gh9hPt=1@v14SHS8QXk9S$iYu*Xf~5 z)-NMOLA~T}RFPuGzF_b}Aa`425USAKT_^q^`Ur)+z4~xEzGx@<#2Ku_bTvH^FRKjzQD$ZdH(N};U*igyyIKxzU4>oxsk_N z8}5{6VdI_Gb4%E&hZlOMP1YL{^v^sk-%mCai1B`W2s>EtqbMX^XvA*Uq)hVwAj*(J zi;S!KU&;C&4rR85>7Wdq1B%F2c_o#$7233noC0Ubb&?aMq8&7f*EuV!gqw;Pd{fH* z6ta`yH!%TInp+FS$2}a*F^QJqT8RsHUbVVGO*2Fd^zkAJ!PJ6@5GLS-)B{GwC~GYQ z8!`03DZRcWTz2s7B@It+I=$SNiISw1PbvLmM9G{lK>1Bu>k#D)^{669>S zGrkroe77q!aO&!XF`p{xzkax(o$=tSv;4ynAlG#irVDA!CoWZ*e%M8GKR6;= zrxqj%r-XI0VKtZnu*QGj5Nglu7sc&iG>gA(AqV4L+_chRI~jV0?dY0n5xFS~NCWfR zYYSi;LVAx40~!OEnPDez<)o5<}&F*=kG*2;vU^NXNU zxrOntR&c-?hq9eK4^aa`RKi=Y@>GXcQ>~q<_hsSxqC5G8FsABoenpB$2;24Lt0})x z=FP!^S%`*r>Y}x3#&`m?IK26J>|fSf!BMei*!S|^Pa*OD+Ni}!|Ad#Jvjo>Aq?2Xj zW>jQlBQUG!jl%V2E|;nmvq@rJ@Mf7hZ>*F6&!m~34KX=T{xqEYEH-ls z?Jz`Z7M~dr8fg-K*Fub!Tty<=dqQRuM~|cb>V-c3oTaW!t=7Kp#E&2Hp7bsZOK3kvv@jiG9j3gO z57Pa!0I$W}=C|;xnBw#wm>RGyr|C@XP+C%oISfTFPL3(aj~bqNTE?)UtkN4R|2PFT zdz&|6p%aTmfXoN**lfCtPJIsEZ)~_XHb=^Iq9Tl6UNziw-3KQ5<3nO8J;K}y>ujqn z1>qfOPfkbw1Fg;q;A$`k;>Yx0fCe3zrTIQS$AW6`(38IKgY1v?9J<2bp2FrGXjRG? zndQDAPptc^WU^jl=Y@XRJVgq5{4c`>-g!Fyez#i3?Wh(n@U69d#MpAixv6UQ$d}F) z`gF8%g(Em~l10yxbMLDVSr`*3dGpNf!qm}bPpk6DjsF%$sjwsAnCLqw?Kb@H$99Un z)a|5Nljy8+@Hm%0bQ8POCtNX}N-?+OqglV7wY-t54Tc5Z@TOnw?@AP}wup6TlD06F zhQA4Z3RI{@HD1Z+*9f;&Ki^t$El{~OEBSXg(J>2%uav^!w7qv)hngpCdH%P4jX=`!2r^B=T#q(RwOR$w2$ZthzIrDmG zA_Rk{4|;4TWyUSnr`!CIMr(!R=Z<6OjDO{_S zD|2schJ$V7{U+e{QW$MIMYcYQBdC%}i#8FGmsuQhxG(F(wR?tskmTEhbiR+%AFC5u z`v!g;`OY}@KFr=-YWLwG8%U>B0~YU+$`OD))GO@uJPEb$~Re;-UY zGr`O8w~dUalR6B|q_D!@p1J062ZCKh@iE#2Ikh}$-E|cO$x-#xFMVZWdMOyp7z%!N zy{7iMiN7VmIPk3jWC_`oIs=fyH}JkzLv+Y44Ne|4E?ylr9s@4iCww&$hdd8Yt3=m* zb0R_ENh7YI$6`-6rVEpic_!78-UyaCt-?75p;x&AM60G6g3xbIBe;E|nCc<%otzgE zf-C5M7Fn<|etm=`T#?@Oj`T$|t2WbNUMm5@BEgKj#8uPLHFI}h6W#g<%Hh1mYSv|c zkCSfAG)|Z%z9B~}>F`~3#lKfB3@M60bv?Eb&y(xKjDou&wuZUXq#sRy7Cft((Kh%0 zRwQp8j2P~r_6@)D5Egeqvx})!CTE>%96I_xoxdiqm=)5wNR-KS+?Tzh{}P6rlCVN9 zcI?&&>FfdYC|0b80v4md^y!ls(t6^ldUoFeFYd{-l!lbRd#zG-f3o=oq$*XtD+Ixt zdYAJxl@qKWR8bo7Uonvv3zd3}pUk95Nq?)l8u5NprHpOd<;+@7%m@>jQnaAO#P0#} z#D58^c!tPlJ6jBQb_!m^^BYA{^a}H#-~Gt(%fSql_~nvBL2?^h&m-$UEcu@`t}DEfcL~&5fPH!!HJFTXFd(Y_gU(HyD}4D8z6u3blGzVxxfB2Dx8&1 z;)cHJ_hkCIteJb*kv{&sE@kQP+M7~=$^Of|x6z$&iR>S>$G&ZOJJ-|K<3+pWgD$Zm zPF(DAsxcmY{jOM8{_V`zyu9~X>x*;9vPbz!`4i!>qN(o44iRDgp*uJ4EI(dV$Sq>5 z=Zn6AHF`2h*;ES98EiPlq~Oi*#305d$<_?9w!`O{0BjgXPJ}-WCKIrFU|^m|(WU@C z$hP3l?dS(Tsa7b9gF9hHVrq(aw_+>Zn@0{@zg?*X+W;B_s-g!703w@nqy=g((^bVM zO*SsL(u-!qc#8TkHf-2xG!C6!-E;2H037_EP+5_jTHxyV*}4Z8rXNh716V0` zW=Nqk5z8@5HFE1JW`@S-EHBv7;4r6SQam1X15;iwTAs|bH&dwWGunk0tk9&$9M*t% zXnbb}g*pBFCH=xg!|N{=3uW~uYz{&UL)qpl93|sLORXFCYuvL_jafv2xQebt?ge@~ z@2tTvsyW}pHfDUA=9K(f169Mkj(# zWS{rQN0_odeeR-M46GP}8bx8egx{hM{0@R|j+Wpg+Mwa?>T*g}URvYyA61uJ8BH(*>HibM-c(gOm`e$nW&admmpW&L0x62ZYfz*J zi_pVSh@%RO&0bnS=4ufgacZP$Qalv#7y_JCf^WN@4ZM5$tc^Qd>O z3xPqXZay1H)0Mp@CUy6XB?eO3Ef<4dp2DqeUo@~2&zde3De0&gM?FgypP763$Iq0+ zm|0FAT!qs=lzBeeN z9u#dYd0Hk`@*C(QFk#^=&`?MJNe=g0!O28l%bnJ0fBOZY@0hbkyw4QM8sW*JD5wI_ z|4N3`A!l@c>J>&NP4-#2q*urLGL^{S1t&};wH*m2=lB(^u_b5Dy z+&`(a=Wo#U9diO=-oXJxg{qk5c#~~6N%|8JAna!>9{xs*EdH*p5Y1!7hw6)5V9ivo zTJV00>hiXYPuB(18-RkOhhwg-KFaI&P6w#&>xlzA3N#-}A0lE(8ihPMbF*TIz$!t< ztRt_1HdFQ!$heuY4mEq?`-A60lIWzEuc=%Xz}J(+e~q%{y(R9o?-rp0Lu(ga9WEqy zne)W*y4@{zbG=`8m<9oeu26oLE3LQxwq*`nnH4)&q&HQg_SE84d`?m4`5zMjP?tgI zPOf_5A&wR_dHGS)vOV-)ZR6SS<7Tf**_yT>_Wy=VcnS=zZSPa%AMF4J`@eRBS z5!!5yWy-|HdwhfAdaH?Dtv*$wwngBJHHTsmw|$)_$;jqJhco!t%gt?-wLQ3IF~!r< zvKdx0h#F0QjaKO#1-iJ%uzC@KWG=Sq=vzNN5zsjNgBO>o5|d z$atx=!i_f@4FcE>DpaJi0S%+mo3l)$?qOL^C zSODYN_Mg=pcpeVpo8$BWa#JL8!(Ci?Xq2;D4)YyTt^CAO^Xo=_N-Fq^xa+b7%f1CcqkHEAq3{dzzKAxQqSAc=+|V zW*GNE!g;a&SX=6r_Wa2mhZm-Jx*icP2I!Pi@;~;>Z zZd0WqCEtEpaHS^P18l*~DWo;i8Rvbu7&LN}6YuXJ3YZ@{xAe^Zif z4W(+G6%+%-F?!bp%mw}5E`*9&+p!@eMNXzAhTOG2rdz%dT`haFEWrX2leaU9q@XV7 ztc;Ei(_bZD`{!A#_6;Zgt0SLL8487Cdl?7~xIc{4Lp>0+~K&7MvdAOq&{(MgyB zysn|l@_iGOS-zWS!2ykP_i+IB#eq`m_XFj2e+^Xqp%>(*SKFVET-=*dKoydpat(=;idYI&;oSXnMJuw_e%Gd9qc;=Qzqbg=poGF!UK%g2)@%&dCQnYgS@O^(*{ zWw0`tzF+h-kYHMTfb0O+=2laXj~lSJ!nMH|)Dpl&<{U+PinY@c&M%N!kG--PVYjOA zIt@5ICYorVQ6}eSHe(8!$-WI~sr!r-ovArduE;Uf=rh)q3y&-Z64|8FX@N)8=qGlnmTBHMi+!gr`jbMLnn?3565B%nv+#Ug6~ zj#l|3JLKOikpcvaF1?Is7Uvw51X$&L5irCl6*TC$)<4T9-*L-mlB|=+)YILxrKqsI z*mVpf@+YHb<~e!e=9btk`HcCCE(Jth!d;b7lrL7J6RvW&w~da;E>E<`huTq+u|-u$ zDKE#;_TfwBM%$<>n%^EoUe-o{lJox){Q94(tmjD~{(Q?D4=1`5RgQO1nb{)| z#n-Gfxg#|^dNPu*(#hD>t^p6RxY<-Ozb(xbR^7*cqWcBa<<@qlo;{s264~boGe(glnjy_C8UfDq4DK0*5(0%Cg z=TVPv6Z?BaL^}HZ1{PpBl(bk8ENZ!=YH~nd-WitxOQ{7^dl=gjw(3ve9@iuofi_Mu zoUY+(|7Y-2pPLnOLH1L>?UC{vKBbr9Ba;}VLfG)CWE!CtgD7P`Y`_!Ta!K0di#+|x zxLUb9O!nb+xjMNc{9#c2x!n`{w44!k)w3Yuz+4`Q?z@#F8`7eSm^x);VO-*ZM4MrK zO=vXPk!yMNN3*n1-@;-rMwm;2NsSwkF8VAh3~PdcMOYEx_>L8ycERXqo+-|d(aEH> zH*}271xn9NvOh9$h+Z;R5jXe`uRZt552%uW7!@-F$!!F!g-{We(OhW}V zV1k2I%|O_V$u%GOH2Y|pJdgxU~!Hb;3EvhfdOiND`zgC>s3h(7!ncBj&dwTfT&&{y>yO%4it zcP{NV!`=5-w%X9WD7$HnEfzC4t~)E|Dai0Yfj&$L)1>pe)NRe|dxt&8k37)2bl~<_S0p4|y$!=q zfSVk=f|-!a_4?zEO7UV#xJSie_p5%N*qw?xEu={O*Z67CK@I|H|oh-yn?nBh>l6nP8!c zvHko_G2{zxKl@!b!!3E-?CN9AA{+%dO!V>diS=fxCUmRl_egn01jTxkhcXIf*w%~> z>wJ;szc&ox4_m69kJv1#%4Y0u)%c-*0O#4-HwTY|F#iWOt*`j6@@4~lQ0w(Dnt?)L zfe;3T5VRgJZTZv{LZFdCdpCAa{@0y<&hGcloEYrxXbYo*_Tx%ZpB$h2nvCb=+@^=; zs<}qIdLG%E3~5@SI~ouRW#N-4e&=OJ(k6iNoi@SnukV>QB&@dv_Zhx0R={@gJwh&-MCuYCgHdd%m(F(MGK~@TSD9X6>*!Ck!9VK88_L zo7om>@I8XJ1o{*zmkM#GSN5>B1Q;~ert7XkN6kwTE5bsy0_r2oI@@ycmQH{07tHkO z!+=^(7@Z{&c#n9u>f?w_tMn8HMKSZT8Q1pL0X4(I3^GSLm%|4s^iK9gTBCk!eK*7~ z{UqC*olI(+TL>)!8Yu2pD)|7`rubykSx2a322oZNz&h&OLq(HuLQVwP`HhQLjq7Er zY$|}Ujh@)aKYrw+ODI@LF#?^sIRI4eCQ04^Q0N8t9d1g+kxJ#Lk{06bOqO<-t9c0+ zwSCIA|0M+zwG@M8r@zye1WdIBD6WBoh8Oh1Ct-zCF~lil63}bw-xb87uFQze!SnFy z?<~qBn70`>GoS2zoTb*<)<$E#s1t+(T*zw8eLjQqV8F0KhrqhaAqKpv zqBQw24W8I!Nx#)RlGd9oKM_(2T%Ms($@Fl1iF2;1z|{^FDb+IVtQ-Qes)g0kt4r!Xbr7=6=& z_)pHuj1`keYT40#iQ~pdG#wi>X$szNe6IVJ>(zf321Sy+0R0irQql!|e~xgVv+Z@Y zW?WIV{`=mEq}xdP0QfS~QAdt!{qMciw`I1TH-d(?v5qs0$0%`#^F5xOqr!=yZTchc zQ#b$1Z(R>c=a?;wR$tTWu?Rk&xlR|AhB=Ra&6Z$v(sxdKUM}1zZ~%9T#qFEFJRmB< zb|C}K($Cxv%&^ZV|E+J^q*l7{8h=zgUtm(w;unE~7Ud6mGLD<#HfO(i&bY`XUmswl zy0ZeA{UYs)Eq+g1744iqd$exd9)Df!biyY~ zRd=dpPD0EZh0Uvsv>dQ78ncn!pfOLHp?n8iEqf~!nc$;AE}mAqn+(z zfX;Bk-{BYFEHe^Z;tl^xU|_Sj46QC@CwoNXZ|&F;Y1k9Ne1-={3Xl2NzWNZ?cOil2 zqM}8r=gQ-U(7_*F@2!=bEriFZgMm^A@>3J(fxX9tAjd*MC7^+bh(^jZhZe^Y9VQ*+ z1N-6nW5eJrZ^ZLzq*i+3iV(|L6a9KZ2=Zkfb7Ol~wsbV{26VFZrAt z0;KRS*5rNq5S$NbC~lL|@Esfk{zn&{YS9lg@@o`MIL}Wxm@(N&^FPtQS(GlHM3x45 zGEk2R-$+{ig#5BIp`&O14t+C=&oYyHFJpkH%0Ce5O5V9CSiGZ-CuHiD znf?KNwe=l>dG>(iJLrOfX|Gc-+cyYMdCtUC9~_od`K#5_;}Ml;)1@3MSylRlAI&MS zRR?P%pTq5=i8A%bS&Jjm6snOpO@1kcjJ>|Is?oP@ot2x|%aa<0c? z3wI-Twc-X5$>BqvYic2_f8>&hJ>O0Q6)~H_l87f1Ii0Gza@f{8))qbg0k^sR;d*?; z=lJ^cN&KZu)9@E1v*FjP_GyqC1@Q|H)c5!NzihU6ew)`QBcjcBJoUdv0uA3`ObS2~ zI9teot!Wz8qxgyhJn<5NLDH|EH#G)WdRgk9bYtZZotq!xp_=S2g}6`o90t2k?3g9X zKs@54LBk17Pi*|NOfa6R>ZM+Mtd)9D%c^9Y)`eAqf0@cb(o5P$flBo*O{I2g-%s zE$9YzosP@vZ|Ij7{1N<;)&jS0RX_!?2KtriJfp(pR#EjzzKyJle_*a@sPfl>&w1z4 z;9%=E3)SSuwEKYnG)UmM~=*K$_^*e=04xNA-Mw1O|cOjqu z>&JQNo?l#9cDkTU7|J<2QPqG^)h8sA9L6@Ej5|uSc2q~^HCQsXSZA?9i41hJ`6TT; zT$Yo+&b|)g&pn=EjJ;Y5Zm7NZ?Kp&1L-|ID~ZEALnBFq z7~v39AB3gTywW(;Cd~;ziPSJ-8ZGxD5HF3ROnj4nsR|U5nfl=hR$lxwdrjmI%3l}a zfsZwbnQh;NGz&I@uYq^Dw3=on_~9W}oaZ%Umg_xaRD%Y{8*gC<^H+kyeF|bPypyiK zyJjPPTAFQPjZFw%-A{;S%I*ix#i3hQ{`g5>cZQ-krnTI_z%)Umi^XqN(i1(TdYl8{ z-k!BkZ!GWQ@;H9~C_@ez#ooJ-$v3()r;p#E3eFryM$dYJ{2oK3W5_q2VfJf+bE1hrRd66vBSRY|0~f+|B!T z6No8;HvlKZQwqy|+K&?Y&f8f9|GeUWi(L=Ku$97_U7!L9{f89a9u>ZFkLt2)<;F=3 zOS$FvdGLUN$dSv*vMZAFCl;2Z9YfS=dQE0JKHD4?7pr*FEX7|K_y92&iNrU9k$!;G_6p)216Ty@OnbY;{m+L$Yr%P)8P$Dk!U z^gaxR2>DV$VB0$0j8Kg5F}(_5l*!$j}C|0|DAqjZ1#X= z_dB+yyj+lYDljGw(Zi>@E1<9EtYkd21q!Z86Vf*+FruKIRK;w*_`v&uIHTrC} z`lFk`jy7Pcq^uyF7gdf9`RF3RLl%#PzB<03kg-M}-Vehbnm<0pVUqXm7vqtDIt0uX z{KI(4*ePtT2}6m+)@a7NQd^cO(Ws<4j~x^u;Qlc!|48>)XGq&u(CMDR zpq50;rr3Y^Xc5a|MuVz>)bsV+`AZWrO;8D!W>p#H<5pl~x@*jqRvTy@kkR)mEi(Y2 z2xj1ex9MYLG)L$%9}meA;eCJgc9#d3_{dlNzIJ!~b<`95$2>hM8c{LAzMFcxPkv=2 z!u0s~qr*|`j3=9W_`Ex)o7gYybo2fv1ZZro50Xj!9Arv1{_a3mrx|~I;Ss-~^ppP= z-XxKixEx3T35nl~=@v}oH;=CxMUM_GqFmhZgJArrWF+&?aWj^ELeUHIB`c zhvyH7p^d&!0+kj^lsD$mOxOvq!)0lf9?~e;Mv2 zB$LG8lZx0BXLNV&V~+~TXv(9C-sdu(uv1T{rz8( zr3@oG$&4OS#CeCedGjYd!NRo&CS@GrofvftyEMq~83b%7s^>L35p1P%g?p=$TrV!I z!;vo8I7!fPeI{KWr}OMwXb4>jDLU2aFh9?t0P`vX!EHtIY!?5f^_DariX zNiGzfk~r>~F4v!7gLcc}NYtxxdc~OaFvc{9qt&%eUAS~I+dpn@8xZEVAxp-dz zY4P8gXi@*w8G(3svpaqlOWLjVcaLyB%^cYU|A}BWe_E1R{~FWp0!R6H$S}tq>bhb&ByupoGc3cL7wkTf98O{h&9eO|`TLS64wxH=WL($dAVO&J9>2Mz*UjO| zOQY{yGKBQXzyBq`DeX8DX#3tDe!&&Dk%kYzeG^-(i>vPV-m|7CjIHIvkw!~h^>mb< z=70Ey1c@5k?@O~D4)=44dG@2lF#I>31s%4I>ls83qoFuoN#K_MGZvflBW8>u@oMC* z)^`)W?sPkm{n8%wDuHQ;#bu|bku{qUq`b2%e~|rm+!B}F$0Z=j_|vicJJntRpU-0sumz~NlDjl)Ec&ko5!z(wU%D$b1*{k^?PohNst;>Xq- zGutEfOSC?-CfiD!YX8gj4HX3$BP&XtYLsV%R`JmJUoE=-zsR7>pPziW z?~4P!+gqEbmkhYgPJ5c}Z8kFK0b{mO}8Hl8ruS$cNrFcwx|uw^B27pmll6sp7 zFD_-l{||K8umhm$=f}3#MP>Qv96wT@QfzIcD{1a9yda1is43H?TH>w5@m7o!Lf$esTKYdP@a>>>Dye*kdiQXuZn<;^RQIj6a zt=vr*#nO-5_As_Ad!@WwcS|Lw*xC1DkmCGS=TC&m>Klu2zk%pWBYh!cPrl85KXXm| zd4B)>dN8FXBYhrdYgKxUDz>!Gw#WfvQH4dd5k8$%Xk)(nJ4=*ejzu+#Is98qbbw@> zzBLg2dSeeCa1K8&CqI7K;$}IS@K>^5#n0>lF)=_}o(&i|rK0M(QY=C~FN(YRTKPgW ze*^;%RsmBXlYZFX0P(=ijK6w}1k#9>Hn{Kc>zQ!hf$%4O#28csn*=PW&jenPTi0P7 zA5!?hO&pzmUHX*P@E3XJ{;C5BdLE~lsodx=!&1N5 zP@qD9Yi58@ut)&767Hq;n^8XY9E(!%8f;?D z4=Dei$t`>JFg6er-R{fwe1rr{n!7aBl!eD0o$OY$j>s)fgj)OdA@QJq+4`TO=N`q+ za0X1%fNybKoTBWg3D0bjs6agRtkqNWqB7i z$t7$(;5FsP2)Gg*WZW5LpE0y|_fG23!hYLw1XzlzyAlY zaGI`Mo2B;v@o*q$`suk&>ZXj#v-rw_let*>oXSir-Ni(H%|A{`QbR2ELuZp#;rtNTR2=C6|-h4=et% zgYVk@!lLTAW&~2*&$_`6VyWgkoJLb0GRAFR>W|Q&q9zRxDA?lc8=FWW+X=wMK=~j7< zPu;ognX=iN^70;Zl$ubIC>g9`_PTi$tzafR_oWnqreh)m#nDtXhnSTJloShL{v*-= z70Jmi+DEoDyZ-Gt@a*WS`mAq@Rk???#~bsAh=0rpK|qk;#JB(PBM>CLdflT@X9S*| z-J?i$L}4k61f{GVWX^J!4npMjfzi*6!O~K*8|((D>+@s;E=mOnGtdH&8ZCClY~t~q z+<1fGFP>{AzYS+d18c(ep{oai>DX)3Z-o$lc4ZN}O!l3?K)wMDpSgoWOzj{Eq1Of+ zd-0CL44EY2tVM)Bv8uZ1C(T2LXJlrgx^0sV0-m6muuD&S3W( za}fuQxqgSaM|zF#ACmp^%VPRY1({He|DT*Xh~^Atx)zfH&b~SSdb#m2VL03{s$^)p z&&R_Tvtll|v#M>M%NqIH&hh1yn^N@4B`uF`7~gk;x`f^1K4RlGl8`m*wgNNc9!T%n zcsfmMJvdT4=1#sUo>1F7Eu($4BH7FNZkI$rDD+1h{>H8dp~G=vGMIQf4+2|!9NdK(!# z+0(-;bXybWYYQj?^{9sG@&cI|4xD^gn|70PLAW|m3b)I5sqB(`!(ouZ*;zwG>~i4D zS>L1s9#x3%O*?k2?BIzzhkaVW4?Czy7Fz;jUKGENh#jDa(F=im3MA7@Mh^(uWi=)EcQ&an;(r35DNcYo}# znKs91@o1Dt(#9X0KRf3<7sWY$SiLoqE_Rm~>07PwnYn#H0iRA(^U*_MV@i~1<}81_ zYKyGZ64kHVPg6-nC!QD{q{iS5l(s+DaZ?hB`PJ(&_sxdi9y~!ti8hg`_1Zr3a(M*W z_Zc9^`^{03Vlg?v{|{Yn85BqVM2+GuiwD=>!QEXF+zAle-Q5@W;4Z;~LvVKq?(Xgq zT<$#od+XNw<^8%fH9ND_efsn{z5PS5?+n{W7

J^{(a?BZ61ZfJQ7++ZL;sASBMp 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ξykD17ycp~ChwMf)~){tv` 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 Date: Fri, 15 Sep 2023 09:27:03 -0400 Subject: [PATCH 010/105] Add official govulncheck action to Go Tests workflow (#28456) * Add official govulncheck action * Revert "Add official govulncheck action" * formatting * add workflow dispatch * Remove work-dir arg, clean names * target package * try at 1.01 for work-dir * oops --- .github/workflows/go_tests.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/go_tests.yml b/.github/workflows/go_tests.yml index 07e58bba2f1d..f52065e60ebd 100644 --- a/.github/workflows/go_tests.yml +++ b/.github/workflows/go_tests.yml @@ -29,6 +29,7 @@ on: branches: ['master', 'release-*'] tags: ['v*'] paths: ['sdks/go/pkg/**', 'sdks/go.mod', 'sdks/go.sum', 'sdks/go/container/*', 'sdks/java/container/*', 'sdks/python/container/*', 'sdks/typescript/container/*'] + workflow_dispatch: # This allows a subsequently queued workflow run to interrupt previous runs concurrency: group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' @@ -65,3 +66,8 @@ jobs: go install "honnef.co/go/tools/cmd/staticcheck@2023.1.3" cd sdks/go/pkg/beam $(go env GOPATH)/bin/staticcheck ./... + - uses: golang/govulncheck-action@v1.0.1 + with: + work-dir: ./sdks + go-package: ./... + go-version-input: 1.21 \ No newline at end of file From f09955e47d71c1fc53396c707290f9d6de6369f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Sep 2023 10:20:50 -0400 Subject: [PATCH 011/105] Bump google.golang.org/grpc from 1.58.0 to 1.58.1 in /sdks (#28464) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.58.0 to 1.58.1. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.58.0...v1.58.1) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdks/go.mod | 2 +- sdks/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sdks/go.mod b/sdks/go.mod index 5e91aea021f8..3cf0b4eb5c8b 100644 --- a/sdks/go.mod +++ b/sdks/go.mod @@ -59,7 +59,7 @@ require ( golang.org/x/text v0.13.0 google.golang.org/api v0.140.0 google.golang.org/genproto v0.0.0-20230821184602-ccc8af3d0e93 - google.golang.org/grpc v1.58.0 + google.golang.org/grpc v1.58.1 google.golang.org/protobuf v1.31.0 gopkg.in/retry.v1 v1.0.3 gopkg.in/yaml.v2 v2.4.0 diff --git a/sdks/go.sum b/sdks/go.sum index c30891294dbd..b53fdd5c3f69 100644 --- a/sdks/go.sum +++ b/sdks/go.sum @@ -689,8 +689,8 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.58.0 h1:32JY8YpPMSR45K+c3o6b8VL73V+rR8k+DeMIr4vRH8o= -google.golang.org/grpc v1.58.0/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.58.1 h1:OL+Vz23DTtrrldqHK49FUOPHyY75rvFqJfXC84NYW58= +google.golang.org/grpc v1.58.1/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= From 108680aadbd124e11954b40ffccfa2926c8e3846 Mon Sep 17 00:00:00 2001 From: Arwin Tio Date: Fri, 15 Sep 2023 08:45:37 -0700 Subject: [PATCH 012/105] gcsio.py use RuntimeError with exc chaining instead of overwriting previous exception (#28470) * use RuntimeError with exc chaining instead of overwriting previous exception * update changes --- CHANGES.md | 3 ++- sdks/python/apache_beam/io/gcp/gcsio.py | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 40a9a1dc9490..3705cebc88df 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -80,7 +80,8 @@ ## Bugfixes -* Fixed X (Java/Python) ([#X](https://github.com/apache/beam/issues/X)). +* Fixed exception chaining issue in GCS connector (Python) ([#26769](https://github.com/apache/beam/issues/26769#issuecomment-1700422615)). + ## Security Fixes * Python containers updated, fixing [CVE-2021-30474](https://nvd.nist.gov/vuln/detail/CVE-2021-30474), [CVE-2021-30475](https://nvd.nist.gov/vuln/detail/CVE-2021-30475), [CVE-2021-30473](https://nvd.nist.gov/vuln/detail/CVE-2021-30473), [CVE-2020-36133](https://nvd.nist.gov/vuln/detail/CVE-2020-36133), [CVE-2020-36131](https://nvd.nist.gov/vuln/detail/CVE-2020-36131), [CVE-2020-36130](https://nvd.nist.gov/vuln/detail/CVE-2020-36130), and [CVE-2020-36135](https://nvd.nist.gov/vuln/detail/CVE-2020-36135) diff --git a/sdks/python/apache_beam/io/gcp/gcsio.py b/sdks/python/apache_beam/io/gcp/gcsio.py index 2fdbce73170a..d75af4fe6ac1 100644 --- a/sdks/python/apache_beam/io/gcp/gcsio.py +++ b/sdks/python/apache_beam/io/gcp/gcsio.py @@ -825,5 +825,4 @@ def finish(self): # Check for exception since the last put() call. if self._upload_thread.last_error is not None: e = self._upload_thread.last_error - raise type(self._upload_thread.last_error)( - "Error while uploading file %s" % self._path) from e # pylint: disable=raising-bad-type + raise RuntimeError("Error while uploading file %s" % self._path) from e From 8415534040bf2876a56ee7bdc8283ed6751bc1a2 Mon Sep 17 00:00:00 2001 From: Andrey Devyatkin Date: Fri, 15 Sep 2023 19:23:29 +0200 Subject: [PATCH 013/105] added beam_PostCommit_PortableJar jobs to GitHub Actions (#28447) --- .github/workflows/README.md | 2 + .../beam_PostCommit_PortableJar_Flink.yml | 88 +++++++++++++++++++ .../beam_PostCommit_PortableJar_Spark.yml | 88 +++++++++++++++++++ 3 files changed, 178 insertions(+) create mode 100644 .github/workflows/beam_PostCommit_PortableJar_Flink.yml create mode 100644 .github/workflows/beam_PostCommit_PortableJar_Spark.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 5513604f5cfc..383df86d1496 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -215,6 +215,8 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | [ PostCommit Java ValidatesRunner SparkStructuredStreaming ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml) | N/A |`Run Spark StructuredStreaming ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml) | | [ PostCommit Java ValidatesRunner Twister2 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml) | N/A |`Run Twister2 ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml) | | [ PostCommit Java ValidatesRunner ULR ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml) | N/A |`Run ULR Loopback ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml) | +| [ PostCommit PortableJar Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_PortableJar_Flink.yml) | N/A |`Run PortableJar_Flink PostCommit`| [![.github/workflows/beam_PostCommit_PortableJar_Flink.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_PortableJar_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_PortableJar_Flink.yml) | +| [ PostCommit PortableJar Spark ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_PortableJar_Spark.yml) | N/A |`Run PortableJar_Spark PostCommit`| [![.github/workflows/beam_PostCommit_PortableJar_Spark.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_PortableJar_Spark.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_PortableJar_Spark.yml) | | [ PostCommit Python Examples Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Dataflow.yml) | N/A |`Run Python Examples_Dataflow`| [![.github/workflows/beam_PostCommit_Python_Examples_Dataflow.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Dataflow.yml) | | [ PostCommit Python Examples Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml) | ['3.8','3.9','3.10','3.11'] |`Run Python Examples_Direct (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_Examples_Direct.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml) | | [ PostCommit Python Examples Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml) | ['3.8','3.11'] |`Run Python Examples_Flink (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_Examples_Flink.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml) | diff --git a/.github/workflows/beam_PostCommit_PortableJar_Flink.yml b/.github/workflows/beam_PostCommit_PortableJar_Flink.yml new file mode 100644 index 000000000000..f3670a377df5 --- /dev/null +++ b/.github/workflows/beam_PostCommit_PortableJar_Flink.yml @@ -0,0 +1,88 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit PortableJar Flink + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_PortableJar_Flink: + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + if: | + github.event_name == 'schedule' || + github.event_name == 'workflow_dispatch' || + github.event.comment.body == 'Run PortableJar_Flink PostCommit' + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 120 + strategy: + matrix: + job_name: ["beam_PostCommit_PortableJar_Flink"] + job_phrase: ["Run PortableJar_Flink PostCommit"] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: run testPipelineJarFlinkRunner script + env: + CLOUDSDK_CONFIG: ${{ env.KUBELET_GCLOUD_CONFIG_PATH}} + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:python:test-suites:portable:py38:testPipelineJarFlinkRunner + arguments: | + -PpythonVersion=3.8 \ + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + with: + name: python-code-coverage-report + path: "**/pytest*.xml" \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_PortableJar_Spark.yml b/.github/workflows/beam_PostCommit_PortableJar_Spark.yml new file mode 100644 index 000000000000..a4079b654874 --- /dev/null +++ b/.github/workflows/beam_PostCommit_PortableJar_Spark.yml @@ -0,0 +1,88 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit PortableJar Spark + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_PortableJar_Spark: + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + if: | + github.event_name == 'schedule' || + github.event_name == 'workflow_dispatch' || + github.event.comment.body == 'Run PortableJar_Spark PostCommit' + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 100 + strategy: + matrix: + job_name: ["beam_PostCommit_PortableJar_Spark"] + job_phrase: ["Run PortableJar_Spark PostCommit"] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: run testPipelineJarSparkRunner script + env: + CLOUDSDK_CONFIG: ${{ env.KUBELET_GCLOUD_CONFIG_PATH}} + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:python:test-suites:portable:py38:testPipelineJarSparkRunner + arguments: | + -PpythonVersion=3.8 \ + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + with: + name: python-code-coverage-report + path: "**/pytest*.xml" \ No newline at end of file From 52eed458d2aa3fbb9011377d055d1c41f1a3110c Mon Sep 17 00:00:00 2001 From: Andrey Devyatkin Date: Fri, 15 Sep 2023 19:24:44 +0200 Subject: [PATCH 014/105] Migrate "beam_PostCommit_Python_ValidatesContainer_Dataflow" Jenkins jobs to GitHub Actions (#28448) * added beam_PostCommit_Python_ValidatesContainer jobs to GitHub Actions * updated README file --- .github/workflows/README.md | 4 +- ...mit_Python_ValidatesContainer_Dataflow.yml | 105 +++++++++++++++++ ...on_ValidatesContainer_Dataflow_With_RC.yml | 106 ++++++++++++++++++ 3 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml create mode 100644 .github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 383df86d1496..c89067a97097 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -204,7 +204,7 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | [ PostCommit Java ValidatesRunner Dataflow Streaming ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml) | N/A |`Run Dataflow Streaming ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml) | | [ PostCommit Java ValidatesRunner Dataflow V2 Streaming ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml) | N/A |`Run Java Dataflow V2 ValidatesRunner Streaming`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml) | | [ PostCommit Java ValidatesRunner Dataflow V2 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml) | N/A |`Run Java Dataflow V2 ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml) | -| [.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml) | N/A |`Run Dataflow ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml) | +| [ PostCommit Java ValidatesRunner Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml) | N/A |`Run Dataflow ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml) | | [ PostCommit Java ValidatesRunner Direct JavaVersions ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_Java.yml) | ['11','17'] |`Run Direct ValidatesRunner Java (matrix_element)`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_Java.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_Java.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_Java.yml) | | [ PostCommit Java ValidatesRunner Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml) | N/A |`Run Direct ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml) | | [ PostCommit Java ValidatesRunner Flink Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml) | N/A |`Run Flink ValidatesRunner Java 11`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml) | @@ -221,6 +221,8 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | [ PostCommit Python Examples Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml) | ['3.8','3.9','3.10','3.11'] |`Run Python Examples_Direct (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_Examples_Direct.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml) | | [ PostCommit Python Examples Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml) | ['3.8','3.11'] |`Run Python Examples_Flink (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_Examples_Flink.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml) | | [ PostCommit Python Examples Spark ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Spark.yml) | ['3.8','3.11'] |`Run Python Examples_Spark (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_Examples_Spark.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Spark.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Spark.yml) | +| [ PostCommit Python ValidatesContainer Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml) | ['3.8','3.9','3.10','3.11'] |`Run Python Dataflow ValidatesContainer (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml) | +| [ PostCommit Python ValidatesContainer Dataflow With RC ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml) | ['3.8','3.9','3.10','3.11'] |`Run Python RC Dataflow ValidatesContainer (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml) | | [ PostCommit Python ValidatesRunner Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml) | ['3.8','3.11'] |`Run Python Dataflow ValidatesRunner (matrix_element)`| [![PostCommit Python ValidatesRunner Dataflow](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml) | | [ PostCommit Python ValidatesRunner Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml) | ['3.8','3.11'] |`Run Python Flink ValidatesRunner (matrix_element)`| [![PostCommit Python ValidatesRunner Flink](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml) | | [ PostCommit Python ValidatesRunner Samza ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml) | ['3.8','3.11'] |`Run Python Samza ValidatesRunner (matrix_element)`| [![PostCommit Python ValidatesRunner Samza](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml) | diff --git a/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml b/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml new file mode 100644 index 000000000000..dc2d2554b9a1 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml @@ -0,0 +1,105 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit Python ValidatesContainer Dataflow + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Python_ValidatesContainer_Dataflow: + if: | + github.event_name == 'schedule' || + github.event_name == 'workflow_dispatch' || + startsWith(github.event.comment.body, 'Run Python Dataflow ValidatesContainer') + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 100 + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + strategy: + fail-fast: false + matrix: + job_name: ["beam_PostCommit_Python_ValidatesContainer_Dataflow"] + job_phrase: ["Run Python Dataflow ValidatesContainer"] + python_version: ['3.8','3.9','3.10','3.11'] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python_version }} + - name: Install Java + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: '8' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + with: + cache-read-only: false + - name: Set PY_VER_CLEAN + id: set_py_ver_clean + run: | + PY_VER=${{ matrix.python_version }} + PY_VER_CLEAN=${PY_VER//.} + echo "py_ver_clean=$PY_VER_CLEAN" >> $GITHUB_OUTPUT + - name: Run validatesContainer script + env: + USER: github-actions + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:python:test-suites:dataflow:py${{steps.set_py_ver_clean.outputs.py_ver_clean}}:validatesContainer + arguments: | + -PpythonVersion=${{ matrix.python_version }} \ + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + with: + name: python-code-coverage-report + path: "**/pytest*.xml" \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml b/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml new file mode 100644 index 000000000000..a4c518c930c2 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml @@ -0,0 +1,106 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit Python ValidatesContainer Dataflow With RC + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC: + if: | + github.event_name == 'schedule' || + github.event_name == 'workflow_dispatch' || + startsWith(github.event.comment.body, 'Run Python RC Dataflow ValidatesContainer') + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 100 + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + strategy: + fail-fast: false + matrix: + job_name: ["beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC"] + job_phrase: ["Run Python RC Dataflow ValidatesContainer"] + python_version: ['3.8','3.9','3.10','3.11'] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python_version }} + - name: Install Java + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: '8' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + with: + cache-read-only: false + - name: Set PY_VER_CLEAN + id: set_py_ver_clean + run: | + PY_VER=${{ matrix.python_version }} + PY_VER_CLEAN=${PY_VER//.} + echo "py_ver_clean=$PY_VER_CLEAN" >> $GITHUB_OUTPUT + - name: Run validatesContainer script + env: + USER: github-actions + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:python:test-suites:dataflow:py${{steps.set_py_ver_clean.outputs.py_ver_clean}}:validatesContainer + arguments: | + -PtestRCDependencies=true + -PpythonVersion=${{ matrix.python_version }} \ + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + with: + name: python-code-coverage-report + path: "**/pytest*.xml" \ No newline at end of file From da6f1348ef098a65e74f82bc3c920dd1489d35e2 Mon Sep 17 00:00:00 2001 From: magicgoody <131876064+magicgoody@users.noreply.github.com> Date: Fri, 15 Sep 2023 23:26:28 +0600 Subject: [PATCH 015/105] beam_PostCommit_Python_Xlang (#28466) --- .github/workflows/README.md | 3 + ...m_PostCommit_Python_Xlang_Gcp_Dataflow.yml | 88 ++++++++++++++++++ ...eam_PostCommit_Python_Xlang_Gcp_Direct.yml | 87 ++++++++++++++++++ ...am_PostCommit_Python_Xlang_IO_Dataflow.yml | 90 +++++++++++++++++++ 4 files changed, 268 insertions(+) create mode 100644 .github/workflows/beam_PostCommit_Python_Xlang_Gcp_Dataflow.yml create mode 100644 .github/workflows/beam_PostCommit_Python_Xlang_Gcp_Direct.yml create mode 100644 .github/workflows/beam_PostCommit_Python_Xlang_IO_Dataflow.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index c89067a97097..190fcae1771b 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -227,6 +227,9 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | [ PostCommit Python ValidatesRunner Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml) | ['3.8','3.11'] |`Run Python Flink ValidatesRunner (matrix_element)`| [![PostCommit Python ValidatesRunner Flink](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml) | | [ PostCommit Python ValidatesRunner Samza ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml) | ['3.8','3.11'] |`Run Python Samza ValidatesRunner (matrix_element)`| [![PostCommit Python ValidatesRunner Samza](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml) | | [ PostCommit Python ValidatesRunner Spark ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml) | ['3.8','3.9','3.11'] |`Run Python Spark ValidatesRunner (matrix_element)`| [![PostCommit Python ValidatesRunner Spark](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml) | +| [ PostCommit Python Xlang Gcp Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Xlang_Gcp_Dataflow.yml) | N/A |`Run Python_Xlang_Gcp_Dataflow PostCommit`| [![.github/workflows/beam_PostCommit_Python_Xlang_Gcp_Dataflow.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Xlang_Gcp_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Xlang_Gcp_Dataflow.yml) | +| [ PostCommit Python Xlang Gcp Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Xlang_Gcp_Direct.yml) | N/A |`Run Python_Xlang_Gcp_Direct PostCommit`| [![.github/workflows/beam_PostCommit_Python_Xlang_Gcp_Direct.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Xlang_Gcp_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Xlang_Gcp_Direct.yml) | +| [ PostCommit Python Xlang IO Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Xlang_IO_Dataflow.yml) | N/A |`Run Python_Xlang_IO_Dataflow PostCommit`| [![.github/workflows/beam_PostCommit_Python_Xlang_IO_Dataflow.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Xlang_IO_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Xlang_IO_Dataflow.yml) | | [ PostCommit Sickbay Python ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Sickbay_Python.yml) | ['3.8','3.9','3.10','3.11'] |`Run Python (matrix_element) PostCommit Sickbay`| [![.github/workflows/beam_PostCommit_Sickbay_Python.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Sickbay_Python.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Sickbay_Python.yml) | | [ PostCommit TransformService Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_TransformService_Direct.yml) | N/A |`Run TransformService_Direct PostCommit`| [![.github/workflows/beam_PostCommit_TransformService_Direct.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_TransformService_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_TransformService_Direct.yml) | [ PostCommit Website Publish ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml) | N/A | N/A | [![.github/workflows/beam_PostCommit_Website_Publish.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml) | diff --git a/.github/workflows/beam_PostCommit_Python_Xlang_Gcp_Dataflow.yml b/.github/workflows/beam_PostCommit_Python_Xlang_Gcp_Dataflow.yml new file mode 100644 index 000000000000..c52dd1869432 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Python_Xlang_Gcp_Dataflow.yml @@ -0,0 +1,88 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit Python Xlang Gcp Dataflow + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Python_Xlang_Gcp_Dataflow: + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Python_Xlang_Gcp_Dataflow PostCommit' + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 180 + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + strategy: + matrix: + job_name: ["beam_PostCommit_Python_Xlang_Gcp_Dataflow"] + job_phrase: ["Run Python_Xlang_Gcp_Dataflow PostCommit"] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: | + 3.8 + 3.11 + - name: run PostCommit Python Xlang Gcp Dataflow script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:python:test-suites:dataflow:gcpCrossLanguagePostCommit + arguments: -PuseWheelDistribution + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + if: always() + with: + name: archiveJunit + path: "**/pytest*.xml" \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Python_Xlang_Gcp_Direct.yml b/.github/workflows/beam_PostCommit_Python_Xlang_Gcp_Direct.yml new file mode 100644 index 000000000000..e1b5e401438f --- /dev/null +++ b/.github/workflows/beam_PostCommit_Python_Xlang_Gcp_Direct.yml @@ -0,0 +1,87 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit Python Xlang Gcp Direct + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Python_Xlang_Gcp_Direct: + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Python_Xlang_Gcp_Direct PostCommit' + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 100 + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + strategy: + matrix: + job_name: ["beam_PostCommit_Python_Xlang_Gcp_Direct"] + job_phrase: ["Run Python_Xlang_Gcp_Direct PostCommit"] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: | + 3.8 + 3.11 + - name: run PostCommit Python Xlang Gcp Direct script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:python:test-suites:direct:gcpCrossLanguagePostCommit + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + if: always() + with: + name: archiveJunit + path: "**/pytest*.xml" \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Python_Xlang_IO_Dataflow.yml b/.github/workflows/beam_PostCommit_Python_Xlang_IO_Dataflow.yml new file mode 100644 index 000000000000..0937fcbadb6c --- /dev/null +++ b/.github/workflows/beam_PostCommit_Python_Xlang_IO_Dataflow.yml @@ -0,0 +1,90 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit Python Xlang IO Dataflow + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Python_Xlang_IO_Dataflow: + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Python_Xlang_IO_Dataflow PostCommit' + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 180 + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + strategy: + matrix: + job_name: ["beam_PostCommit_Python_Xlang_IO_Dataflow"] + job_phrase: ["Run Python_Xlang_IO_Dataflow PostCommit"] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: | + 3.8 + 3.11 + - name: run PostCommit Python Xlang IO Dataflow script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:python:test-suites:dataflow:ioCrossLanguagePostCommit + arguments: | + -PuseWheelDistribution \ + -PkafkaBootstrapServer=10.128.0.40:9094,10.128.0.28:9094,10.128.0.165:9094 \ + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + if: always() + with: + name: archiveJunit + path: "**/pytest*.xml" \ No newline at end of file From cfc96940d5a104e2beb0dcc4f2e0693ca75cefd9 Mon Sep 17 00:00:00 2001 From: magicgoody <131876064+magicgoody@users.noreply.github.com> Date: Fri, 15 Sep 2023 23:28:17 +0600 Subject: [PATCH 016/105] Github Workflow Replacement for Jenkins Jobs, beam_PostCommit_XVR_* (#28432) * beam_PostCommit_XVR * update --- .github/workflows/README.md | 7 ++ .../workflows/beam_PostCommit_XVR_Direct.yml | 104 +++++++++++++++++ .../workflows/beam_PostCommit_XVR_Flink.yml | 105 ++++++++++++++++++ ...ostCommit_XVR_JavaUsingPython_Dataflow.yml | 92 +++++++++++++++ ...Commit_XVR_PythonUsingJavaSQL_Dataflow.yml | 89 +++++++++++++++ ...ostCommit_XVR_PythonUsingJava_Dataflow.yml | 92 +++++++++++++++ .../workflows/beam_PostCommit_XVR_Samza.yml | 104 +++++++++++++++++ .../workflows/beam_PostCommit_XVR_Spark3.yml | 104 +++++++++++++++++ 8 files changed, 697 insertions(+) create mode 100644 .github/workflows/beam_PostCommit_XVR_Direct.yml create mode 100644 .github/workflows/beam_PostCommit_XVR_Flink.yml create mode 100644 .github/workflows/beam_PostCommit_XVR_JavaUsingPython_Dataflow.yml create mode 100644 .github/workflows/beam_PostCommit_XVR_PythonUsingJavaSQL_Dataflow.yml create mode 100644 .github/workflows/beam_PostCommit_XVR_PythonUsingJava_Dataflow.yml create mode 100644 .github/workflows/beam_PostCommit_XVR_Samza.yml create mode 100644 .github/workflows/beam_PostCommit_XVR_Spark3.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 190fcae1771b..66866de5ec2e 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -234,6 +234,13 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | [ PostCommit TransformService Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_TransformService_Direct.yml) | N/A |`Run TransformService_Direct PostCommit`| [![.github/workflows/beam_PostCommit_TransformService_Direct.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_TransformService_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_TransformService_Direct.yml) | [ PostCommit Website Publish ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml) | N/A | N/A | [![.github/workflows/beam_PostCommit_Website_Publish.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml) | | [ PostCommit XVR GoUsingJava Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml) | N/A |`Run XVR_GoUsingJava_Dataflow PostCommit`| [![.github/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml) | +| [ PostCommit XVR Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_Direct.yml) | N/A |`Run XVR_Direct PostCommit`| [![.github/workflows/beam_PostCommit_XVR_Direct](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_Direct.yml) | +| [ PostCommit XVR Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_Flink.yml) | N/A |`Run XVR_Flink PostCommit`| [![.github/workflows/beam_PostCommit_XVR_Flink](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_Flink.yml) | +| [ PostCommit XVR JavaUsingPython Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_JavaUsingPython_Dataflow.yml) | N/A |`Run XVR_JavaUsingPython_Dataflow PostCommit`| [![.github/workflows/beam_PostCommit_XVR_JavaUsingPython_Dataflow](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_JavaUsingPython_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_JavaUsingPython_Dataflow.yml) | +| [ PostCommit XVR PythonUsingJava Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_PythonUsingJava_Dataflow.yml) | N/A |`Run XVR_PythonUsingJava_Dataflow PostCommit`| [![.github/workflows/beam_PostCommit_XVR_PythonUsingJava_Dataflow](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_PythonUsingJava_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_PythonUsingJava_Dataflow.yml) | +| [ PostCommit XVR PythonUsingJavaSQL Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_PythonUsingJavaSQL_Dataflow.yml) | N/A |`Run XVR_PythonUsingJavaSQL_Dataflow PostCommit`| [![.github/workflows/beam_PostCommit_XVR_PythonUsingJavaSQL_Dataflow](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_PythonUsingJavaSQL_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_PythonUsingJavaSQL_Dataflow.yml) | +| [ PostCommit XVR Samza ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_Samza.yml) | N/A |`Run XVR_Samza PostCommit`| [![.github/workflows/beam_PostCommit_XVR_Samza](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_Samza.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_Samza.yml) | +| [ PostCommit XVR Spark3 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_Spark3.yml) | N/A |`Run XVR_Spark3 PostCommit`| [![.github/workflows/beam_PostCommit_XVR_Spark3](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_Spark3.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_Spark3.yml) | | [ PreCommit Community Metrics ](https://github.com/apache/beam/actions/workflows/beam_PreCommit_CommunityMetrics.yml) | N/A |`Run CommunityMetrics PreCommit`| [![.github/workflows/beam_PreCommit_CommunityMetrics.yml](https://github.com/apache/beam/actions/workflows/beam_PreCommit_CommunityMetrics.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PreCommit_CommunityMetrics.yml) | | [ PreCommit Go ](https://github.com/apache/beam/actions/workflows/beam_PreCommit_Go.yml) | N/A |`Run Go PreCommit`| [![.github/workflows/beam_PreCommit_Go.yml](https://github.com/apache/beam/actions/workflows/beam_PreCommit_Go.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PreCommit_Go.yml) | | [ PreCommit Java ](https://github.com/apache/beam/actions/workflows/beam_PreCommit_Java.yml) | N/A |`Run Java PreCommit`| [![.github/workflows/beam_PreCommit_Java.yml](https://github.com/apache/beam/actions/workflows/beam_PreCommit_Java.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PreCommit_Java.yml) | diff --git a/.github/workflows/beam_PostCommit_XVR_Direct.yml b/.github/workflows/beam_PostCommit_XVR_Direct.yml new file mode 100644 index 000000000000..0f74e817f4e9 --- /dev/null +++ b/.github/workflows/beam_PostCommit_XVR_Direct.yml @@ -0,0 +1,104 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit XVR Direct + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_XVR_Direct: + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run XVR_Direct PostCommit' + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 120 + name: ${{matrix.job_name}} (${{matrix.job_phrase}} ${{ matrix.python_version }}) + strategy: + matrix: + job_name: ["beam_PostCommit_XVR_Direct"] + job_phrase: ["Run XVR_Direct PostCommit"] + python_version: ['3.8','3.11'] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: | + 3.8 + 3.11 + - name: run PostCommit XVR Direct script + env: + CLOUDSDK_CONFIG: ${{ env.KUBELET_GCLOUD_CONFIG_PATH}} + if: ${{ matrix.python_version != '3.8' }} + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:python:test-suites:direct:xlang:validatesCrossLanguageRunner + arguments: | + -PpythonVersion=${{ matrix.python_version }} \ + -PskipNonPythonTask=true \ + - name: run PostCommit XVR Direct script + env: + CLOUDSDK_CONFIG: ${{ env.KUBELET_GCLOUD_CONFIG_PATH}} + if: ${{ matrix.python_version == '3.8' }} + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:python:test-suites:direct:xlang:validatesCrossLanguageRunner + arguments: | + -PpythonVersion=${{ matrix.python_version }} \ + -PskipNonPythonTask=false \ + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + if: always() + with: + name: archiveJunit + path: "**/build/test-results/**/*.xml" \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_XVR_Flink.yml b/.github/workflows/beam_PostCommit_XVR_Flink.yml new file mode 100644 index 000000000000..fd2cb95d9c32 --- /dev/null +++ b/.github/workflows/beam_PostCommit_XVR_Flink.yml @@ -0,0 +1,105 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit XVR Flink + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + FlinkVersion: 1.15 + +jobs: + beam_PostCommit_XVR_Flink: + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run XVR_Flink PostCommit' + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 100 + name: ${{matrix.job_name}} (${{matrix.job_phrase}} ${{ matrix.python_version }}) + strategy: + matrix: + job_name: ["beam_PostCommit_XVR_Flink"] + job_phrase: ["Run XVR_Flink PostCommit"] + python_version: ['3.8','3.11'] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: | + 3.8 + 3.11 + - name: run PostCommit XVR Flink script + env: + CLOUDSDK_CONFIG: ${{ env.KUBELET_GCLOUD_CONFIG_PATH}} + if: ${{ matrix.python_version != '3.8' }} + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:flink:${{ env.FlinkVersion }}:job-server:validatesCrossLanguageRunner + arguments: | + -PpythonVersion=${{ matrix.python_version }} \ + -PskipNonPythonTask=true \ + - name: run PostCommit XVR Flink script + env: + CLOUDSDK_CONFIG: ${{ env.KUBELET_GCLOUD_CONFIG_PATH}} + if: ${{ matrix.python_version == '3.8' }} + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:flink:${{ env.FlinkVersion }}:job-server:validatesCrossLanguageRunner + arguments: | + -PpythonVersion=${{ matrix.python_version }} \ + -PskipNonPythonTask=false \ + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + if: always() + with: + name: archiveJunit + path: "**/build/test-results/**/*.xml" \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_XVR_JavaUsingPython_Dataflow.yml b/.github/workflows/beam_PostCommit_XVR_JavaUsingPython_Dataflow.yml new file mode 100644 index 000000000000..dbdfbe2e7c73 --- /dev/null +++ b/.github/workflows/beam_PostCommit_XVR_JavaUsingPython_Dataflow.yml @@ -0,0 +1,92 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit XVR JavaUsingPython Dataflow + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_XVR_JavaUsingPython_Dataflow: + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run XVR_JavaUsingPython_Dataflow PostCommit' + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 100 + name: ${{matrix.job_name}} (${{matrix.job_phrase}} ${{ matrix.python_version }}) + strategy: + matrix: + job_name: ["beam_PostCommit_XVR_JavaUsingPython_Dataflow"] + job_phrase: ["Run XVR_JavaUsingPython_Dataflow PostCommit"] + python_version: ['3.8','3.11'] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: | + 3.8 + 3.11 + - name: run PostCommit XVR JavaUsingPython Dataflow script + env: + CLOUDSDK_CONFIG: ${{ env.KUBELET_GCLOUD_CONFIG_PATH}} + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:google-cloud-dataflow-java:validatesCrossLanguageRunnerJavaUsingPython + arguments: | + -PpythonVersion=${{ matrix.python_version }} \ + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + if: always() + with: + name: archiveJunit + path: "**/build/test-results/**/*.xml" \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_XVR_PythonUsingJavaSQL_Dataflow.yml b/.github/workflows/beam_PostCommit_XVR_PythonUsingJavaSQL_Dataflow.yml new file mode 100644 index 000000000000..68239e8329c2 --- /dev/null +++ b/.github/workflows/beam_PostCommit_XVR_PythonUsingJavaSQL_Dataflow.yml @@ -0,0 +1,89 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit XVR PythonUsingJavaSQL Dataflow + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_XVR_PythonUsingJavaSQL_Dataflow: + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run XVR_PythonUsingJavaSQL_Dataflow PostCommit' + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 100 + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + strategy: + matrix: + job_name: ["beam_PostCommit_XVR_PythonUsingJavaSQL_Dataflow"] + job_phrase: ["Run XVR_PythonUsingJavaSQL_Dataflow PostCommit"] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + - name: run PostCommit XVR PythonUsingJavaSQL Dataflow script + env: + CLOUDSDK_CONFIG: ${{ env.KUBELET_GCLOUD_CONFIG_PATH}} + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:google-cloud-dataflow-java:validatesCrossLanguageRunnerPythonUsingSql + arguments: | + -PpythonVersion=3.11 \ + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + if: always() + with: + name: archiveJunit + path: "**/pytest*.xml" \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_XVR_PythonUsingJava_Dataflow.yml b/.github/workflows/beam_PostCommit_XVR_PythonUsingJava_Dataflow.yml new file mode 100644 index 000000000000..eb184bf0c905 --- /dev/null +++ b/.github/workflows/beam_PostCommit_XVR_PythonUsingJava_Dataflow.yml @@ -0,0 +1,92 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit XVR PythonUsingJava Dataflow + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_XVR_PythonUsingJava_Dataflow: + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run XVR_PythonUsingJava_Dataflow PostCommit' + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 100 + name: ${{matrix.job_name}} (${{matrix.job_phrase}} ${{ matrix.python_version }}) + strategy: + matrix: + job_name: ["beam_PostCommit_XVR_PythonUsingJava_Dataflow"] + job_phrase: ["Run XVR_PythonUsingJava_Dataflow PostCommit"] + python_version: ['3.8','3.11'] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: | + 3.8 + 3.11 + - name: run PostCommit XVR PythonUsingJava Dataflow script + env: + CLOUDSDK_CONFIG: ${{ env.KUBELET_GCLOUD_CONFIG_PATH}} + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:google-cloud-dataflow-java:validatesCrossLanguageRunnerPythonUsingJava + arguments: | + -PpythonVersion=${{ matrix.python_version }} \ + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + if: always() + with: + name: archiveJunit + path: "**/pytest*.xml" \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_XVR_Samza.yml b/.github/workflows/beam_PostCommit_XVR_Samza.yml new file mode 100644 index 000000000000..1ae684bc6911 --- /dev/null +++ b/.github/workflows/beam_PostCommit_XVR_Samza.yml @@ -0,0 +1,104 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit XVR Samza + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_XVR_Samza: + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run XVR_Samza PostCommit' + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 100 + name: ${{matrix.job_name}} (${{matrix.job_phrase}} ${{ matrix.python_version }}) + strategy: + matrix: + job_name: ["beam_PostCommit_XVR_Samza"] + job_phrase: ["Run XVR_Samza PostCommit"] + python_version: ['3.8','3.11'] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: | + 3.8 + 3.11 + - name: run PostCommit XVR Samza script + env: + CLOUDSDK_CONFIG: ${{ env.KUBELET_GCLOUD_CONFIG_PATH}} + if: ${{ matrix.python_version != '3.8' }} + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:samza:job-server:validatesCrossLanguageRunner + arguments: | + -PpythonVersion=${{ matrix.python_version }} \ + -PskipNonPythonTask=true \ + - name: run PostCommit XVR Samza script + env: + CLOUDSDK_CONFIG: ${{ env.KUBELET_GCLOUD_CONFIG_PATH}} + if: ${{ matrix.python_version == '3.8' }} + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:samza:job-server:validatesCrossLanguageRunner + arguments: | + -PpythonVersion=${{ matrix.python_version }} \ + -PskipNonPythonTask=false \ + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + if: always() + with: + name: archiveJunit + path: "**/build/test-results/**/*.xml" \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_XVR_Spark3.yml b/.github/workflows/beam_PostCommit_XVR_Spark3.yml new file mode 100644 index 000000000000..3cf4300222b3 --- /dev/null +++ b/.github/workflows/beam_PostCommit_XVR_Spark3.yml @@ -0,0 +1,104 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit XVR Spark3 + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_XVR_Spark3: + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run XVR_Spark3 PostCommit' + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 100 + name: ${{matrix.job_name}} (${{matrix.job_phrase}} ${{ matrix.python_version }}) + strategy: + matrix: + job_name: ["beam_PostCommit_XVR_Spark3"] + job_phrase: ["Run XVR_Spark3 PostCommit"] + python_version: ['3.8','3.11'] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: | + 3.8 + 3.11 + - name: run PostCommit XVR Spark3 script + env: + CLOUDSDK_CONFIG: ${{ env.KUBELET_GCLOUD_CONFIG_PATH}} + if: ${{ matrix.python_version != '3.8' }} + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:spark:3:job-server:validatesCrossLanguageRunner + arguments: | + -PpythonVersion=${{ matrix.python_version }} \ + -PskipNonPythonTask=true \ + - name: run PostCommit XVR Spark3 script + env: + CLOUDSDK_CONFIG: ${{ env.KUBELET_GCLOUD_CONFIG_PATH}} + if: ${{ matrix.python_version == '3.8' }} + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:spark:3:job-server:validatesCrossLanguageRunner + arguments: | + -PpythonVersion=${{ matrix.python_version }} \ + -PskipNonPythonTask=false \ + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + if: always() + with: + name: archiveJunit + path: "**/build/test-results/**/*.xml" \ No newline at end of file From 2dd8d28d4241b3ac7a9e94c2c4bf4924512bbdb7 Mon Sep 17 00:00:00 2001 From: Andrey Devyatkin Date: Fri, 15 Sep 2023 19:31:00 +0200 Subject: [PATCH 017/105] Migrate "beam_PostCommit_Java_Examples_*" Jenkins jobs to GitHub Actions (#28409) * added beam_PostCommit_Java_Examples jobs to GitHub Actions * added beam_PostCommit_Java_Examples jobs to GitHub Actions * added reporting steps * added reporting steps * updated README file --- .github/workflows/README.md | 6 +- ..._PostCommit_Java_Examples_Dataflow_ARM.yml | 2 +- ...ostCommit_Java_Examples_Dataflow_Java.yml} | 27 +++-- ... beam_PostCommit_Java_Examples_Direct.yml} | 25 ++--- .../beam_PostCommit_Java_Examples_Flink.yml | 101 ++++++++++++++++++ .../beam_PostCommit_Java_Examples_Spark.yml | 93 ++++++++++++++++ 6 files changed, 223 insertions(+), 31 deletions(-) rename .github/workflows/{beam_PostCommit_Java_Examples_Dataflow_Java11.yml => beam_PostCommit_Java_Examples_Dataflow_Java.yml} (80%) rename .github/workflows/{beam_PostCommit_Java_Examples_Dataflow_Java17.yml => beam_PostCommit_Java_Examples_Direct.yml} (82%) create mode 100644 .github/workflows/beam_PostCommit_Java_Examples_Flink.yml create mode 100644 .github/workflows/beam_PostCommit_Java_Examples_Spark.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 66866de5ec2e..9a0d6a4799f5 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -191,8 +191,10 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | [ PostCommit Java Dataflow V1 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV1.yml) | N/A |`Run PostCommit_Java_Dataflow`| [![.github/workflows/beam_PostCommit_Java_DataflowV1.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV1.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV1.yml) | | [ PostCommit Java Dataflow V2 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV2.yml) | N/A |`Run PostCommit_Java_DataflowV2`| [![.github/workflows/beam_PostCommit_Java_DataflowV2.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV2.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV2.yml) | | [ PostCommit Java Examples Dataflow ARM ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml) | N/A |`Run Java_Examples_Dataflow_ARM PostCommit`| [![.github/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml) | -| [ PostCommit Java Examples Dataflow Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml) | N/A |`Run Java examples on Dataflow Java 11`| [![.github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml) | -| [ PostCommit Java Examples Dataflow Java17 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java17.yml) | N/A |`Run Java examples on Dataflow Java 17`| [![.github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java17.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java17.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java17.yml) | +| [ PostCommit Java Examples Dataflow Java ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java.yml) | ['11','17'] |`Run Java examples on Dataflow Java (matrix_element)`| [![.github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Dataflow_Java.yml) | +| [ PostCommit Java Examples Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Direct.yml) | N/A |`Run Java Examples_Direct`| [![.github/workflows/beam_PostCommit_Java_Examples_Direct.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Direct.yml) | +| [ PostCommit Java Examples Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Flink.yml) | N/A |`Run Java Examples_Flink`| [![.github/workflows/beam_PostCommit_Java_Examples_Flink.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Flink.yml) | +| [ PostCommit Java Examples Spark ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Spark.yml) | N/A |`Run Java Examples_Spark`| [![.github/workflows/beam_PostCommit_Java_Examples_Spark.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Spark.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Spark.yml) | | [ PostCommit Java Jpms Dataflow Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11.yml) | N/A |`Run Jpms Dataflow Java 11 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11.yml) | | [ PostCommit Java Jpms Dataflow Java17 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17.yml) | N/A |`Run Jpms Dataflow Java 17 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17.yml) | | [ PostCommit Java Jpms Direct Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java11.yml) | N/A |`Run Jpms Direct Java 11 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java11](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java11.yml) | diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml index 596b7f35ab5f..8516ccd393a6 100644 --- a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml +++ b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml @@ -84,7 +84,7 @@ jobs: with: comment_phrase: ${{ matrix.job_phrase }} ${{matrix.java_version}} github_token: ${{ secrets.GITHUB_TOKEN }} - github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) ${{matrix.java_version}} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{matrix.java_version}}) - name: Setup self-hosted uses: ./.github/actions/setup-self-hosted-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java.yml similarity index 80% rename from .github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml rename to .github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java.yml index bc26da173042..7015c4fee20a 100644 --- a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java11.yml +++ b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java.yml @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -name: PostCommit Java Examples Dataflow Java11 +name: PostCommit Java Examples Dataflow Java on: issue_comment: @@ -51,40 +51,39 @@ env: GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} jobs: - beam_PostCommit_Java_Examples_Dataflow_Java11: - name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + beam_PostCommit_Java_Examples_Dataflow_Java: + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.java_version }}) runs-on: [self-hosted, ubuntu-20.04, main] timeout-minutes: 180 strategy: fail-fast: false matrix: - job_name: [beam_PostCommit_Java_Examples_Dataflow_Java11] - job_phrase: [Run Java examples on Dataflow Java 11] + job_name: [beam_PostCommit_Java_Examples_Dataflow_Java] + job_phrase: [Run Java examples on Dataflow Java] + java_version: ['11','17'] if: | github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || - github.event.comment.body == 'Run Java examples on Dataflow Java 11' + startswith(github.event.comment.body, 'Run Java examples on Dataflow Java') steps: - uses: actions/checkout@v3 - name: Setup repository uses: ./.github/actions/setup-action with: - comment_phrase: ${{ matrix.job_phrase }} + comment_phrase: ${{ matrix.job_phrase }} ${{matrix.java_version}} github_token: ${{ secrets.GITHUB_TOKEN }} - github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - # The test requires Java 11 and Java 8 versions. - # Java 8 is installed second because JAVA_HOME needs to point to Java 8. - - name: Set up Java + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.java_version }}) + - name: Set up Java${{ matrix.java_version }} uses: actions/setup-java@v3.8.0 with: distribution: 'temurin' java-version: | - 11 + ${{ matrix.java_version }} 8 - - name: run PostCommit Java Examples Dataflow Java11 script + - name: run java${{ matrix.java_version }}PostCommit script uses: ./.github/actions/gradle-command-self-hosted-action with: - gradle-command: :runners:google-cloud-dataflow-java:examples:java11PostCommit + gradle-command: :runners:google-cloud-dataflow-java:examples:java${{ matrix.java_version }}PostCommit max-workers: 12 - name: Archive JUnit Test Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java17.yml b/.github/workflows/beam_PostCommit_Java_Examples_Direct.yml similarity index 82% rename from .github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java17.yml rename to .github/workflows/beam_PostCommit_Java_Examples_Direct.yml index f61cc41d5222..6831d7db3b5d 100644 --- a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java17.yml +++ b/.github/workflows/beam_PostCommit_Java_Examples_Direct.yml @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -name: PostCommit Java Examples Dataflow Java17 +name: PostCommit Java Examples Direct on: issue_comment: @@ -51,18 +51,18 @@ env: GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} jobs: - beam_PostCommit_Java_Examples_Dataflow_Java17: + beam_PostCommit_Java_Examples_Direct: name: ${{matrix.job_name}} (${{matrix.job_phrase}}) runs-on: [self-hosted, ubuntu-20.04, main] - timeout-minutes: 180 + timeout-minutes: 120 strategy: matrix: - job_name: [beam_PostCommit_Java_Examples_Dataflow_Java17] - job_phrase: [Run Java examples on Dataflow Java 17] + job_name: [beam_PostCommit_Java_Examples_Direct] + job_phrase: [Run Java Examples_Direct] if: | github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || - github.event.comment.body == 'Run Java examples on Dataflow Java 17' + github.event.comment.body == 'Run Java Examples_Direct' steps: - uses: actions/checkout@v3 - name: Setup repository @@ -71,18 +71,15 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Set up Java + - name: Install Java uses: actions/setup-java@v3.8.0 with: - distribution: 'temurin' - java-version: | - 17 - 8 - - name: run PostCommit Java Examples Dataflow Java17 script + distribution: 'zulu' + java-version: '8' + - name: run examplesIntegrationTest script uses: ./.github/actions/gradle-command-self-hosted-action with: - gradle-command: :runners:google-cloud-dataflow-java:examples:java17PostCommit - max-workers: 12 + gradle-command: :runners:direct:examplesIntegrationTest - name: Archive JUnit Test Results uses: actions/upload-artifact@v3 if: failure() diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Flink.yml b/.github/workflows/beam_PostCommit_Java_Examples_Flink.yml new file mode 100644 index 000000000000..60644c434edc --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_Examples_Flink.yml @@ -0,0 +1,101 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java Examples Flink + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_Examples_Flink: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 120 + strategy: + matrix: + job_name: [beam_PostCommit_Java_Examples_Flink] + job_phrase: [Run Java Examples_Flink] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Java Examples_Flink' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Java + uses: actions/setup-java@v3.8.0 + with: + distribution: 'zulu' + java-version: '8' + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: '3.8' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + with: + cache-read-only: false + - name: run examplesIntegrationTest script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:flink:1.15:examplesIntegrationTest + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Spark.yml b/.github/workflows/beam_PostCommit_Java_Examples_Spark.yml new file mode 100644 index 000000000000..17cbc8e583e2 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_Examples_Spark.yml @@ -0,0 +1,93 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java Examples Spark + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_Examples_Spark: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 120 + strategy: + matrix: + job_name: [beam_PostCommit_Java_Examples_Spark] + job_phrase: [Run Java Examples_Spark] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Java Examples_Spark' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Java + uses: actions/setup-java@v3.8.0 + with: + distribution: 'zulu' + java-version: '8' + - name: run examplesIntegrationTest script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:spark:3:examplesIntegrationTest + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file From a0f14a84c3f62015740cc61c8dbb900a5a41c625 Mon Sep 17 00:00:00 2001 From: Robert Burke Date: Fri, 15 Sep 2023 10:39:26 -0700 Subject: [PATCH 018/105] Prism docker env (#28444) * Move envs to own file. Err, not panic. * artifact and docker finagling * kill containers on job completion. * switch to docker sdk * tiny cleanup, log copy propperly * Fix pulls. Reduce logging. * Reduce metrics spam. * Minimize diff * Check only after worker start * Correct chunk size. --------- Co-authored-by: lostluck <13907733+lostluck@users.noreply.github.com> --- .../beam/core/runtime/metricsx/metricsx.go | 3 +- .../runners/prism/internal/environments.go | 199 ++++++++++++++++++ .../beam/runners/prism/internal/execute.go | 90 +++----- .../prism/internal/jobservices/artifact.go | 48 ++++- .../runners/prism/internal/jobservices/job.go | 10 + .../prism/internal/jobservices/management.go | 2 + .../prism/internal/jobservices/server.go | 5 + .../runners/prism/internal/worker/worker.go | 17 +- .../beam/runners/universal/runnerlib/stage.go | 2 +- 9 files changed, 299 insertions(+), 77 deletions(-) create mode 100644 sdks/go/pkg/beam/runners/prism/internal/environments.go diff --git a/sdks/go/pkg/beam/core/runtime/metricsx/metricsx.go b/sdks/go/pkg/beam/core/runtime/metricsx/metricsx.go index 4a872e291c6a..c71ead208364 100644 --- a/sdks/go/pkg/beam/core/runtime/metricsx/metricsx.go +++ b/sdks/go/pkg/beam/core/runtime/metricsx/metricsx.go @@ -24,6 +24,7 @@ import ( "github.com/apache/beam/sdks/v2/go/pkg/beam/core/graph/coder" "github.com/apache/beam/sdks/v2/go/pkg/beam/core/metrics" pipepb "github.com/apache/beam/sdks/v2/go/pkg/beam/model/pipeline_v1" + "golang.org/x/exp/slog" ) // FromMonitoringInfos extracts metrics from monitored states and @@ -139,7 +140,7 @@ func groupByType(p *pipepb.Pipeline, minfos []*pipepb.MonitoringInfo) ( } } if len(errs) > 0 { - log.Printf("Warning: %v errors during metrics processing: %v\n", len(errs), errs) + slog.Debug("errors during metrics processing", "count", len(errs), "errors", errs) } return counters, distributions, gauges, msecs, pcols } diff --git a/sdks/go/pkg/beam/runners/prism/internal/environments.go b/sdks/go/pkg/beam/runners/prism/internal/environments.go new file mode 100644 index 000000000000..5830325bd054 --- /dev/null +++ b/sdks/go/pkg/beam/runners/prism/internal/environments.go @@ -0,0 +1,199 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package internal + +import ( + "bytes" + "context" + "fmt" + "io" + "os" + + fnpb "github.com/apache/beam/sdks/v2/go/pkg/beam/model/fnexecution_v1" + pipepb "github.com/apache/beam/sdks/v2/go/pkg/beam/model/pipeline_v1" + "github.com/apache/beam/sdks/v2/go/pkg/beam/runners/prism/internal/jobservices" + "github.com/apache/beam/sdks/v2/go/pkg/beam/runners/prism/internal/urns" + "github.com/apache/beam/sdks/v2/go/pkg/beam/runners/prism/internal/worker" + "golang.org/x/exp/slog" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/protobuf/proto" + + dtyp "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/mount" + dcli "github.com/docker/docker/client" + "github.com/docker/docker/pkg/stdcopy" +) + +// TODO move environment handling to the worker package. + +func runEnvironment(ctx context.Context, j *jobservices.Job, env string, wk *worker.W) error { + logger := slog.With(slog.String("envID", wk.Env)) + // TODO fix broken abstraction. + // We're starting a worker pool here, because that's the loopback environment. + // It's sort of a mess, largely because of loopback, which has + // a different flow from a provisioned docker container. + e := j.Pipeline.GetComponents().GetEnvironments()[env] + switch e.GetUrn() { + case urns.EnvExternal: + ep := &pipepb.ExternalPayload{} + if err := (proto.UnmarshalOptions{}).Unmarshal(e.GetPayload(), ep); err != nil { + logger.Error("unmarshing external environment payload", "error", err) + } + go func() { + externalEnvironment(ctx, ep, wk) + slog.Debug("environment stopped", slog.String("job", j.String())) + }() + return nil + case urns.EnvDocker: + dp := &pipepb.DockerPayload{} + if err := (proto.UnmarshalOptions{}).Unmarshal(e.GetPayload(), dp); err != nil { + logger.Error("unmarshing docker environment payload", "error", err) + } + return dockerEnvironment(ctx, logger, dp, wk, j.ArtifactEndpoint()) + default: + return fmt.Errorf("environment %v with urn %v unimplemented", env, e.GetUrn()) + } +} + +func externalEnvironment(ctx context.Context, ep *pipepb.ExternalPayload, wk *worker.W) { + conn, err := grpc.Dial(ep.GetEndpoint().GetUrl(), grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + panic(fmt.Sprintf("unable to dial sdk worker %v: %v", ep.GetEndpoint().GetUrl(), err)) + } + defer conn.Close() + pool := fnpb.NewBeamFnExternalWorkerPoolClient(conn) + + endpoint := &pipepb.ApiServiceDescriptor{ + Url: wk.Endpoint(), + } + pool.StartWorker(ctx, &fnpb.StartWorkerRequest{ + WorkerId: wk.ID, + ControlEndpoint: endpoint, + LoggingEndpoint: endpoint, + ArtifactEndpoint: endpoint, + ProvisionEndpoint: endpoint, + Params: ep.GetParams(), + }) + // Job processing happens here, but orchestrated by other goroutines + // This goroutine blocks until the context is cancelled, signalling + // that the pool runner should stop the worker. + <-ctx.Done() + + // Previous context cancelled so we need a new one + // for this request. + pool.StopWorker(context.Background(), &fnpb.StopWorkerRequest{ + WorkerId: wk.ID, + }) +} + +func dockerEnvironment(ctx context.Context, logger *slog.Logger, dp *pipepb.DockerPayload, wk *worker.W, artifactEndpoint string) error { + logger = logger.With("worker_id", wk.ID, "image", dp.GetContainerImage()) + + // TODO consider preserving client? + cli, err := dcli.NewClientWithOpts(dcli.FromEnv, dcli.WithAPIVersionNegotiation()) + if err != nil { + return fmt.Errorf("couldn't connect to docker:%w", err) + } + + // TODO abstract mounting cloud specific auths better. + const gcloudCredsEnv = "GOOGLE_APPLICATION_CREDENTIALS" + gcloudCredsFile, ok := os.LookupEnv(gcloudCredsEnv) + var mounts []mount.Mount + var envs []string + if ok { + _, err := os.Stat(gcloudCredsFile) + // File exists + if err == nil { + dockerGcloudCredsFile := "/docker_cred_file.json" + mounts = append(mounts, mount.Mount{ + Type: "bind", + Source: gcloudCredsFile, + Target: dockerGcloudCredsFile, + }) + credEnv := fmt.Sprintf("%v=%v", gcloudCredsEnv, dockerGcloudCredsFile) + envs = append(envs, credEnv) + } + } + + if rc, err := cli.ImagePull(ctx, dp.GetContainerImage(), dtyp.ImagePullOptions{}); err == nil { + // Copy the output, but discard it so we can wait until the image pull is finished. + io.Copy(io.Discard, rc) + rc.Close() + } else { + logger.Warn("unable to pull image", "error", err) + } + + ccr, err := cli.ContainerCreate(ctx, &container.Config{ + Image: dp.GetContainerImage(), + Cmd: []string{ + fmt.Sprintf("--id=%v-%v", wk.JobKey, wk.Env), + fmt.Sprintf("--control_endpoint=%v", wk.Endpoint()), + fmt.Sprintf("--artifact_endpoint=%v", artifactEndpoint), + fmt.Sprintf("--provision_endpoint=%v", wk.Endpoint()), + fmt.Sprintf("--logging_endpoint=%v", wk.Endpoint()), + }, + Env: envs, + Tty: false, + }, &container.HostConfig{ + NetworkMode: "host", + Mounts: mounts, + }, nil, nil, "") + if err != nil { + cli.Close() + return fmt.Errorf("unable to create container image %v with docker for env %v, err: %w", dp.GetContainerImage(), wk.Env, err) + } + containerID := ccr.ID + logger = logger.With("container", containerID) + + if err := cli.ContainerStart(ctx, containerID, dtyp.ContainerStartOptions{}); err != nil { + cli.Close() + return fmt.Errorf("unable to start container image %v with docker for env %v, err: %w", dp.GetContainerImage(), wk.Env, err) + } + + // Start goroutine to wait on container state. + go func() { + defer cli.Close() + + statusCh, errCh := cli.ContainerWait(ctx, containerID, container.WaitConditionNotRunning) + select { + case <-ctx.Done(): + // Can't use command context, since it's already canceled here. + err := cli.ContainerKill(context.Background(), containerID, "") + if err != nil { + logger.Error("docker container kill error", "error", err) + } + case err := <-errCh: + if err != nil { + logger.Error("docker container wait error", "error", err) + } + case resp := <-statusCh: + logger.Info("docker container has self terminated", "status_code", resp.StatusCode) + + rc, err := cli.ContainerLogs(ctx, containerID, dtyp.ContainerLogsOptions{Details: true, ShowStdout: true, ShowStderr: true}) + if err != nil { + logger.Error("docker container logs error", "error", err) + } + defer rc.Close() + var buf bytes.Buffer + stdcopy.StdCopy(&buf, &buf, rc) + logger.Error("container self terminated", "log", buf.String()) + } + }() + + return nil +} diff --git a/sdks/go/pkg/beam/runners/prism/internal/execute.go b/sdks/go/pkg/beam/runners/prism/internal/execute.go index b2f9d866603a..e0c67105d451 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/execute.go +++ b/sdks/go/pkg/beam/runners/prism/internal/execute.go @@ -24,7 +24,6 @@ import ( "github.com/apache/beam/sdks/v2/go/pkg/beam/core/graph/mtime" "github.com/apache/beam/sdks/v2/go/pkg/beam/core/runtime/exec" - fnpb "github.com/apache/beam/sdks/v2/go/pkg/beam/model/fnexecution_v1" pipepb "github.com/apache/beam/sdks/v2/go/pkg/beam/model/pipeline_v1" "github.com/apache/beam/sdks/v2/go/pkg/beam/runners/prism/internal/engine" "github.com/apache/beam/sdks/v2/go/pkg/beam/runners/prism/internal/jobservices" @@ -32,8 +31,6 @@ import ( "github.com/apache/beam/sdks/v2/go/pkg/beam/runners/prism/internal/worker" "golang.org/x/exp/maps" "golang.org/x/exp/slog" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" "google.golang.org/protobuf/proto" ) @@ -54,30 +51,21 @@ func RunPipeline(j *jobservices.Job) { return } env, _ := getOnlyPair(envs) - wk := worker.New(j.String()+"_"+env, env) // Cheating by having the worker id match the environment id. - go wk.Serve() - timeout := time.Minute - time.AfterFunc(timeout, func() { - if wk.Connected() { - return - } - err := fmt.Errorf("prism %v didn't get control connection after %v", wk, timeout) + wk, err := makeWorker(env, j) + if err != nil { j.Failed(err) - j.CancelFn(err) - }) - + return + } // When this function exits, we cancel the context to clear // any related job resources. defer func() { j.CancelFn(fmt.Errorf("runPipeline returned, cleaning up")) }() - go runEnvironment(j.RootCtx, j, env, wk) j.SendMsg("running " + j.String()) j.Running() - err := executePipeline(j.RootCtx, wk, j) - if err != nil { + if err := executePipeline(j.RootCtx, wk, j); err != nil { j.Failed(err) return } @@ -90,57 +78,27 @@ func RunPipeline(j *jobservices.Job) { j.Done() } -// TODO move environment handling to the worker package. - -func runEnvironment(ctx context.Context, j *jobservices.Job, env string, wk *worker.W) { - // TODO fix broken abstraction. - // We're starting a worker pool here, because that's the loopback environment. - // It's sort of a mess, largely because of loopback, which has - // a different flow from a provisioned docker container. - e := j.Pipeline.GetComponents().GetEnvironments()[env] - switch e.GetUrn() { - case urns.EnvExternal: - ep := &pipepb.ExternalPayload{} - if err := (proto.UnmarshalOptions{}).Unmarshal(e.GetPayload(), ep); err != nil { - slog.Error("unmarshing environment payload", err, slog.String("envID", wk.Env)) - } - externalEnvironment(ctx, ep, wk) - slog.Debug("environment stopped", slog.String("envID", wk.String()), slog.String("job", j.String())) - default: - panic(fmt.Sprintf("environment %v with urn %v unimplemented", env, e.GetUrn())) - } -} - -func externalEnvironment(ctx context.Context, ep *pipepb.ExternalPayload, wk *worker.W) { - conn, err := grpc.Dial(ep.GetEndpoint().GetUrl(), grpc.WithTransportCredentials(insecure.NewCredentials())) - if err != nil { - panic(fmt.Sprintf("unable to dial sdk worker %v: %v", ep.GetEndpoint().GetUrl(), err)) - } - defer conn.Close() - pool := fnpb.NewBeamFnExternalWorkerPoolClient(conn) - - endpoint := &pipepb.ApiServiceDescriptor{ - Url: wk.Endpoint(), +// makeWorker creates a worker for that environment. +func makeWorker(env string, j *jobservices.Job) (*worker.W, error) { + wk := worker.New(j.String()+"_"+env, env) + wk.EnvPb = j.Pipeline.GetComponents().GetEnvironments()[env] + wk.JobKey = j.JobKey() + wk.ArtifactEndpoint = j.ArtifactEndpoint() + go wk.Serve() + if err := runEnvironment(j.RootCtx, j, env, wk); err != nil { + return nil, fmt.Errorf("failed to start environment %v for job %v: %w", env, j, err) } - pool.StartWorker(ctx, &fnpb.StartWorkerRequest{ - WorkerId: wk.ID, - ControlEndpoint: endpoint, - LoggingEndpoint: endpoint, - ArtifactEndpoint: endpoint, - ProvisionEndpoint: endpoint, - Params: nil, - }) - - // Job processing happens here, but orchestrated by other goroutines - // This goroutine blocks until the context is cancelled, signalling - // that the pool runner should stop the worker. - <-ctx.Done() - - // Previous context cancelled so we need a new one - // for this request. - pool.StopWorker(context.Background(), &fnpb.StopWorkerRequest{ - WorkerId: wk.ID, + // Check for connection succeeding after we've created the environment successfully. + timeout := 1 * time.Minute + time.AfterFunc(timeout, func() { + if wk.Connected() { + return + } + err := fmt.Errorf("prism %v didn't get control connection to %v after %v", wk, wk.Endpoint(), timeout) + j.Failed(err) + j.CancelFn(err) }) + return wk, nil } type transformExecuter interface { diff --git a/sdks/go/pkg/beam/runners/prism/internal/jobservices/artifact.go b/sdks/go/pkg/beam/runners/prism/internal/jobservices/artifact.go index e66def5b0fe8..99b786d45980 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/jobservices/artifact.go +++ b/sdks/go/pkg/beam/runners/prism/internal/jobservices/artifact.go @@ -16,11 +16,14 @@ package jobservices import ( + "bytes" + "context" "fmt" "io" jobpb "github.com/apache/beam/sdks/v2/go/pkg/beam/model/jobmanagement_v1" "golang.org/x/exp/slog" + "google.golang.org/protobuf/encoding/prototext" ) func (s *Server) ReverseArtifactRetrievalService(stream jobpb.ArtifactStagingService_ReverseArtifactRetrievalServiceServer) error { @@ -47,7 +50,7 @@ func (s *Server) ReverseArtifactRetrievalService(stream jobpb.ArtifactStagingSer }, }, }) - var count int + var buf bytes.Buffer for { in, err := stream.Recv() if err == io.EOF { @@ -56,26 +59,61 @@ func (s *Server) ReverseArtifactRetrievalService(stream jobpb.ArtifactStagingSer if err != nil { return err } - if in.IsLast { - slog.Debug("GetArtifact finish", + if in.GetIsLast() { + slog.Debug("GetArtifact finished", slog.Group("dep", slog.String("urn", dep.GetTypeUrn()), slog.String("payload", string(dep.GetTypePayload()))), - slog.Int("bytesReceived", count)) + slog.Int("bytesReceived", buf.Len()), + slog.String("rtype", fmt.Sprintf("%T", in.GetResponse())), + ) break } // Here's where we go through each environment's artifacts. // We do nothing with them. switch req := in.GetResponse().(type) { case *jobpb.ArtifactResponseWrapper_GetArtifactResponse: - count += len(req.GetArtifactResponse.GetData()) + buf.Write(req.GetArtifactResponse.GetData()) + case *jobpb.ArtifactResponseWrapper_ResolveArtifactResponse: err := fmt.Errorf("unexpected ResolveArtifactResponse to GetArtifact: %v", in.GetResponse()) slog.Error("GetArtifact failure", err) return err } } + if len(s.artifacts) == 0 { + s.artifacts = map[string][]byte{} + } + s.artifacts[string(dep.GetTypePayload())] = buf.Bytes() } } return nil } + +func (s *Server) ResolveArtifacts(_ context.Context, req *jobpb.ResolveArtifactsRequest) (*jobpb.ResolveArtifactsResponse, error) { + return &jobpb.ResolveArtifactsResponse{ + Replacements: req.GetArtifacts(), + }, nil +} + +func (s *Server) GetArtifact(req *jobpb.GetArtifactRequest, stream jobpb.ArtifactRetrievalService_GetArtifactServer) error { + info := req.GetArtifact() + buf, ok := s.artifacts[string(info.GetTypePayload())] + if !ok { + pt := prototext.Format(info) + slog.Warn("unable to provide artifact to worker", "artifact_info", pt) + return fmt.Errorf("unable to provide %v to worker", pt) + } + chunk := 128 * 1024 * 1024 // 128 MB + var i int + for i+chunk < len(buf) { + stream.Send(&jobpb.GetArtifactResponse{ + Data: buf[i : i+chunk], + }) + i += chunk + } + stream.Send(&jobpb.GetArtifactResponse{ + Data: buf[i:], + }) + return nil +} diff --git a/sdks/go/pkg/beam/runners/prism/internal/jobservices/job.go b/sdks/go/pkg/beam/runners/prism/internal/jobservices/job.go index 10d36066391f..87b0ec007bfb 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/jobservices/job.go +++ b/sdks/go/pkg/beam/runners/prism/internal/jobservices/job.go @@ -68,6 +68,8 @@ type Job struct { key string jobName string + artifactEndpoint string + Pipeline *pipepb.Pipeline options *structpb.Struct @@ -88,6 +90,10 @@ type Job struct { metrics metricsStore } +func (j *Job) ArtifactEndpoint() string { + return j.artifactEndpoint +} + // ContributeTentativeMetrics returns the datachannel read index, and any unknown monitoring short ids. func (j *Job) ContributeTentativeMetrics(payloads *fnpb.ProcessBundleProgressResponse) (int64, []string) { return j.metrics.ContributeTentativeMetrics(payloads) @@ -113,6 +119,10 @@ func (j *Job) LogValue() slog.Value { slog.String("name", j.jobName)) } +func (j *Job) JobKey() string { + return j.key +} + func (j *Job) SendMsg(msg string) { j.streamCond.L.Lock() defer j.streamCond.L.Unlock() diff --git a/sdks/go/pkg/beam/runners/prism/internal/jobservices/management.go b/sdks/go/pkg/beam/runners/prism/internal/jobservices/management.go index e626a05b51e1..213e33a78379 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/jobservices/management.go +++ b/sdks/go/pkg/beam/runners/prism/internal/jobservices/management.go @@ -79,6 +79,8 @@ func (s *Server) Prepare(ctx context.Context, req *jobpb.PrepareJobRequest) (*jo streamCond: sync.NewCond(&sync.Mutex{}), RootCtx: rootCtx, CancelFn: cancelFn, + + artifactEndpoint: s.Endpoint(), } // Queue initial state of the job. diff --git a/sdks/go/pkg/beam/runners/prism/internal/jobservices/server.go b/sdks/go/pkg/beam/runners/prism/internal/jobservices/server.go index e3fb7766b519..bf2db814813c 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/jobservices/server.go +++ b/sdks/go/pkg/beam/runners/prism/internal/jobservices/server.go @@ -29,6 +29,7 @@ import ( type Server struct { jobpb.UnimplementedJobServiceServer jobpb.UnimplementedArtifactStagingServiceServer + jobpb.UnimplementedArtifactRetrievalServiceServer fnpb.UnimplementedProvisionServiceServer // Server management @@ -42,6 +43,9 @@ type Server struct { // execute defines how a job is executed. execute func(*Job) + + // Artifact hack + artifacts map[string][]byte } // NewServer acquires the indicated port. @@ -60,6 +64,7 @@ func NewServer(port int, execute func(*Job)) *Server { s.server = grpc.NewServer(opts...) jobpb.RegisterJobServiceServer(s.server, s) jobpb.RegisterArtifactStagingServiceServer(s.server, s) + jobpb.RegisterArtifactRetrievalServiceServer(s.server, s) return s } diff --git a/sdks/go/pkg/beam/runners/prism/internal/worker/worker.go b/sdks/go/pkg/beam/runners/prism/internal/worker/worker.go index 0ad7ccb37032..3a862a143b73 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/worker/worker.go +++ b/sdks/go/pkg/beam/runners/prism/internal/worker/worker.go @@ -57,6 +57,9 @@ type W struct { ID, Env string + JobKey, ArtifactEndpoint string + EnvPb *pipepb.Environment + // Server management lis net.Listener server *grpc.Server @@ -107,6 +110,7 @@ func New(id, env string) *W { fnpb.RegisterBeamFnDataServer(wk.server, wk) fnpb.RegisterBeamFnLoggingServer(wk.server, wk) fnpb.RegisterBeamFnStateServer(wk.server, wk) + fnpb.RegisterProvisionServiceServer(wk.server, wk) return wk } @@ -164,10 +168,15 @@ func (wk *W) GetProvisionInfo(_ context.Context, _ *fnpb.GetProvisionInfoRequest RunnerCapabilities: []string{ urns.CapabilityMonitoringInfoShortIDs, }, - LoggingEndpoint: endpoint, - ControlEndpoint: endpoint, - ArtifactEndpoint: endpoint, - // TODO add this job's RetrievalToken + LoggingEndpoint: endpoint, + ControlEndpoint: endpoint, + ArtifactEndpoint: &pipepb.ApiServiceDescriptor{ + Url: wk.ArtifactEndpoint, + }, + + RetrievalToken: wk.JobKey, + Dependencies: wk.EnvPb.GetDependencies(), + // TODO add this job's artifact Dependencies Metadata: map[string]string{ diff --git a/sdks/go/pkg/beam/runners/universal/runnerlib/stage.go b/sdks/go/pkg/beam/runners/universal/runnerlib/stage.go index 732f4382ab5d..d5cc6aa7327a 100644 --- a/sdks/go/pkg/beam/runners/universal/runnerlib/stage.go +++ b/sdks/go/pkg/beam/runners/universal/runnerlib/stage.go @@ -44,7 +44,7 @@ func Stage(ctx context.Context, id, endpoint, binary, st string) (retrievalToken defer cc.Close() if err := StageViaPortableAPI(ctx, cc, binary, st); err == nil { - return "", nil + return st, nil } log.Warnf(ctx, "unable to stage with PortableAPI: %v; falling back to legacy", err) From 67a9cf378611754ac563a9b18e9b4c6f32469f39 Mon Sep 17 00:00:00 2001 From: Xinyu Liu Date: Fri, 15 Sep 2023 11:07:33 -0700 Subject: [PATCH 019/105] Allow dropLataData in GBK for SamzaRunner (#28461) --- .../runners/samza/SamzaPipelineOptions.java | 6 + .../runners/samza/runtime/GroupByKeyOp.java | 12 +- .../samza/runtime/GroupByKeyOpTest.java | 123 ++++++++++++++++++ 3 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 runners/samza/src/test/java/org/apache/beam/runners/samza/runtime/GroupByKeyOpTest.java diff --git a/runners/samza/src/main/java/org/apache/beam/runners/samza/SamzaPipelineOptions.java b/runners/samza/src/main/java/org/apache/beam/runners/samza/SamzaPipelineOptions.java index af6d890cf51b..a34303d92552 100644 --- a/runners/samza/src/main/java/org/apache/beam/runners/samza/SamzaPipelineOptions.java +++ b/runners/samza/src/main/java/org/apache/beam/runners/samza/SamzaPipelineOptions.java @@ -173,4 +173,10 @@ public ExecutorService create(PipelineOptions options) { new ThreadFactoryBuilder().setNameFormat("Process Element Thread-%d").build()); } } + + @Description("Enable/disable late data dropping in GroupByKey/Combine transforms") + @Default.Boolean(false) + boolean getDropLateData(); + + void setDropLateData(boolean dropLateData); } diff --git a/runners/samza/src/main/java/org/apache/beam/runners/samza/runtime/GroupByKeyOp.java b/runners/samza/src/main/java/org/apache/beam/runners/samza/runtime/GroupByKeyOp.java index 3ecd406da615..1b19275dd967 100644 --- a/runners/samza/src/main/java/org/apache/beam/runners/samza/runtime/GroupByKeyOp.java +++ b/runners/samza/src/main/java/org/apache/beam/runners/samza/runtime/GroupByKeyOp.java @@ -180,11 +180,19 @@ public TimerInternals timerInternals() { DoFnSchemaInformation.create(), Collections.emptyMap()); + final DoFnRunner, KV> dropLateDataRunner = + pipelineOptions.getDropLateData() + ? DoFnRunners.lateDataDroppingRunner( + doFnRunner, keyedInternals.timerInternals(), windowingStrategy) + : doFnRunner; + final SamzaExecutionContext executionContext = (SamzaExecutionContext) context.getApplicationContainerContext(); - this.fnRunner = + final DoFnRunner, KV> doFnRunnerWithMetrics = DoFnRunnerWithMetrics.wrap( - doFnRunner, executionContext.getMetricsContainer(), transformFullName); + dropLateDataRunner, executionContext.getMetricsContainer(), transformFullName); + + this.fnRunner = new DoFnRunnerWithKeyedInternals<>(doFnRunnerWithMetrics, keyedInternals); } @Override diff --git a/runners/samza/src/test/java/org/apache/beam/runners/samza/runtime/GroupByKeyOpTest.java b/runners/samza/src/test/java/org/apache/beam/runners/samza/runtime/GroupByKeyOpTest.java new file mode 100644 index 000000000000..8670d9a46eac --- /dev/null +++ b/runners/samza/src/test/java/org/apache/beam/runners/samza/runtime/GroupByKeyOpTest.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.beam.runners.samza.runtime; + +import java.io.Serializable; +import java.util.Arrays; +import org.apache.beam.sdk.coders.KvCoder; +import org.apache.beam.sdk.coders.StringUtf8Coder; +import org.apache.beam.sdk.coders.VarIntCoder; +import org.apache.beam.sdk.options.PipelineOptionsFactory; +import org.apache.beam.sdk.testing.PAssert; +import org.apache.beam.sdk.testing.TestPipeline; +import org.apache.beam.sdk.testing.TestStream; +import org.apache.beam.sdk.transforms.Combine; +import org.apache.beam.sdk.transforms.Sum; +import org.apache.beam.sdk.transforms.windowing.FixedWindows; +import org.apache.beam.sdk.transforms.windowing.Window; +import org.apache.beam.sdk.values.KV; +import org.apache.beam.sdk.values.PCollection; +import org.apache.beam.sdk.values.TimestampedValue; +import org.joda.time.Duration; +import org.joda.time.Instant; +import org.junit.Rule; +import org.junit.Test; + +/** Tests for GroupByKeyOp. */ +public class GroupByKeyOpTest implements Serializable { + @Rule + public final transient TestPipeline pipeline = + TestPipeline.fromOptions( + PipelineOptionsFactory.fromArgs("--runner=TestSamzaRunner").create()); + + @Rule + public final transient TestPipeline dropLateDataPipeline = + TestPipeline.fromOptions( + PipelineOptionsFactory.fromArgs("--runner=TestSamzaRunner", "--dropLateData=true") + .create()); + + @Test + public void testDefaultGbk() { + TestStream.Builder testStream = + TestStream.create(VarIntCoder.of()) + .addElements(TimestampedValue.of(1, new Instant(1000))) + .addElements(TimestampedValue.of(2, new Instant(2000))) + .advanceWatermarkTo(new Instant(3000)) + .addElements(TimestampedValue.of(10, new Instant(1000))) + .advanceWatermarkTo(new Instant(10000)); + + PCollection aggregated = + pipeline + .apply(testStream.advanceWatermarkToInfinity()) + .apply( + Window.into(FixedWindows.of(Duration.standardSeconds(3))) + .accumulatingFiredPanes()) + .apply(Combine.globally(Sum.ofIntegers()).withoutDefaults()); + + PAssert.that(aggregated).containsInAnyOrder(Arrays.asList(3, 10)); + + pipeline.run().waitUntilFinish(); + } + + @Test + public void testDropLateDataNonKeyed() { + TestStream.Builder testStream = + TestStream.create(VarIntCoder.of()) + .addElements(TimestampedValue.of(1, new Instant(1000))) + .addElements(TimestampedValue.of(2, new Instant(2000))) + .advanceWatermarkTo(new Instant(3000)) + .addElements(TimestampedValue.of(10, new Instant(1000))) + .advanceWatermarkTo(new Instant(10000)); + + PCollection aggregated = + dropLateDataPipeline + .apply(testStream.advanceWatermarkToInfinity()) + .apply( + Window.into(FixedWindows.of(Duration.standardSeconds(3))) + .accumulatingFiredPanes()) + .apply(Combine.globally(Sum.ofIntegers()).withoutDefaults()); + + PAssert.that(aggregated).containsInAnyOrder(3); + + dropLateDataPipeline.run().waitUntilFinish(); + } + + @Test + public void testDropLateDataKeyed() { + TestStream.Builder> testStream = + TestStream.create(KvCoder.of(StringUtf8Coder.of(), VarIntCoder.of())) + .addElements(TimestampedValue.of(KV.of("a", 1), new Instant(1000))) + .addElements(TimestampedValue.of(KV.of("b", 2), new Instant(2000))) + .addElements(TimestampedValue.of(KV.of("a", 3), new Instant(2500))) + .advanceWatermarkTo(new Instant(3000)) + .addElements(TimestampedValue.of(KV.of("a", 10), new Instant(1000))) + .advanceWatermarkTo(new Instant(10000)); + + PCollection> aggregated = + dropLateDataPipeline + .apply(testStream.advanceWatermarkToInfinity()) + .apply( + Window.>into(FixedWindows.of(Duration.standardSeconds(3))) + .accumulatingFiredPanes()) + .apply(Sum.integersPerKey()); + + PAssert.that(aggregated).containsInAnyOrder(Arrays.asList(KV.of("a", 4), KV.of("b", 2))); + + dropLateDataPipeline.run().waitUntilFinish(); + } +} From d1fffb60de67c55aebf189f1299fecef7f90c810 Mon Sep 17 00:00:00 2001 From: Andrey Devyatkin Date: Fri, 15 Sep 2023 20:34:19 +0200 Subject: [PATCH 020/105] added beam_PostCommit_Python_MongoDBIO_IT job to GitHub Actions (#28472) --- .github/workflows/README.md | 1 + .../beam_PostCommit_Python_MongoDBIO_IT.yml | 95 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 .github/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 9a0d6a4799f5..a878acffbf18 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -223,6 +223,7 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | [ PostCommit Python Examples Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml) | ['3.8','3.9','3.10','3.11'] |`Run Python Examples_Direct (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_Examples_Direct.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml) | | [ PostCommit Python Examples Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml) | ['3.8','3.11'] |`Run Python Examples_Flink (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_Examples_Flink.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml) | | [ PostCommit Python Examples Spark ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Spark.yml) | ['3.8','3.11'] |`Run Python Examples_Spark (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_Examples_Spark.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Spark.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Spark.yml) | +| [ PostCommit Python MongoDBIO IT ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml) | N/A |`Run Python MongoDBIO_IT`| [![.github/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml) | | [ PostCommit Python ValidatesContainer Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml) | ['3.8','3.9','3.10','3.11'] |`Run Python Dataflow ValidatesContainer (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml) | | [ PostCommit Python ValidatesContainer Dataflow With RC ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml) | ['3.8','3.9','3.10','3.11'] |`Run Python RC Dataflow ValidatesContainer (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml) | | [ PostCommit Python ValidatesRunner Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml) | ['3.8','3.11'] |`Run Python Dataflow ValidatesRunner (matrix_element)`| [![PostCommit Python ValidatesRunner Dataflow](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml) | diff --git a/.github/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml b/.github/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml new file mode 100644 index 000000000000..9f75d7588fa8 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml @@ -0,0 +1,95 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit Python MongoDBIO IT + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Python_MongoDBIO_IT: + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + if: | + github.event_name == 'schedule' || + github.event_name == 'workflow_dispatch' || + github.event.comment.body == 'Run Python MongoDBIO_IT' + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 100 + strategy: + matrix: + job_name: ["beam_PostCommit_Python_MongoDBIO_IT"] + job_phrase: ["Run Python MongoDBIO_IT"] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + - name: Install Java + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: '8' + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + with: + cache-read-only: false + - name: Run mongodbioIT script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:python:test-suites:direct:py311:mongodbioIT + arguments: | + -PpythonVersion=3.11 \ + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + with: + name: python-code-coverage-report + path: "**/pytest*.xml" \ No newline at end of file From d32ddc6f19d6eafd8b1a69ba866905575e1ac349 Mon Sep 17 00:00:00 2001 From: Andrey Devyatkin Date: Fri, 15 Sep 2023 20:35:26 +0200 Subject: [PATCH 021/105] added beam_PostCommit_Java_Hadoop_Versions job to GitHub Actions (#28473) --- .github/workflows/README.md | 1 + .../beam_PostCommit_Java_Hadoop_Versions.yml | 91 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 .github/workflows/beam_PostCommit_Java_Hadoop_Versions.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index a878acffbf18..25b5a9f2da12 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -195,6 +195,7 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | [ PostCommit Java Examples Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Direct.yml) | N/A |`Run Java Examples_Direct`| [![.github/workflows/beam_PostCommit_Java_Examples_Direct.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Direct.yml) | | [ PostCommit Java Examples Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Flink.yml) | N/A |`Run Java Examples_Flink`| [![.github/workflows/beam_PostCommit_Java_Examples_Flink.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Flink.yml) | | [ PostCommit Java Examples Spark ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Spark.yml) | N/A |`Run Java Examples_Spark`| [![.github/workflows/beam_PostCommit_Java_Examples_Spark.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Spark.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Examples_Spark.yml) | +| [ PostCommit Java Hadoop Versions ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Hadoop_Versions.yml) | N/A |`Run PostCommit_Java_Hadoop_Versions`| [![.github/workflows/beam_PostCommit_Java_Hadoop_Versions.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Hadoop_Versions.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Hadoop_Versions.yml) | | [ PostCommit Java Jpms Dataflow Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11.yml) | N/A |`Run Jpms Dataflow Java 11 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11.yml) | | [ PostCommit Java Jpms Dataflow Java17 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17.yml) | N/A |`Run Jpms Dataflow Java 17 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17.yml) | | [ PostCommit Java Jpms Direct Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java11.yml) | N/A |`Run Jpms Direct Java 11 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java11](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java11.yml) | diff --git a/.github/workflows/beam_PostCommit_Java_Hadoop_Versions.yml b/.github/workflows/beam_PostCommit_Java_Hadoop_Versions.yml new file mode 100644 index 000000000000..cd281c77a7e1 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_Hadoop_Versions.yml @@ -0,0 +1,91 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit Java Hadoop Versions + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_Hadoop_Versions: + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 240 + strategy: + matrix: + job_name: [beam_PostCommit_Java_Hadoop_Versions] + job_phrase: [Run PostCommit_Java_Hadoop_Versions] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run PostCommit_Java_Hadoop_Versions' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Java + uses: actions/setup-java@v3.8.0 + with: + distribution: 'zulu' + java-version: '8' + - name: run validatesRunner script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :javaHadoopVersionsTest + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file From 38720a39d91db1853741f86d29819bf5a5d59a36 Mon Sep 17 00:00:00 2001 From: magicgoody <131876064+magicgoody@users.noreply.github.com> Date: Sat, 16 Sep 2023 00:53:08 +0600 Subject: [PATCH 022/105] Github Workflow Replacement for Jenkins Job, beam_PostCommit_Website_Test (#28368) * PostCommit Website Test * readme * timeout-minutes * concurrency update --- .github/workflows/README.md | 1 + .../beam_PostCommit_Website_Test.yml | 76 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 .github/workflows/beam_PostCommit_Website_Test.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 25b5a9f2da12..c345dcc6d549 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -202,6 +202,7 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | [ PostCommit Java Jpms Direct Java17 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml) | N/A |`Run Jpms Direct Java 17 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java17](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml) | | [ PostCommit Java Jpms Flink Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml) | N/A |`Run Jpms Flink Java 11 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Flink_Java11](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml) | | [ PostCommit Java Jpms Spark Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml) | N/A |`Run Jpms Spark Java 11 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Spark_Java11](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml) | +| [ PostCommit Website Test](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Test.yml) | N/A |`Run Full Website Test`| [![.github/workflows/beam_PostCommit_Website_Test](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Test.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Test.yml) | | [ PostCommit Java Sickbay ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Sickbay.yml) | N/A |`Run Java Sickbay`| [![.github/workflows/beam_PostCommit_Java_Sickbay.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Sickbay.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Sickbay.yml) | | [ PostCommit Java ValidatesRunner Dataflow JavaVersions ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Java.yml) | ['11','17'] |`Run Dataflow ValidatesRunner Java (matrix_element)`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Java.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Java.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Java.yml) | | [ PostCommit Java ValidatesRunner Dataflow Streaming ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml) | N/A |`Run Dataflow Streaming ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml) | diff --git a/.github/workflows/beam_PostCommit_Website_Test.yml b/.github/workflows/beam_PostCommit_Website_Test.yml new file mode 100644 index 000000000000..a411953e01f9 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Website_Test.yml @@ -0,0 +1,76 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PostCommit Website Test + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Website_Test: + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Full Website Test' + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 30 + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + strategy: + matrix: + job_name: ["beam_PostCommit_Website_Test"] + job_phrase: ["Run Full Website Test"] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: run PostCommit Website Test script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :website:testWebsite + arguments: -PdisableExternal=false \ No newline at end of file From 1f980eaa894cc43ea5ca1aeb4cb2ef1de1162b17 Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Fri, 15 Sep 2023 12:16:53 -0700 Subject: [PATCH 023/105] Remove implicitly defined IOs. (#28479) We should be explicit about which IOs we support and their signatures. --- sdks/python/apache_beam/yaml/yaml_io.py | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/sdks/python/apache_beam/yaml/yaml_io.py b/sdks/python/apache_beam/yaml/yaml_io.py index 646d5e1fbff1..2a9d1be62c6d 100644 --- a/sdks/python/apache_beam/yaml/yaml_io.py +++ b/sdks/python/apache_beam/yaml/yaml_io.py @@ -99,18 +99,4 @@ def raise_exception(failed_row_with_error): def io_providers(): with open(os.path.join(os.path.dirname(__file__), 'standard_io.yaml')) as fin: - explicit_ios = yaml_provider.parse_providers( - yaml.load(fin, Loader=yaml.SafeLoader)) - - # TOOD(yaml): We should make all top-level IOs explicit. - # This will be a chance to clean up the APIs and align them with their - # Java implementations. - # PythonTransform can be used to get the "raw" transforms for any others. - implicit_ios = yaml_provider.InlineProvider({ - key: getattr(beam.io, key) - for key in dir(beam.io) - if (key.startswith('ReadFrom') or key.startswith('WriteTo')) and - key not in explicit_ios - }) - - return yaml_provider.merge_providers(explicit_ios, implicit_ios) + return yaml_provider.parse_providers(yaml.load(fin, Loader=yaml.SafeLoader)) From 6870283a56cd1dcbe1975459cfe22e34f0df83dc Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Fri, 15 Sep 2023 13:12:53 -0700 Subject: [PATCH 024/105] [BEAM-28399] Ensure dataflow experiments are always set when dataflow runner is used. --- sdks/python/apache_beam/runners/dataflow/dataflow_runner.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdks/python/apache_beam/runners/dataflow/dataflow_runner.py b/sdks/python/apache_beam/runners/dataflow/dataflow_runner.py index 11e2e1218659..3d612bd6ec0f 100644 --- a/sdks/python/apache_beam/runners/dataflow/dataflow_runner.py +++ b/sdks/python/apache_beam/runners/dataflow/dataflow_runner.py @@ -359,6 +359,8 @@ def run_pipeline(self, pipeline, options, pipeline_proto=None): 'Google Cloud Dataflow runner not available, ' 'please install apache_beam[gcp]') + _check_and_add_missing_options(options) + # Convert all side inputs into a form acceptable to Dataflow. if pipeline: pipeline.visit(self.combinefn_visitor()) From 841d7cd3a71fcea838a8425c906a99cacd7c958b Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Fri, 15 Sep 2023 17:03:45 -0400 Subject: [PATCH 025/105] Update dataflow container version (#28484) --- sdks/python/apache_beam/runners/dataflow/internal/names.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdks/python/apache_beam/runners/dataflow/internal/names.py b/sdks/python/apache_beam/runners/dataflow/internal/names.py index 8bb39940e484..57c0bcdff201 100644 --- a/sdks/python/apache_beam/runners/dataflow/internal/names.py +++ b/sdks/python/apache_beam/runners/dataflow/internal/names.py @@ -34,6 +34,6 @@ # Unreleased sdks use container image tag specified below. # Update this tag whenever there is a change that # requires changes to SDK harness container or SDK harness launcher. -BEAM_DEV_SDK_CONTAINER_TAG = 'beam-master-20230912' +BEAM_DEV_SDK_CONTAINER_TAG = 'beam-master-20230915' DATAFLOW_CONTAINER_IMAGE_REPOSITORY = 'gcr.io/cloud-dataflow/v1beta3' From e7e140ec2169d9908472e551e33f5c9c86bac7dc Mon Sep 17 00:00:00 2001 From: Joey Tran Date: Fri, 15 Sep 2023 19:44:59 -0400 Subject: [PATCH 026/105] Implement unittest --- .../portability/fn_api_runner/translations.py | 1 + .../fn_api_runner/translations_test.py | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/sdks/python/apache_beam/runners/portability/fn_api_runner/translations.py b/sdks/python/apache_beam/runners/portability/fn_api_runner/translations.py index 13f1dc635965..1f4d78581d7c 100644 --- a/sdks/python/apache_beam/runners/portability/fn_api_runner/translations.py +++ b/sdks/python/apache_beam/runners/portability/fn_api_runner/translations.py @@ -1367,6 +1367,7 @@ def lifted_stages(stage): payload=transform.spec.payload), inputs={'in': grouped_pcoll_id}, outputs={'out': merged_pcoll_id}, + annotations=transform.annotations, environment_id=transform.environment_id)) yield make_stage( diff --git a/sdks/python/apache_beam/runners/portability/fn_api_runner/translations_test.py b/sdks/python/apache_beam/runners/portability/fn_api_runner/translations_test.py index 144f067900f3..d52ec65afc52 100644 --- a/sdks/python/apache_beam/runners/portability/fn_api_runner/translations_test.py +++ b/sdks/python/apache_beam/runners/portability/fn_api_runner/translations_test.py @@ -384,6 +384,35 @@ def expand(self, pcoll): 'multiple-small-combines/min-4-globally/CombinePerKey', optimized_stage_names) + def test_combineperkey_annotation_propagation(self): + """ + Test that the CPK component transforms inherit annotations from the + source CPK + """ + class MyCombinePerKey(beam.CombinePerKey): + def annotations(self): + return {"my_annotation":b""} + # Verify the results are as expected. + with TestPipeline() as pipeline: + pipeline | beam.Create([(1, 2)]) | MyCombinePerKey(min) + + # Verify the optimization is as expected. + proto = pipeline.to_runner_api( + default_environment=environments.EmbeddedPythonEnvironment( + capabilities=environments.python_sdk_capabilities())) + optimized = translations.optimize_pipeline( + proto, + phases=[translations.lift_combiners], + known_runner_urns=frozenset(), + partial=True) + for transform_id in [ + 'MyCombinePerKey(min)/Precombine', + 'MyCombinePerKey(min)/Group', + 'MyCombinePerKey(min)/Merge', + 'MyCombinePerKey(min)/ExtractOutputs']: + assert ("my_annotation" in + optimized.components.transforms[transform_id].annotations) + def test_conditionally_packed_combiners(self): class RecursiveCombine(beam.PTransform): def __init__(self, labels): From f37d86014ae0e6ec0b671bad5c62e29c611348d7 Mon Sep 17 00:00:00 2001 From: Joey Tran Date: Fri, 15 Sep 2023 19:46:25 -0400 Subject: [PATCH 027/105] Implement fix --- .../runners/portability/fn_api_runner/translations.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sdks/python/apache_beam/runners/portability/fn_api_runner/translations.py b/sdks/python/apache_beam/runners/portability/fn_api_runner/translations.py index 1f4d78581d7c..cc1494fc7ae2 100644 --- a/sdks/python/apache_beam/runners/portability/fn_api_runner/translations.py +++ b/sdks/python/apache_beam/runners/portability/fn_api_runner/translations.py @@ -1346,6 +1346,7 @@ def lifted_stages(stage): payload=transform.spec.payload), inputs=transform.inputs, outputs={'out': precombined_pcoll_id}, + annotations=transform.annotations, environment_id=transform.environment_id)) yield make_stage( @@ -1355,6 +1356,7 @@ def lifted_stages(stage): spec=beam_runner_api_pb2.FunctionSpec( urn=common_urns.primitives.GROUP_BY_KEY.urn), inputs={'in': precombined_pcoll_id}, + annotations=transform.annotations, outputs={'out': grouped_pcoll_id})) yield make_stage( @@ -1380,6 +1382,7 @@ def lifted_stages(stage): payload=transform.spec.payload), inputs={'in': merged_pcoll_id}, outputs=transform.outputs, + annotations=transform.annotations, environment_id=transform.environment_id)) def unlifted_stages(stage): From 4a5a0076f35a0ff46a7df7de2e311e0c43014598 Mon Sep 17 00:00:00 2001 From: Hai Joey Tran Date: Fri, 15 Sep 2023 19:55:42 -0400 Subject: [PATCH 028/105] Update translations_test.py --- .../runners/portability/fn_api_runner/translations_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdks/python/apache_beam/runners/portability/fn_api_runner/translations_test.py b/sdks/python/apache_beam/runners/portability/fn_api_runner/translations_test.py index d52ec65afc52..b06ebcc4b2f4 100644 --- a/sdks/python/apache_beam/runners/portability/fn_api_runner/translations_test.py +++ b/sdks/python/apache_beam/runners/portability/fn_api_runner/translations_test.py @@ -392,11 +392,11 @@ def test_combineperkey_annotation_propagation(self): class MyCombinePerKey(beam.CombinePerKey): def annotations(self): return {"my_annotation":b""} - # Verify the results are as expected. with TestPipeline() as pipeline: pipeline | beam.Create([(1, 2)]) | MyCombinePerKey(min) - # Verify the optimization is as expected. + # Verify the annotations are propagated to the split up + # CPK transforms proto = pipeline.to_runner_api( default_environment=environments.EmbeddedPythonEnvironment( capabilities=environments.python_sdk_capabilities())) From b3a8e1531cabf42beab6fda79bce533d4b4ddfd5 Mon Sep 17 00:00:00 2001 From: Joey Tran Date: Fri, 15 Sep 2023 22:37:36 -0400 Subject: [PATCH 029/105] yapf --- .../fn_api_runner/translations_test.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/sdks/python/apache_beam/runners/portability/fn_api_runner/translations_test.py b/sdks/python/apache_beam/runners/portability/fn_api_runner/translations_test.py index b06ebcc4b2f4..3ff2421e6265 100644 --- a/sdks/python/apache_beam/runners/portability/fn_api_runner/translations_test.py +++ b/sdks/python/apache_beam/runners/portability/fn_api_runner/translations_test.py @@ -391,9 +391,10 @@ def test_combineperkey_annotation_propagation(self): """ class MyCombinePerKey(beam.CombinePerKey): def annotations(self): - return {"my_annotation":b""} + return {"my_annotation": b""} + with TestPipeline() as pipeline: - pipeline | beam.Create([(1, 2)]) | MyCombinePerKey(min) + _ = pipeline | beam.Create([(1, 2)]) | MyCombinePerKey(min) # Verify the annotations are propagated to the split up # CPK transforms @@ -405,13 +406,13 @@ def annotations(self): phases=[translations.lift_combiners], known_runner_urns=frozenset(), partial=True) - for transform_id in [ - 'MyCombinePerKey(min)/Precombine', - 'MyCombinePerKey(min)/Group', - 'MyCombinePerKey(min)/Merge', - 'MyCombinePerKey(min)/ExtractOutputs']: - assert ("my_annotation" in - optimized.components.transforms[transform_id].annotations) + for transform_id in ['MyCombinePerKey(min)/Precombine', + 'MyCombinePerKey(min)/Group', + 'MyCombinePerKey(min)/Merge', + 'MyCombinePerKey(min)/ExtractOutputs']: + assert ( + "my_annotation" in + optimized.components.transforms[transform_id].annotations) def test_conditionally_packed_combiners(self): class RecursiveCombine(beam.PTransform): From db01dbe5f3c6fb79a23d4ec330d6d0eeeb318016 Mon Sep 17 00:00:00 2001 From: Joey Tran Date: Sun, 17 Sep 2023 07:13:08 -0400 Subject: [PATCH 030/105] create run_portable_pipeline test --- sdks/python/apache_beam/runners/render_test.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sdks/python/apache_beam/runners/render_test.py b/sdks/python/apache_beam/runners/render_test.py index 4dca2b8b5221..b763a311ca1b 100644 --- a/sdks/python/apache_beam/runners/render_test.py +++ b/sdks/python/apache_beam/runners/render_test.py @@ -39,6 +39,18 @@ def test_basic_graph(self): self.assertIn('CustomName', dot) self.assertEqual(dot.count('->'), 2) + def test_run_portable_pipeline(self): + p = beam.Pipeline() + _ = ( + p | beam.Impulse() | beam.Map(lambda _: 2) + | 'CustomName' >> beam.Map(lambda x: x * x)) + pipeline_proto = p.to_runner_api() + dot = render.RenderRunner().run_portable_pipeline( + pipeline_proto, default_options) + self.assertIn('digraph', dot) + self.assertIn('CustomName', dot) + self.assertEqual(dot.count('->'), 2) + def test_side_input(self): p = beam.Pipeline() pcoll = p | beam.Impulse() | beam.FlatMap(lambda x: [1, 2, 3]) From 7642b60385ca4dccc76dd457339d620c9a817f10 Mon Sep 17 00:00:00 2001 From: Joey Tran Date: Sun, 17 Sep 2023 07:35:36 -0400 Subject: [PATCH 031/105] implement change - required seemingly unrelated changes as well --- sdks/python/apache_beam/runners/render.py | 23 ++++++++++--------- .../python/apache_beam/runners/render_test.py | 11 +++++---- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/sdks/python/apache_beam/runners/render.py b/sdks/python/apache_beam/runners/render.py index 306bf8c2090b..26cb922c12cd 100644 --- a/sdks/python/apache_beam/runners/render.py +++ b/sdks/python/apache_beam/runners/render.py @@ -404,15 +404,17 @@ class RenderRunner(runner.PipelineRunner): # (such as counters, stage completion status, or possibly even PCollection # samples) queryable and/or displayed. This could evolve into a full Beam # UI. - def run_pipeline(self, pipeline_object, options, pipeline_proto=None): - if not pipeline_proto: - pipeline_proto = pipeline_object.to_runner_api() - render_options = options.view_as(RenderOptions) + def run_pipeline(self, pipeline_object, options): + return self.run_portable_pipeline(pipeline_object.to_runner_api(), options) + + def run_portable_pipeline(self, pipeline_proto, options): + #render_options = options.view_as(RenderOptions) + render_options = options if render_options.log_proto: logging.info(pipeline_proto) renderer = PipelineRenderer(pipeline_proto, render_options) try: - subprocess.run(['dotX', '-V'], capture_output=True, check=True) + subprocess.run(['dot', '-V'], capture_output=True, check=True) except FileNotFoundError as exn: # If dot is not available, we can at least output the raw .dot files. dot_files = [ @@ -543,17 +545,16 @@ def render_one(options): pipeline_proto = beam_runner_api_pb2.Pipeline() pipeline_proto.ParseFromString(content) - RenderRunner().run_pipeline( - None, pipeline_options.PipelineOptions(**vars(options)), pipeline_proto) + RenderRunner().run_portable_pipeline( + pipeline_proto, pipeline_options.PipelineOptions(**vars(options))) def run_server(options): class RenderBeamJob(local_job_service.BeamJob): def _invoke_runner(self): - return RenderRunner().run_pipeline( - None, - pipeline_options.PipelineOptions(**vars(options)), - self._pipeline_proto) + return RenderRunner().run_portable_pipeline( + self._pipeline_proto, + pipeline_options.PipelineOptions(**vars(options))) with tempfile.TemporaryDirectory() as staging_dir: job_servicer = local_job_service.LocalJobServicer( diff --git a/sdks/python/apache_beam/runners/render_test.py b/sdks/python/apache_beam/runners/render_test.py index b763a311ca1b..6b672d96b50e 100644 --- a/sdks/python/apache_beam/runners/render_test.py +++ b/sdks/python/apache_beam/runners/render_test.py @@ -16,6 +16,7 @@ # # pytype: skip-file +import os import argparse import logging import subprocess @@ -45,11 +46,11 @@ def test_run_portable_pipeline(self): p | beam.Impulse() | beam.Map(lambda _: 2) | 'CustomName' >> beam.Map(lambda x: x * x)) pipeline_proto = p.to_runner_api() - dot = render.RenderRunner().run_portable_pipeline( - pipeline_proto, default_options) - self.assertIn('digraph', dot) - self.assertIn('CustomName', dot) - self.assertEqual(dot.count('->'), 2) + render.RenderRunner().run_portable_pipeline( + pipeline_proto, + render.RenderOptions( + render_output=["my_output.svg"], render_testing=True)) + assert os.path.exists("my_output.svg") def test_side_input(self): p = beam.Pipeline() From 718fb77722b8eee28f1a9fd55b7be84d405027aa Mon Sep 17 00:00:00 2001 From: Joey Tran Date: Sun, 17 Sep 2023 07:57:25 -0400 Subject: [PATCH 032/105] Move confib validation to runner execution time --- sdks/python/apache_beam/runners/render.py | 12 ++++-------- .../python/apache_beam/runners/render_test.py | 19 ++++++++++++++----- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/sdks/python/apache_beam/runners/render.py b/sdks/python/apache_beam/runners/render.py index 26cb922c12cd..7c72d9155feb 100644 --- a/sdks/python/apache_beam/runners/render.py +++ b/sdks/python/apache_beam/runners/render.py @@ -129,12 +129,6 @@ def _add_argparse_args(cls, parser): help='Set to also log input pipeline proto to stdout.') return parser - def __init__(self, *args, render_testing=False, **kwargs): - super().__init__(*args, **kwargs) - if self.render_port < 0 and not self.render_output and not render_testing: - raise ValueError( - 'At least one of --render_port or --render_output must be provided.') - class PipelineRenderer: def __init__(self, pipeline, options): @@ -408,8 +402,10 @@ def run_pipeline(self, pipeline_object, options): return self.run_portable_pipeline(pipeline_object.to_runner_api(), options) def run_portable_pipeline(self, pipeline_proto, options): - #render_options = options.view_as(RenderOptions) - render_options = options + render_options = options.view_as(RenderOptions) + if render_options.render_port < 0 and not render_options.render_output: + raise ValueError( + 'At least one of --render_port or --render_output must be provided.') if render_options.log_proto: logging.info(pipeline_proto) renderer = PipelineRenderer(pipeline_proto, render_options) diff --git a/sdks/python/apache_beam/runners/render_test.py b/sdks/python/apache_beam/runners/render_test.py index 6b672d96b50e..37d56eecc05a 100644 --- a/sdks/python/apache_beam/runners/render_test.py +++ b/sdks/python/apache_beam/runners/render_test.py @@ -21,6 +21,7 @@ import logging import subprocess import unittest +import pytest import apache_beam as beam from apache_beam.runners import render @@ -47,11 +48,19 @@ def test_run_portable_pipeline(self): | 'CustomName' >> beam.Map(lambda x: x * x)) pipeline_proto = p.to_runner_api() render.RenderRunner().run_portable_pipeline( - pipeline_proto, - render.RenderOptions( - render_output=["my_output.svg"], render_testing=True)) + pipeline_proto, render.RenderOptions(render_output=["my_output.svg"])) assert os.path.exists("my_output.svg") + def test_render_config_validation(self): + p = beam.Pipeline() + _ = ( + p | beam.Impulse() | beam.Map(lambda _: 2) + | 'CustomName' >> beam.Map(lambda x: x * x)) + pipeline_proto = p.to_runner_api() + with pytest.raises(ValueError): + render.RenderRunner().run_portable_pipeline( + pipeline_proto, render.RenderOptions()) + def test_side_input(self): p = beam.Pipeline() pcoll = p | beam.Impulse() | beam.FlatMap(lambda x: [1, 2, 3]) @@ -105,8 +114,8 @@ def test_leaf_composite_filter(self): _ = p | beam.Create([1, 2, 3]) | beam.Map(lambda x: x * x) dot = render.PipelineRenderer( p.to_runner_api(), - render.RenderOptions(['--render_leaf_composite_nodes=Create'], - render_testing=True)).to_dot() + render.RenderOptions(['--render_leaf_composite_nodes=Create' + ])).to_dot() self.assertEqual(dot.count('->'), 1) From ee603b7d27977736c0e10d77e65edd9058a08610 Mon Sep 17 00:00:00 2001 From: Joey Tran Date: Sun, 17 Sep 2023 08:12:40 -0400 Subject: [PATCH 033/105] Move dot requiring tests out into separate testcase --- .../python/apache_beam/runners/render_test.py | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/sdks/python/apache_beam/runners/render_test.py b/sdks/python/apache_beam/runners/render_test.py index 37d56eecc05a..b17be0a0e6d5 100644 --- a/sdks/python/apache_beam/runners/render_test.py +++ b/sdks/python/apache_beam/runners/render_test.py @@ -41,16 +41,6 @@ def test_basic_graph(self): self.assertIn('CustomName', dot) self.assertEqual(dot.count('->'), 2) - def test_run_portable_pipeline(self): - p = beam.Pipeline() - _ = ( - p | beam.Impulse() | beam.Map(lambda _: 2) - | 'CustomName' >> beam.Map(lambda x: x * x)) - pipeline_proto = p.to_runner_api() - render.RenderRunner().run_portable_pipeline( - pipeline_proto, render.RenderOptions(render_output=["my_output.svg"])) - assert os.path.exists("my_output.svg") - def test_render_config_validation(self): p = beam.Pipeline() _ = ( @@ -87,11 +77,32 @@ def test_composite_collapse(self): renderer.update(toggle=[create_transform_id]) self.assertEqual(renderer.to_dot().count('->'), 1) - def test_dot_well_formed(self): + +class DotRequiringRenderingTest(unittest.TestCase): + @classmethod + def setUpClass(cls): try: subprocess.run(['dot', '-V'], capture_output=True, check=True) except FileNotFoundError: + cls._dot_installed = False + else: + cls._dot_installed = True + + def setUp(self) -> None: + if not self._dot_installed: self.skipTest('dot executable not installed') + + def test_run_portable_pipeline(self): + p = beam.Pipeline() + _ = ( + p | beam.Impulse() | beam.Map(lambda _: 2) + | 'CustomName' >> beam.Map(lambda x: x * x)) + pipeline_proto = p.to_runner_api() + render.RenderRunner().run_portable_pipeline( + pipeline_proto, render.RenderOptions(render_output=["my_output.svg"])) + assert os.path.exists("my_output.svg") + + def test_dot_well_formed(self): p = beam.Pipeline() _ = p | beam.Create([1, 2, 3]) | beam.Map(lambda x: x * x) pipeline_proto = p.to_runner_api() @@ -106,10 +117,6 @@ def test_dot_well_formed(self): renderer.render_data() def test_leaf_composite_filter(self): - try: - subprocess.run(['dot', '-V'], capture_output=True, check=True) - except FileNotFoundError: - self.skipTest('dot executable not installed') p = beam.Pipeline() _ = p | beam.Create([1, 2, 3]) | beam.Map(lambda x: x * x) dot = render.PipelineRenderer( From e42f4fb67081936b030860167f75836cef2b78e8 Mon Sep 17 00:00:00 2001 From: Joey Tran Date: Sun, 17 Sep 2023 08:14:13 -0400 Subject: [PATCH 034/105] Use module-specific logger --- sdks/python/apache_beam/runners/render.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/sdks/python/apache_beam/runners/render.py b/sdks/python/apache_beam/runners/render.py index 7c72d9155feb..da153d25a4bd 100644 --- a/sdks/python/apache_beam/runners/render.py +++ b/sdks/python/apache_beam/runners/render.py @@ -78,6 +78,8 @@ except ImportError: gcsio = None # type: ignore +_LOGGER = logging.getLogger(__name__) + # From the Beam site, circa November 2022. DEFAULT_EDGE_STYLE = 'color="#ff570b"' DEFAULT_TRANSFORM_STYLE = ( @@ -336,7 +338,7 @@ def page_callback_data(self, layout): } def render_data(self): - logging.info("Re-rendering pipeline...") + _LOGGER.info("Re-rendering pipeline...") layout = self.layout_dot() if self.options.render_output: for path in self.options.render_output: @@ -346,10 +348,10 @@ def render_data(self): input=layout, check=False) if result.returncode: - logging.error( + _LOGGER.error( "Failed render pipeline as %r: exit %s", path, result.returncode) else: - logging.info("Rendered pipeline as %r", path) + _LOGGER.info("Rendered pipeline as %r", path) return self.page_callback_data(layout) def render_json(self): @@ -407,7 +409,7 @@ def run_portable_pipeline(self, pipeline_proto, options): raise ValueError( 'At least one of --render_port or --render_output must be provided.') if render_options.log_proto: - logging.info(pipeline_proto) + _LOGGER.info(pipeline_proto) renderer = PipelineRenderer(pipeline_proto, render_options) try: subprocess.run(['dot', '-V'], capture_output=True, check=True) @@ -420,7 +422,7 @@ def run_portable_pipeline(self, pipeline_proto, options): for output in dot_files: with open(output, 'w') as fout: fout.write(renderer.to_dot()) - logging.info("Wrote pipeline as %s", output) + _LOGGER.info("Wrote pipeline as %s", output) non_dot_files = set(render_options.render_output) - set(dot_files) if non_dot_files: From 36038fcad4c980aae2931f02639c8fafed606f9f Mon Sep 17 00:00:00 2001 From: Joey Tran Date: Sun, 17 Sep 2023 10:23:10 -0400 Subject: [PATCH 035/105] use tempdir for unit test --- sdks/python/apache_beam/runners/render_test.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/sdks/python/apache_beam/runners/render_test.py b/sdks/python/apache_beam/runners/render_test.py index b17be0a0e6d5..5bfe97c61184 100644 --- a/sdks/python/apache_beam/runners/render_test.py +++ b/sdks/python/apache_beam/runners/render_test.py @@ -21,6 +21,7 @@ import logging import subprocess import unittest +import tempfile import pytest import apache_beam as beam @@ -98,9 +99,16 @@ def test_run_portable_pipeline(self): p | beam.Impulse() | beam.Map(lambda _: 2) | 'CustomName' >> beam.Map(lambda x: x * x)) pipeline_proto = p.to_runner_api() - render.RenderRunner().run_portable_pipeline( - pipeline_proto, render.RenderOptions(render_output=["my_output.svg"])) - assert os.path.exists("my_output.svg") + + # In tmpdir: + # - Create a file called "my_output.svg" + # - Run the pipeline + # - Assert that "my_output.svg" exists + with tempfile.TemporaryDirectory() as tmpdir: + svg_path = os.path.join(tmpdir, "my_output.svg") + render.RenderRunner().run_portable_pipeline( + pipeline_proto, render.RenderOptions(render_output=[svg_path])) + assert os.path.exists(svg_path) def test_dot_well_formed(self): p = beam.Pipeline() From 7f709e712c321b09861de02aaddf6753e982276a Mon Sep 17 00:00:00 2001 From: Joey Tran Date: Mon, 18 Sep 2023 07:09:46 -0400 Subject: [PATCH 036/105] remove unuseful test comments --- sdks/python/apache_beam/runners/render_test.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sdks/python/apache_beam/runners/render_test.py b/sdks/python/apache_beam/runners/render_test.py index 5bfe97c61184..27961e34ed5f 100644 --- a/sdks/python/apache_beam/runners/render_test.py +++ b/sdks/python/apache_beam/runners/render_test.py @@ -100,10 +100,6 @@ def test_run_portable_pipeline(self): | 'CustomName' >> beam.Map(lambda x: x * x)) pipeline_proto = p.to_runner_api() - # In tmpdir: - # - Create a file called "my_output.svg" - # - Run the pipeline - # - Assert that "my_output.svg" exists with tempfile.TemporaryDirectory() as tmpdir: svg_path = os.path.join(tmpdir, "my_output.svg") render.RenderRunner().run_portable_pipeline( From 011efacee3f80269d368ed8d88bf1d2203b12136 Mon Sep 17 00:00:00 2001 From: Aleksandr Dudko <116064902+aleksandr-dudko@users.noreply.github.com> Date: Mon, 18 Sep 2023 17:56:04 +0400 Subject: [PATCH 037/105] Add GitHub Workflow Replacement for Jenkins job_PostCommit_SQL (#28469) --- .github/workflows/README.md | 1 + .github/workflows/beam_PostCommit_SQL.yml | 88 +++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 .github/workflows/beam_PostCommit_SQL.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index c345dcc6d549..9444ba6bcbe9 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -236,6 +236,7 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | [ PostCommit Python Xlang Gcp Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Xlang_Gcp_Direct.yml) | N/A |`Run Python_Xlang_Gcp_Direct PostCommit`| [![.github/workflows/beam_PostCommit_Python_Xlang_Gcp_Direct.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Xlang_Gcp_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Xlang_Gcp_Direct.yml) | | [ PostCommit Python Xlang IO Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Xlang_IO_Dataflow.yml) | N/A |`Run Python_Xlang_IO_Dataflow PostCommit`| [![.github/workflows/beam_PostCommit_Python_Xlang_IO_Dataflow.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Xlang_IO_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Xlang_IO_Dataflow.yml) | | [ PostCommit Sickbay Python ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Sickbay_Python.yml) | ['3.8','3.9','3.10','3.11'] |`Run Python (matrix_element) PostCommit Sickbay`| [![.github/workflows/beam_PostCommit_Sickbay_Python.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Sickbay_Python.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Sickbay_Python.yml) | +| [ PostCommit SQL ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_SQL.yml) | N/A |`Run SQL PostCommit`| [![.github/workflows/beam_PostCommit_SQL.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_SQL.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_SQL.yml) | | [ PostCommit TransformService Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_TransformService_Direct.yml) | N/A |`Run TransformService_Direct PostCommit`| [![.github/workflows/beam_PostCommit_TransformService_Direct.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_TransformService_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_TransformService_Direct.yml) | [ PostCommit Website Publish ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml) | N/A | N/A | [![.github/workflows/beam_PostCommit_Website_Publish.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml) | | [ PostCommit XVR GoUsingJava Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml) | N/A |`Run XVR_GoUsingJava_Dataflow PostCommit`| [![.github/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml) | diff --git a/.github/workflows/beam_PostCommit_SQL.yml b/.github/workflows/beam_PostCommit_SQL.yml new file mode 100644 index 000000000000..f85f98d0d0d5 --- /dev/null +++ b/.github/workflows/beam_PostCommit_SQL.yml @@ -0,0 +1,88 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit SQL + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_SQL: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 240 + strategy: + matrix: + job_name: [beam_PostCommit_SQL] + job_phrase: [Run SQL PostCommit] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run SQL PostCommit' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: run PostCommit SQL script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sqlPostCommit + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' From f087afad003a058efc13cc95064b7a1a588f08ae Mon Sep 17 00:00:00 2001 From: Robert Burke Date: Mon, 18 Sep 2023 07:49:45 -0700 Subject: [PATCH 038/105] Remove extra registrations for createFn. (#28488) * Remove extra registrations for createFn. * go fmt --------- Co-authored-by: lostluck <13907733+lostluck@users.noreply.github.com> --- sdks/go/pkg/beam/beam.shims.go | 15 --------------- sdks/go/pkg/beam/create.go | 5 ----- sdks/go/pkg/beam/util.go | 2 +- 3 files changed, 1 insertion(+), 21 deletions(-) diff --git a/sdks/go/pkg/beam/beam.shims.go b/sdks/go/pkg/beam/beam.shims.go index 6653fb0129f7..29ebaf2ca681 100644 --- a/sdks/go/pkg/beam/beam.shims.go +++ b/sdks/go/pkg/beam/beam.shims.go @@ -25,7 +25,6 @@ import ( // Library imports "github.com/apache/beam/sdks/v2/go/pkg/beam/core/runtime" "github.com/apache/beam/sdks/v2/go/pkg/beam/core/runtime/exec" - "github.com/apache/beam/sdks/v2/go/pkg/beam/core/runtime/graphx/schema" "github.com/apache/beam/sdks/v2/go/pkg/beam/core/sdf" "github.com/apache/beam/sdks/v2/go/pkg/beam/core/typex" "github.com/apache/beam/sdks/v2/go/pkg/beam/core/util/reflectx" @@ -44,13 +43,6 @@ func init() { runtime.RegisterFunction(schemaDec) runtime.RegisterFunction(schemaEnc) runtime.RegisterFunction(swapKVFn) - runtime.RegisterType(reflect.TypeOf((*createFn)(nil)).Elem()) - schema.RegisterType(reflect.TypeOf((*createFn)(nil)).Elem()) - runtime.RegisterType(reflect.TypeOf((*reflect.Type)(nil)).Elem()) - schema.RegisterType(reflect.TypeOf((*reflect.Type)(nil)).Elem()) - runtime.RegisterType(reflect.TypeOf((*reflectx.Func)(nil)).Elem()) - schema.RegisterType(reflect.TypeOf((*reflectx.Func)(nil)).Elem()) - reflectx.RegisterStructWrapper(reflect.TypeOf((*createFn)(nil)).Elem(), wrapMakerCreateFn) reflectx.RegisterFunc(reflect.TypeOf((*func(reflect.Type, []byte) (typex.T, error))(nil)).Elem(), funcMakerReflectÛ°TypeSliceOfByteГTypexÛ°TError) reflectx.RegisterFunc(reflect.TypeOf((*func(reflect.Type, typex.T) ([]byte, error))(nil)).Elem(), funcMakerReflectÛ°TypeTypexÛ°TГSliceOfByteError) reflectx.RegisterFunc(reflect.TypeOf((*func([]byte, func(typex.T)) error)(nil)).Elem(), funcMakerSliceOfByteEmitTypexÛ°TГError) @@ -64,13 +56,6 @@ func init() { exec.RegisterEmitter(reflect.TypeOf((*func(typex.T))(nil)).Elem(), emitMakerTypexÛ°T) } -func wrapMakerCreateFn(fn any) map[string]reflectx.Func { - dfn := fn.(*createFn) - return map[string]reflectx.Func{ - "ProcessElement": reflectx.MakeFunc(func(a0 []byte, a1 func(typex.T)) error { return dfn.ProcessElement(a0, a1) }), - } -} - type callerReflectÛ°TypeSliceOfByteГTypexÛ°TError struct { fn func(reflect.Type, []byte) (typex.T, error) } diff --git a/sdks/go/pkg/beam/create.go b/sdks/go/pkg/beam/create.go index 91e9f335ef87..d2bd554963ee 100644 --- a/sdks/go/pkg/beam/create.go +++ b/sdks/go/pkg/beam/create.go @@ -112,11 +112,6 @@ func createList(s Scope, values []any, t reflect.Type) (PCollection, error) { // TODO(herohde) 6/26/2017: make 'create' a SDF once supported. See BEAM-2421. -func init() { - register.DoFn2x1[[]byte, func(T), error]((*createFn)(nil)) - register.Emitter1[T]() -} - type createFn struct { Values [][]byte `json:"values"` Type EncodedType `json:"type"` diff --git a/sdks/go/pkg/beam/util.go b/sdks/go/pkg/beam/util.go index d591dedd7624..4b24af831134 100644 --- a/sdks/go/pkg/beam/util.go +++ b/sdks/go/pkg/beam/util.go @@ -16,7 +16,7 @@ package beam //go:generate go install github.com/apache/beam/sdks/v2/go/cmd/starcgen -//go:generate starcgen --package=beam --identifiers=addFixedKeyFn,dropKeyFn,dropValueFn,swapKVFn,explodeFn,jsonDec,jsonEnc,protoEnc,protoDec,schemaEnc,schemaDec,makePartitionFn,createFn +//go:generate starcgen --package=beam --identifiers=addFixedKeyFn,dropKeyFn,dropValueFn,swapKVFn,explodeFn,jsonDec,jsonEnc,protoEnc,protoDec,schemaEnc,schemaDec,makePartitionFn //go:generate go fmt // We have some freedom to create various utilities, users can use depending on From fa144f45b14b83d66c1a3a43ab47f20441d8e0fc Mon Sep 17 00:00:00 2001 From: Aleksandr Dudko <116064902+aleksandr-dudko@users.noreply.github.com> Date: Mon, 18 Sep 2023 18:51:36 +0400 Subject: [PATCH 039/105] Add GitHub Workflow Replacement for Jenkins job_PostCommit_Java_Examples_Dataflow_V2 (#28352) * Add GitHub Workflow Replacement for Jenkins job_PostCommit_Java_Examples_Dataflow_V2 * Update if-condition. --- ...m_PostCommit_Java_Examples_Dataflow_V2.yml | 89 +++++++++++++++ ...tCommit_Java_Examples_Dataflow_V2_Java.yml | 103 ++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 .github/workflows/beam_PostCommit_Java_Examples_Dataflow_V2.yml create mode 100644 .github/workflows/beam_PostCommit_Java_Examples_Dataflow_V2_Java.yml diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_V2.yml b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_V2.yml new file mode 100644 index 000000000000..f267b9b5b598 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_V2.yml @@ -0,0 +1,89 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java Examples Dataflow V2 + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_Examples_Dataflow_V2: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 180 + strategy: + matrix: + job_name: [beam_PostCommit_Java_Examples_Dataflow_V2] + job_phrase: [Run Java Examples on Dataflow Runner V2] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Java Examples on Dataflow Runner V2' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: run PostCommit Java Examples Dataflow V2 script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:google-cloud-dataflow-java:examplesJavaRunnerV2IntegrationTest + max-workers: 12 + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_V2_Java.yml b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_V2_Java.yml new file mode 100644 index 000000000000..817c5c82dba1 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_V2_Java.yml @@ -0,0 +1,103 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java Examples Dataflow V2 Java + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_Examples_Dataflow_V2_Java: + name: ${{matrix.job_name}} (${{matrix.job_phrase_1}}${{matrix.job_phrase_2}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 180 + strategy: + fail-fast: false + matrix: + job_name: [beam_PostCommit_Java_Examples_Dataflow_V2_Java] + job_phrase_1: [Run Java ] + job_phrase_2: [Examples on Dataflow Runner V2] + java_version: ['11', '17'] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + (contains(github.event.comment.body, 'Run Java') && + contains(github.event.comment.body, 'Examples on Dataflow Runner V2')) + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase_1 }} ${{matrix.java_version}} ${{ matrix.job_phrase_2 }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} ${{ matrix.job_phrase_1 }} ${{matrix.java_version}} ${{ matrix.job_phrase_2 }} + - name: Set up Java${{ matrix.java_version }} + uses: actions/setup-java@v3.8.0 + with: + distribution: 'temurin' + java-version: ${{ matrix.java_version }} + - name: run PostCommit Java Examples Dataflow V2 Java${{ matrix.java_version }} script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:google-cloud-dataflow-java:examplesJavaRunnerV2IntegrationTest + arguments: | + -PdisableSpotlessCheck=true \ + -PdisableCheckStyle=true \ + -PskipCheckerFramework \ + -PcompileAndRunTestsWithJava${{ matrix.java_version }} \ + -Pjava${{ matrix.java_version }}Home=$JAVA_HOME_${{ matrix.java_version }}_X64 \ + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file From 3f56f6578b786b61f749cc250a73f3d5a74e6851 Mon Sep 17 00:00:00 2001 From: Yi Hu Date: Mon, 18 Sep 2023 10:52:39 -0400 Subject: [PATCH 040/105] Comment results to PR only when issue_comments (#28480) * Comment results to PR only when issue_comments adjust comment mode * add same change to newly added workflow --- .github/workflows/beam_PostCommit_Java.yml | 14 +++++++++++--- .../beam_PostCommit_Java_Avro_Versions.yml | 2 ++ .../workflows/beam_PostCommit_Java_DataflowV1.yml | 2 ++ .../workflows/beam_PostCommit_Java_DataflowV2.yml | 2 ++ ...beam_PostCommit_Java_Examples_Dataflow_ARM.yml | 2 ++ ...eam_PostCommit_Java_Examples_Dataflow_Java.yml | 2 ++ .../beam_PostCommit_Java_Examples_Direct.yml | 2 ++ .../beam_PostCommit_Java_Examples_Flink.yml | 2 ++ .../beam_PostCommit_Java_Examples_Spark.yml | 2 ++ .../beam_PostCommit_Java_Hadoop_Versions.yml | 2 ++ .../beam_PostCommit_Java_IO_Performance_Tests.yml | 15 ++++++++++++++- .../beam_PostCommit_Java_Jpms_Dataflow_Java11.yml | 2 ++ .../beam_PostCommit_Java_Jpms_Dataflow_Java17.yml | 2 ++ .../beam_PostCommit_Java_Jpms_Direct_Java11.yml | 2 ++ .../beam_PostCommit_Java_Jpms_Direct_Java17.yml | 2 ++ .../beam_PostCommit_Java_Jpms_Flink_Java11.yml | 2 ++ .../beam_PostCommit_Java_Jpms_Spark_Java11.yml | 2 ++ .../workflows/beam_PostCommit_Java_Sickbay.yml | 2 ++ ...m_PostCommit_Java_ValidatesRunner_Dataflow.yml | 2 ++ ...Java_ValidatesRunner_Dataflow_JavaVersions.yml | 2 ++ ...it_Java_ValidatesRunner_Dataflow_Streaming.yml | 2 ++ ...ostCommit_Java_ValidatesRunner_Dataflow_V2.yml | 2 ++ ...Java_ValidatesRunner_Dataflow_V2_Streaming.yml | 2 ++ ...eam_PostCommit_Java_ValidatesRunner_Direct.yml | 2 ++ ...t_Java_ValidatesRunner_Direct_JavaVersions.yml | 2 ++ ...beam_PostCommit_Java_ValidatesRunner_Flink.yml | 2 ++ ...stCommit_Java_ValidatesRunner_Flink_Java11.yml | 2 ++ ...beam_PostCommit_Java_ValidatesRunner_Samza.yml | 2 ++ ...beam_PostCommit_Java_ValidatesRunner_Spark.yml | 2 ++ ...a_ValidatesRunner_SparkStructuredStreaming.yml | 2 ++ ...stCommit_Java_ValidatesRunner_Spark_Java11.yml | 2 ++ ...m_PostCommit_Java_ValidatesRunner_Twister2.yml | 2 ++ .../beam_PostCommit_Java_ValidatesRunner_ULR.yml | 2 ++ .github/workflows/beam_PostCommit_SQL.yml | 2 ++ .../beam_PostCommit_XVR_GoUsingJava_Dataflow.yml | 2 ++ .github/workflows/beam_PreCommit_ItFramework.yml | 2 ++ .github/workflows/beam_PreCommit_Java.yml | 2 ++ ...Commit_Java_Amazon-Web-Services2_IO_Direct.yml | 2 ++ ...eCommit_Java_Amazon-Web-Services_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Amqp_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Azure_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Cassandra_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Cdap_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Clickhouse_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Csv_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Debezium_IO_Direct.yml | 2 ++ ...eam_PreCommit_Java_ElasticSearch_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Examples_Dataflow.yml | 2 ++ ...am_PreCommit_Java_Examples_Dataflow_Java11.yml | 2 ++ ...am_PreCommit_Java_Examples_Dataflow_Java17.yml | 2 ++ ...ommit_Java_File-schema-transform_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Flink_Versions.yml | 2 ++ .../beam_PreCommit_Java_GCP_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_HBase_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_HCatalog_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Hadoop_IO_Direct.yml | 2 ++ .../workflows/beam_PreCommit_Java_IOs_Direct.yml | 2 ++ .../beam_PreCommit_Java_InfluxDb_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_JDBC_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Jms_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Kafka_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Kinesis_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Kudu_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_MongoDb_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Mqtt_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Neo4j_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_PVR_Flink_Batch.yml | 2 ++ .../beam_PreCommit_Java_PVR_Flink_Docker.yml | 2 ++ .../beam_PreCommit_Java_Parquet_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Pulsar_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_RabbitMq_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Redis_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_SingleStore_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Snowflake_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Solr_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Spark3_Versions.yml | 2 ++ .../beam_PreCommit_Java_Splunk_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Thrift_IO_Direct.yml | 2 ++ .../beam_PreCommit_Java_Tika_IO_Direct.yml | 2 ++ .github/workflows/beam_PreCommit_SQL.yml | 2 ++ .github/workflows/beam_PreCommit_SQL_Java11.yml | 2 ++ .github/workflows/beam_PreCommit_SQL_Java17.yml | 2 ++ 82 files changed, 185 insertions(+), 4 deletions(-) diff --git a/.github/workflows/beam_PostCommit_Java.yml b/.github/workflows/beam_PostCommit_Java.yml index 4cc7f638e1c1..a95291ea4d92 100644 --- a/.github/workflows/beam_PostCommit_Java.yml +++ b/.github/workflows/beam_PostCommit_Java.yml @@ -75,8 +75,16 @@ jobs: uses: ./.github/actions/gradle-command-self-hosted-action with: gradle-command: :javaPostCommit - - name: Upload test report + - name: Archive JUnit Test Results uses: actions/upload-artifact@v3 + if: failure() with: - name: java-code-coverage-report - path: "**/build/test-results/**/*.xml" \ No newline at end of file + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Avro_Versions.yml b/.github/workflows/beam_PostCommit_Java_Avro_Versions.yml index b18beb51beed..1fc83db7398e 100644 --- a/.github/workflows/beam_PostCommit_Java_Avro_Versions.yml +++ b/.github/workflows/beam_PostCommit_Java_Avro_Versions.yml @@ -80,4 +80,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_DataflowV1.yml b/.github/workflows/beam_PostCommit_Java_DataflowV1.yml index 1a3f07201fe8..5c2350a86a25 100644 --- a/.github/workflows/beam_PostCommit_Java_DataflowV1.yml +++ b/.github/workflows/beam_PostCommit_Java_DataflowV1.yml @@ -92,4 +92,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_DataflowV2.yml b/.github/workflows/beam_PostCommit_Java_DataflowV2.yml index 4b06592a972c..0bbee0bb7e2d 100644 --- a/.github/workflows/beam_PostCommit_Java_DataflowV2.yml +++ b/.github/workflows/beam_PostCommit_Java_DataflowV2.yml @@ -85,4 +85,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml index 8516ccd393a6..fbec73dd433e 100644 --- a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml +++ b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml @@ -140,4 +140,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java.yml b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java.yml index 7015c4fee20a..f34ffc878ea5 100644 --- a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java.yml +++ b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java.yml @@ -95,4 +95,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Direct.yml b/.github/workflows/beam_PostCommit_Java_Examples_Direct.yml index 6831d7db3b5d..b21fa394a8e8 100644 --- a/.github/workflows/beam_PostCommit_Java_Examples_Direct.yml +++ b/.github/workflows/beam_PostCommit_Java_Examples_Direct.yml @@ -90,4 +90,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Flink.yml b/.github/workflows/beam_PostCommit_Java_Examples_Flink.yml index 60644c434edc..59cb66457ec5 100644 --- a/.github/workflows/beam_PostCommit_Java_Examples_Flink.yml +++ b/.github/workflows/beam_PostCommit_Java_Examples_Flink.yml @@ -98,4 +98,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Spark.yml b/.github/workflows/beam_PostCommit_Java_Examples_Spark.yml index 17cbc8e583e2..1a46dd737428 100644 --- a/.github/workflows/beam_PostCommit_Java_Examples_Spark.yml +++ b/.github/workflows/beam_PostCommit_Java_Examples_Spark.yml @@ -90,4 +90,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Hadoop_Versions.yml b/.github/workflows/beam_PostCommit_Java_Hadoop_Versions.yml index cd281c77a7e1..16b0373b8aa9 100644 --- a/.github/workflows/beam_PostCommit_Java_Hadoop_Versions.yml +++ b/.github/workflows/beam_PostCommit_Java_Hadoop_Versions.yml @@ -88,4 +88,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_IO_Performance_Tests.yml b/.github/workflows/beam_PostCommit_Java_IO_Performance_Tests.yml index ffd5751fd8b3..e5c6befe2e76 100644 --- a/.github/workflows/beam_PostCommit_Java_IO_Performance_Tests.yml +++ b/.github/workflows/beam_PostCommit_Java_IO_Performance_Tests.yml @@ -111,4 +111,17 @@ jobs: gradle-command: :it:${{ matrix.test_case }} env: exportDataset: performance_tests - exportTable: io_performance_metrics_test \ No newline at end of file + exportTable: io_performance_metrics_test + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11.yml b/.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11.yml index 925942d46b90..25d843561f8f 100644 --- a/.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11.yml +++ b/.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11.yml @@ -89,4 +89,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17.yml b/.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17.yml index 6094cdc7e4e6..90fe8af087e6 100644 --- a/.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17.yml +++ b/.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17.yml @@ -94,4 +94,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java11.yml b/.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java11.yml index 0fa065edd34b..006a974e12ba 100644 --- a/.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java11.yml +++ b/.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java11.yml @@ -89,4 +89,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml b/.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml index f85ad12437bc..b898d62a0c5d 100644 --- a/.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml +++ b/.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml @@ -94,4 +94,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml b/.github/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml index 84f294de5c21..83063ea0e4d1 100644 --- a/.github/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml +++ b/.github/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml @@ -89,4 +89,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml b/.github/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml index 56ec589b38d7..a31802c0edab 100644 --- a/.github/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml +++ b/.github/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml @@ -89,4 +89,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Sickbay.yml b/.github/workflows/beam_PostCommit_Java_Sickbay.yml index ad437500dba4..c3943ac5deff 100644 --- a/.github/workflows/beam_PostCommit_Java_Sickbay.yml +++ b/.github/workflows/beam_PostCommit_Java_Sickbay.yml @@ -85,4 +85,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml index b7f87fe20133..2300daa376e9 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml @@ -91,4 +91,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_JavaVersions.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_JavaVersions.yml index 08740fad64ad..2b61cb216c1c 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_JavaVersions.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_JavaVersions.yml @@ -111,4 +111,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml index b58ffb72ed76..ad114f5e0d3a 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml @@ -91,4 +91,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml index eef664fdf0aa..8dde25a81b25 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml @@ -91,4 +91,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml index df71c3079a5c..2e61a8a6c2de 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml @@ -91,4 +91,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml index 6a260ad4595e..e2980628f36e 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml @@ -90,4 +90,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_JavaVersions.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_JavaVersions.yml index bb73a15b71b9..0a4bb4d27af6 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_JavaVersions.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_JavaVersions.yml @@ -106,4 +106,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml index 0c273713def3..7b2baaaefb64 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml @@ -96,4 +96,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml index 68486868756b..e745c548851f 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml @@ -109,4 +109,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Samza.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Samza.yml index 5d17fbc61346..e5981899b046 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Samza.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Samza.yml @@ -88,4 +88,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark.yml index 3204d9ad6ed5..e4d3bb1602e2 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark.yml @@ -88,4 +88,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml index 7e97f50dcac3..e838bdd419a5 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml @@ -88,4 +88,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark_Java11.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark_Java11.yml index ce63b24d2c33..5bf5fce71060 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark_Java11.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark_Java11.yml @@ -109,4 +109,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml index 1f13269e7b95..16c4800ef72c 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml @@ -88,4 +88,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml index 65b20d5e2a66..7c214f2cbed6 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml @@ -92,4 +92,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_SQL.yml b/.github/workflows/beam_PostCommit_SQL.yml index f85f98d0d0d5..dc6ed87514a9 100644 --- a/.github/workflows/beam_PostCommit_SQL.yml +++ b/.github/workflows/beam_PostCommit_SQL.yml @@ -85,4 +85,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' diff --git a/.github/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml b/.github/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml index ee715495ab0e..2a463afbf8e4 100644 --- a/.github/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml +++ b/.github/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml @@ -89,4 +89,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PreCommit_ItFramework.yml b/.github/workflows/beam_PreCommit_ItFramework.yml index 83f29211da35..b28015138d95 100644 --- a/.github/workflows/beam_PreCommit_ItFramework.yml +++ b/.github/workflows/beam_PreCommit_ItFramework.yml @@ -103,4 +103,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PreCommit_Java.yml b/.github/workflows/beam_PreCommit_Java.yml index 98022dba98ab..bd9ada0bbb21 100644 --- a/.github/workflows/beam_PreCommit_Java.yml +++ b/.github/workflows/beam_PreCommit_Java.yml @@ -182,6 +182,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Amazon-Web-Services2_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Amazon-Web-Services2_IO_Direct.yml index ef1dcb636032..9f9fd15a5a2b 100644 --- a/.github/workflows/beam_PreCommit_Java_Amazon-Web-Services2_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Amazon-Web-Services2_IO_Direct.yml @@ -123,6 +123,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Amazon-Web-Services_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Amazon-Web-Services_IO_Direct.yml index cf57623f0602..559486ee39e7 100644 --- a/.github/workflows/beam_PreCommit_Java_Amazon-Web-Services_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Amazon-Web-Services_IO_Direct.yml @@ -123,6 +123,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Amqp_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Amqp_IO_Direct.yml index 28aeae758376..d50564469d45 100644 --- a/.github/workflows/beam_PreCommit_Java_Amqp_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Amqp_IO_Direct.yml @@ -98,6 +98,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Azure_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Azure_IO_Direct.yml index 05f1bb5ca53b..5fff744408a2 100644 --- a/.github/workflows/beam_PreCommit_Java_Azure_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Azure_IO_Direct.yml @@ -116,6 +116,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Cassandra_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Cassandra_IO_Direct.yml index 3c56c4f062a5..6d661333d98a 100644 --- a/.github/workflows/beam_PreCommit_Java_Cassandra_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Cassandra_IO_Direct.yml @@ -98,6 +98,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Cdap_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Cdap_IO_Direct.yml index fa83a5958fe2..4cd4eb850590 100644 --- a/.github/workflows/beam_PreCommit_Java_Cdap_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Cdap_IO_Direct.yml @@ -102,6 +102,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Clickhouse_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Clickhouse_IO_Direct.yml index 266e311c092a..f281182fc287 100644 --- a/.github/workflows/beam_PreCommit_Java_Clickhouse_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Clickhouse_IO_Direct.yml @@ -98,6 +98,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Csv_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Csv_IO_Direct.yml index 9b6d73395a1b..573d434b9dad 100644 --- a/.github/workflows/beam_PreCommit_Java_Csv_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Csv_IO_Direct.yml @@ -98,6 +98,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Debezium_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Debezium_IO_Direct.yml index 5d08a5e03e61..4558e3eb3b7b 100644 --- a/.github/workflows/beam_PreCommit_Java_Debezium_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Debezium_IO_Direct.yml @@ -102,6 +102,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_ElasticSearch_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_ElasticSearch_IO_Direct.yml index c58876de7ff0..263b298ff9ff 100644 --- a/.github/workflows/beam_PreCommit_Java_ElasticSearch_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_ElasticSearch_IO_Direct.yml @@ -106,6 +106,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Examples_Dataflow.yml b/.github/workflows/beam_PreCommit_Java_Examples_Dataflow.yml index 2d9e34eeebae..34e52fd15b6b 100644 --- a/.github/workflows/beam_PreCommit_Java_Examples_Dataflow.yml +++ b/.github/workflows/beam_PreCommit_Java_Examples_Dataflow.yml @@ -126,4 +126,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java11.yml b/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java11.yml index 5c7c81497756..28abc3cd7947 100644 --- a/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java11.yml +++ b/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java11.yml @@ -127,4 +127,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java17.yml b/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java17.yml index 724daf16d889..e209035a5fcb 100644 --- a/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java17.yml +++ b/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java17.yml @@ -124,6 +124,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v2 diff --git a/.github/workflows/beam_PreCommit_Java_File-schema-transform_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_File-schema-transform_IO_Direct.yml index abb3c5d029a4..9ad288d42029 100644 --- a/.github/workflows/beam_PreCommit_Java_File-schema-transform_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_File-schema-transform_IO_Direct.yml @@ -94,6 +94,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Flink_Versions.yml b/.github/workflows/beam_PreCommit_Java_Flink_Versions.yml index 8fafdbcaa9e5..f001dad86049 100644 --- a/.github/workflows/beam_PreCommit_Java_Flink_Versions.yml +++ b/.github/workflows/beam_PreCommit_Java_Flink_Versions.yml @@ -108,4 +108,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PreCommit_Java_GCP_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_GCP_IO_Direct.yml index f567a4fd2a0d..d9a5deb3dcfb 100644 --- a/.github/workflows/beam_PreCommit_Java_GCP_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_GCP_IO_Direct.yml @@ -120,6 +120,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_HBase_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_HBase_IO_Direct.yml index 0664f969996e..681a37588043 100644 --- a/.github/workflows/beam_PreCommit_Java_HBase_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_HBase_IO_Direct.yml @@ -100,6 +100,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_HCatalog_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_HCatalog_IO_Direct.yml index 688a4ef2e5ff..ef333637fe98 100644 --- a/.github/workflows/beam_PreCommit_Java_HCatalog_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_HCatalog_IO_Direct.yml @@ -100,6 +100,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Hadoop_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Hadoop_IO_Direct.yml index cbfa9c371e38..efdd46ca98f0 100644 --- a/.github/workflows/beam_PreCommit_Java_Hadoop_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Hadoop_IO_Direct.yml @@ -138,6 +138,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_IOs_Direct.yml b/.github/workflows/beam_PreCommit_Java_IOs_Direct.yml index 8c7796da9a74..f135d5804f1f 100644 --- a/.github/workflows/beam_PreCommit_Java_IOs_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_IOs_Direct.yml @@ -99,6 +99,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_InfluxDb_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_InfluxDb_IO_Direct.yml index 17f1676eb70f..d45ae862f6c6 100644 --- a/.github/workflows/beam_PreCommit_Java_InfluxDb_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_InfluxDb_IO_Direct.yml @@ -98,6 +98,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_JDBC_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_JDBC_IO_Direct.yml index cbdfdaa5d304..4a9850bfba9a 100644 --- a/.github/workflows/beam_PreCommit_Java_JDBC_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_JDBC_IO_Direct.yml @@ -105,6 +105,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Jms_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Jms_IO_Direct.yml index 57aae16ebefc..dbcf0c350532 100644 --- a/.github/workflows/beam_PreCommit_Java_Jms_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Jms_IO_Direct.yml @@ -105,6 +105,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Kafka_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Kafka_IO_Direct.yml index 003b56f80416..9d3c95d5ef3d 100644 --- a/.github/workflows/beam_PreCommit_Java_Kafka_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Kafka_IO_Direct.yml @@ -107,6 +107,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Kinesis_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Kinesis_IO_Direct.yml index 3673fbcfef3a..2d974f86d8c1 100644 --- a/.github/workflows/beam_PreCommit_Java_Kinesis_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Kinesis_IO_Direct.yml @@ -130,6 +130,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Kudu_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Kudu_IO_Direct.yml index be65e5522761..de151381a739 100644 --- a/.github/workflows/beam_PreCommit_Java_Kudu_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Kudu_IO_Direct.yml @@ -98,6 +98,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_MongoDb_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_MongoDb_IO_Direct.yml index e457693adfec..9badf5d11006 100644 --- a/.github/workflows/beam_PreCommit_Java_MongoDb_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_MongoDb_IO_Direct.yml @@ -98,6 +98,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Mqtt_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Mqtt_IO_Direct.yml index d01078fa08ca..d7df7e57da59 100644 --- a/.github/workflows/beam_PreCommit_Java_Mqtt_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Mqtt_IO_Direct.yml @@ -98,6 +98,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Neo4j_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Neo4j_IO_Direct.yml index 0a0ba8664a07..f07719ca01ef 100644 --- a/.github/workflows/beam_PreCommit_Java_Neo4j_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Neo4j_IO_Direct.yml @@ -107,6 +107,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_PVR_Flink_Batch.yml b/.github/workflows/beam_PreCommit_Java_PVR_Flink_Batch.yml index b23d70c4f5af..2eba747cd18a 100644 --- a/.github/workflows/beam_PreCommit_Java_PVR_Flink_Batch.yml +++ b/.github/workflows/beam_PreCommit_Java_PVR_Flink_Batch.yml @@ -98,4 +98,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PreCommit_Java_PVR_Flink_Docker.yml b/.github/workflows/beam_PreCommit_Java_PVR_Flink_Docker.yml index 312210245477..af2d20f721db 100644 --- a/.github/workflows/beam_PreCommit_Java_PVR_Flink_Docker.yml +++ b/.github/workflows/beam_PreCommit_Java_PVR_Flink_Docker.yml @@ -108,4 +108,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PreCommit_Java_Parquet_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Parquet_IO_Direct.yml index 04468ae7588c..f3e0fc45dc42 100644 --- a/.github/workflows/beam_PreCommit_Java_Parquet_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Parquet_IO_Direct.yml @@ -98,6 +98,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Pulsar_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Pulsar_IO_Direct.yml index 258e95e5d1b9..9101e148b1e3 100644 --- a/.github/workflows/beam_PreCommit_Java_Pulsar_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Pulsar_IO_Direct.yml @@ -116,6 +116,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_RabbitMq_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_RabbitMq_IO_Direct.yml index f137a5441bf3..bae9465b9423 100644 --- a/.github/workflows/beam_PreCommit_Java_RabbitMq_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_RabbitMq_IO_Direct.yml @@ -98,6 +98,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Redis_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Redis_IO_Direct.yml index 7a90efe0ede2..23ae58d7fdf7 100644 --- a/.github/workflows/beam_PreCommit_Java_Redis_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Redis_IO_Direct.yml @@ -98,6 +98,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_SingleStore_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_SingleStore_IO_Direct.yml index 26b7c81503bd..3cb79c90496e 100644 --- a/.github/workflows/beam_PreCommit_Java_SingleStore_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_SingleStore_IO_Direct.yml @@ -100,6 +100,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Snowflake_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Snowflake_IO_Direct.yml index 30cc95108239..3d106246954f 100644 --- a/.github/workflows/beam_PreCommit_Java_Snowflake_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Snowflake_IO_Direct.yml @@ -109,6 +109,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Solr_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Solr_IO_Direct.yml index a19db8a6faf4..ce87f25fec57 100644 --- a/.github/workflows/beam_PreCommit_Java_Solr_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Solr_IO_Direct.yml @@ -98,6 +98,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Spark3_Versions.yml b/.github/workflows/beam_PreCommit_Java_Spark3_Versions.yml index b7b1dfa0ea29..d5952e9658a3 100644 --- a/.github/workflows/beam_PreCommit_Java_Spark3_Versions.yml +++ b/.github/workflows/beam_PreCommit_Java_Spark3_Versions.yml @@ -112,4 +112,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PreCommit_Java_Splunk_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Splunk_IO_Direct.yml index 4131bbdfa04f..25e33ae8ed96 100644 --- a/.github/workflows/beam_PreCommit_Java_Splunk_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Splunk_IO_Direct.yml @@ -98,6 +98,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Thrift_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Thrift_IO_Direct.yml index d8b275ccfefb..b0f2355f7c15 100644 --- a/.github/workflows/beam_PreCommit_Java_Thrift_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Thrift_IO_Direct.yml @@ -98,6 +98,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_Java_Tika_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Tika_IO_Direct.yml index c3e340b3aa0e..98aae62683f4 100644 --- a/.github/workflows/beam_PreCommit_Java_Tika_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Tika_IO_Direct.yml @@ -98,6 +98,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_SQL.yml b/.github/workflows/beam_PreCommit_SQL.yml index 3b8ad98db155..cb024bb7e1a3 100644 --- a/.github/workflows/beam_PreCommit_SQL.yml +++ b/.github/workflows/beam_PreCommit_SQL.yml @@ -101,6 +101,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v2 diff --git a/.github/workflows/beam_PreCommit_SQL_Java11.yml b/.github/workflows/beam_PreCommit_SQL_Java11.yml index 2ff5e7459214..13b4f6a0b8b7 100644 --- a/.github/workflows/beam_PreCommit_SQL_Java11.yml +++ b/.github/workflows/beam_PreCommit_SQL_Java11.yml @@ -116,6 +116,8 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' - name: Archive SpotBugs Results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PreCommit_SQL_Java17.yml b/.github/workflows/beam_PreCommit_SQL_Java17.yml index 15a223dcc19e..68464009df2c 100644 --- a/.github/workflows/beam_PreCommit_SQL_Java17.yml +++ b/.github/workflows/beam_PreCommit_SQL_Java17.yml @@ -119,4 +119,6 @@ jobs: uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} files: '**/build/test-results/**/*.xml' \ No newline at end of file From 8c7e8b0949a54debc9d78c5973c6e549bfb5820a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20=C3=96jeling?= <51084516+johannaojeling@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:54:14 +0200 Subject: [PATCH 041/105] Retrieve element type from input PCollection in parquetio.Write (#28491) --- CHANGES.md | 1 + sdks/go/pkg/beam/io/parquetio/parquetio.go | 5 +++-- sdks/go/pkg/beam/io/parquetio/parquetio_test.go | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3705cebc88df..47c35fe3491b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -73,6 +73,7 @@ * Removed fastjson library dependency for Beam SQL. Table property is changed to be based on jackson ObjectNode (Java) ([#24154](https://github.com/apache/beam/issues/24154)). * Removed TensorFlow from Beam Python container images [PR](https://github.com/apache/beam/pull/28424). If you have been negatively affected by this change, please comment on [#20605](https://github.com/apache/beam/issues/20605). +* Removed the parameter `t reflect.Type` from `parquetio.Write`. The element type is derived from the input PCollection (Go) ([#28490](https://github.com/apache/beam/issues/28490)) ## Deprecations diff --git a/sdks/go/pkg/beam/io/parquetio/parquetio.go b/sdks/go/pkg/beam/io/parquetio/parquetio.go index 9c48d134014b..eb2a611f6836 100644 --- a/sdks/go/pkg/beam/io/parquetio/parquetio.go +++ b/sdks/go/pkg/beam/io/parquetio/parquetio.go @@ -96,7 +96,7 @@ func (a *parquetReadFn) ProcessElement(ctx context.Context, file fileio.Readable } // Write writes a PCollection to .parquet file. -// Write expects a type t of struct with parquet tags +// Write expects elements of a struct type with parquet tags // For example: // // type Student struct { @@ -108,7 +108,8 @@ func (a *parquetReadFn) ProcessElement(ctx context.Context, file fileio.Readable // Day int32 `parquet:"name=day, type=INT32, convertedtype=DATE"` // Ignored int32 //without parquet tag and won't write // } -func Write(s beam.Scope, filename string, t reflect.Type, col beam.PCollection) { +func Write(s beam.Scope, filename string, col beam.PCollection) { + t := col.Type().Type() s = s.Scope("parquetio.Write") filesystem.ValidateScheme(filename) pre := beam.AddFixedKey(s, col) diff --git a/sdks/go/pkg/beam/io/parquetio/parquetio_test.go b/sdks/go/pkg/beam/io/parquetio/parquetio_test.go index 1cceefcef46b..f3c901395609 100644 --- a/sdks/go/pkg/beam/io/parquetio/parquetio_test.go +++ b/sdks/go/pkg/beam/io/parquetio/parquetio_test.go @@ -95,7 +95,7 @@ func TestWrite(t *testing.T) { } p, s, sequence := ptest.CreateList(studentList) parquetFile := "./write_student.parquet" - Write(s, parquetFile, reflect.TypeOf(Student{}), sequence) + Write(s, parquetFile, sequence) t.Cleanup(func() { os.Remove(parquetFile) }) From a0e9775ac25a5676714ba743cbd7db02b0512af4 Mon Sep 17 00:00:00 2001 From: Yi Hu Date: Mon, 18 Sep 2023 10:58:30 -0400 Subject: [PATCH 042/105] Support legacy DATE and TIME logical types in xlang JdbcIO (#28382) * Support legacy DATE and TIME logical types in xlang JdbcIO * Add identifier to URN for legacy Java logical types * Implement JdbcIO logical type javasdk_date and javasdk_time in Python --- .../beam/sdk/schemas/SchemaTranslation.java | 74 ++++++++----- .../sdk/schemas/SchemaTranslationTest.java | 69 ++++++++++-- .../apache/beam/sdk/io/jdbc/LogicalTypes.java | 70 +----------- .../io/external/xlang_jdbcio_it_test.py | 16 ++- sdks/python/apache_beam/io/jdbc.py | 100 ++++++++++++++++++ 5 files changed, 223 insertions(+), 106 deletions(-) diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/SchemaTranslation.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/SchemaTranslation.java index fb6746b9cdd1..c0683ef44616 100644 --- a/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/SchemaTranslation.java +++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/SchemaTranslation.java @@ -56,6 +56,7 @@ import org.apache.beam.vendor.grpc.v1p54p0.com.google.protobuf.ByteString; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.annotations.VisibleForTesting; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions; +import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Strings; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.ImmutableMap; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.Iterables; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.Maps; @@ -74,7 +75,23 @@ public class SchemaTranslation { private static final Logger LOG = LoggerFactory.getLogger(SchemaTranslation.class); private static final String URN_BEAM_LOGICAL_DECIMAL = FixedPrecisionNumeric.BASE_IDENTIFIER; - private static final String URN_BEAM_LOGICAL_JAVASDK = "beam:logical_type:javasdk:v1"; + + private static String getLogicalTypeUrn(String identifier) { + if (identifier.startsWith("beam:logical_type:")) { + return identifier; + } else { + String filtered = identifier.replaceAll("[^0-9A-Za-z_]", "").toLowerCase(); + if (!Strings.isNullOrEmpty(filtered)) { + // urn for non-standard Java SDK logical types are assigned with javasdk_ + return String.format("beam:logical_type:javasdk_%s:v1", filtered); + } else { + // raw "javasdk" name should only be a last resort. Types defined in Beam should have their + // own URN. + return "beam:logical_type:javasdk:v1"; + } + } + } + private static final String URN_BEAM_LOGICAL_MILLIS_INSTANT = SchemaApi.LogicalTypes.Enum.MILLIS_INSTANT .getValueDescriptor() @@ -84,18 +101,18 @@ public class SchemaTranslation { // TODO(https://github.com/apache/beam/issues/19715): Populate this with a LogicalTypeRegistrar, // which includes a way to construct // the LogicalType given an argument. - private static final ImmutableMap>> - STANDARD_LOGICAL_TYPES = - ImmutableMap.>>builder() - .put(FixedPrecisionNumeric.IDENTIFIER, FixedPrecisionNumeric.class) - .put(MicrosInstant.IDENTIFIER, MicrosInstant.class) - .put(SchemaLogicalType.IDENTIFIER, SchemaLogicalType.class) - .put(PythonCallable.IDENTIFIER, PythonCallable.class) - .put(FixedBytes.IDENTIFIER, FixedBytes.class) - .put(VariableBytes.IDENTIFIER, VariableBytes.class) - .put(FixedString.IDENTIFIER, FixedString.class) - .put(VariableString.IDENTIFIER, VariableString.class) - .build(); + @VisibleForTesting + static final ImmutableMap>> STANDARD_LOGICAL_TYPES = + ImmutableMap.>>builder() + .put(FixedPrecisionNumeric.IDENTIFIER, FixedPrecisionNumeric.class) + .put(MicrosInstant.IDENTIFIER, MicrosInstant.class) + .put(SchemaLogicalType.IDENTIFIER, SchemaLogicalType.class) + .put(PythonCallable.IDENTIFIER, PythonCallable.class) + .put(FixedBytes.IDENTIFIER, FixedBytes.class) + .put(VariableBytes.IDENTIFIER, VariableBytes.class) + .put(FixedString.IDENTIFIER, FixedString.class) + .put(VariableString.IDENTIFIER, VariableString.class) + .build(); public static SchemaApi.Schema schemaToProto(Schema schema, boolean serializeLogicalType) { String uuid = schema.getUUID() != null ? schema.getUUID().toString() : ""; @@ -179,11 +196,7 @@ static SchemaApi.FieldType fieldTypeToProto(FieldType fieldType, boolean seriali fieldValueToProto(logicalType.getArgumentType(), logicalType.getArgument())); } } else { - // TODO(https://github.com/apache/beam/issues/19715): "javasdk" types should only - // be a last resort. Types defined in Beam should have their own URN, and there - // should be a mechanism for users to register their own types by URN. - String urn = - identifier.startsWith("beam:logical_type:") ? identifier : URN_BEAM_LOGICAL_JAVASDK; + String urn = getLogicalTypeUrn(identifier); logicalTypeBuilder = SchemaApi.LogicalType.newBuilder() .setRepresentation( @@ -429,15 +442,22 @@ private static FieldType fieldTypeFromProtoWithoutNullable(SchemaApi.FieldType p } else if (urn.equals(URN_BEAM_LOGICAL_DECIMAL)) { return FieldType.DECIMAL; } else if (urn.startsWith("beam:logical_type:")) { - try { - return FieldType.logicalType( - (LogicalType) - SerializableUtils.deserializeFromByteArray( - logicalType.getPayload().toByteArray(), "logicalType")); - } catch (IllegalArgumentException e) { - LOG.warn( - "Unable to deserialize the logical type {} from proto. Mark as UnknownLogicalType.", - urn); + if (!logicalType.getPayload().isEmpty()) { + // logical type has a payload, try to recover the instance by deserialization + try { + return FieldType.logicalType( + (LogicalType) + SerializableUtils.deserializeFromByteArray( + logicalType.getPayload().toByteArray(), "logicalType")); + } catch (IllegalArgumentException e) { + LOG.warn( + "Unable to deserialize the logical type {} from proto. Mark as UnknownLogicalType.", + urn); + } + } else { + // logical type does not have a payload. This happens when it is passed xlang. + // TODO(yathu) it appears this path is called heavily, consider cache the instance + LOG.debug("Constructing non-standard logical type {} as UnknownLogicalType", urn); } } // assemble an UnknownLogicalType diff --git a/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/SchemaTranslationTest.java b/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/SchemaTranslationTest.java index bdce452192a4..3020d7e42d05 100644 --- a/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/SchemaTranslationTest.java +++ b/sdks/java/core/src/test/java/org/apache/beam/sdk/schemas/SchemaTranslationTest.java @@ -17,6 +17,7 @@ */ package org.apache.beam.sdk.schemas; +import static org.apache.beam.sdk.schemas.SchemaTranslation.STANDARD_LOGICAL_TYPES; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -48,6 +49,7 @@ import org.apache.beam.sdk.schemas.logicaltypes.PythonCallable; import org.apache.beam.sdk.schemas.logicaltypes.SchemaLogicalType; import org.apache.beam.sdk.schemas.logicaltypes.SqlTypes; +import org.apache.beam.sdk.schemas.logicaltypes.UnknownLogicalType; import org.apache.beam.sdk.schemas.logicaltypes.VariableBytes; import org.apache.beam.sdk.schemas.logicaltypes.VariableString; import org.apache.beam.sdk.values.Row; @@ -186,7 +188,8 @@ public static Iterable data() { .withOptions(optionsBuilder)) .add( Schema.of( - Field.of("null_argument", FieldType.logicalType(new NullArgumentLogicalType())))) + Field.of( + "null_argument", FieldType.logicalType(new PortableNullArgLogicalType())))) .add(Schema.of(Field.of("logical_argument", FieldType.logicalType(new DateTime())))) .add( Schema.of(Field.of("single_arg_argument", FieldType.logicalType(FixedBytes.of(100))))) @@ -348,14 +351,14 @@ public static Iterable data() { .add(simpleRow(FieldType.row(row.getSchema()), row)) .add(simpleRow(FieldType.DATETIME, new Instant(23L))) .add(simpleRow(FieldType.DECIMAL, BigDecimal.valueOf(100000))) - .add(simpleRow(FieldType.logicalType(new NullArgumentLogicalType()), "str")) + .add(simpleRow(FieldType.logicalType(new PortableNullArgLogicalType()), "str")) .add(simpleRow(FieldType.logicalType(new DateTime()), LocalDateTime.of(2000, 1, 3, 3, 1))) .add(simpleNullRow(FieldType.STRING)) .add(simpleNullRow(FieldType.INT32)) .add(simpleNullRow(FieldType.map(FieldType.STRING, FieldType.INT32))) .add(simpleNullRow(FieldType.array(FieldType.STRING))) .add(simpleNullRow(FieldType.row(row.getSchema()))) - .add(simpleNullRow(FieldType.logicalType(new NullArgumentLogicalType()))) + .add(simpleNullRow(FieldType.logicalType(new PortableNullArgLogicalType()))) .add(simpleNullRow(FieldType.logicalType(new DateTime()))) .add(simpleNullRow(FieldType.DECIMAL)) .add(simpleNullRow(FieldType.DATETIME)) @@ -419,6 +422,8 @@ public static Iterable data() { .add(FieldType.logicalType(FixedString.of(10))) .add(FieldType.logicalType(VariableString.of(10))) .add(FieldType.logicalType(FixedPrecisionNumeric.of(10))) + .add(FieldType.logicalType(new PortableNullArgLogicalType())) + .add(FieldType.logicalType(new NullArgumentLogicalType())) .build(); } @@ -426,7 +431,7 @@ public static Iterable data() { public Schema.FieldType fieldType; @Test - public void testPortableLogicalTypeSerializeDeserilizeCorrectly() { + public void testLogicalTypeSerializeDeserilizeCorrectly() { SchemaApi.FieldType proto = SchemaTranslation.fieldTypeToProto(fieldType, true); Schema.FieldType translated = SchemaTranslation.fieldTypeFromProto(proto); @@ -438,14 +443,64 @@ public void testPortableLogicalTypeSerializeDeserilizeCorrectly() { assertThat( translated.getLogicalType().getArgument(), equalTo(fieldType.getLogicalType().getArgument())); + assertThat( + translated.getLogicalType().getIdentifier(), + equalTo(fieldType.getLogicalType().getIdentifier())); + } + + @Test + public void testLogicalTypeFromToProtoCorrectly() { + SchemaApi.FieldType proto = SchemaTranslation.fieldTypeToProto(fieldType, false); + Schema.FieldType translated = SchemaTranslation.fieldTypeFromProto(proto); + + if (STANDARD_LOGICAL_TYPES.containsKey(translated.getLogicalType().getIdentifier())) { + // standard logical type should be able to fully recover the original type + assertThat( + translated.getLogicalType().getClass(), equalTo(fieldType.getLogicalType().getClass())); + } else { + // non-standard type will get assembled to UnknownLogicalType + assertThat(translated.getLogicalType().getClass(), equalTo(UnknownLogicalType.class)); + } + assertThat( + translated.getLogicalType().getArgumentType(), + equalTo(fieldType.getLogicalType().getArgumentType())); + assertThat( + translated.getLogicalType().getArgument(), + equalTo(fieldType.getLogicalType().getArgument())); + if (fieldType.getLogicalType().getIdentifier().startsWith("beam:logical_type:")) { + // portable logical type should fully recover the urn + assertThat( + translated.getLogicalType().getIdentifier(), + equalTo(fieldType.getLogicalType().getIdentifier())); + } else { + // non-portable logical type would have "javasdk_" urn + assertThat( + translated.getLogicalType().getIdentifier(), + equalTo( + String.format( + "beam:logical_type:javasdk_%s:v1", + fieldType + .getLogicalType() + .getIdentifier() + .toLowerCase() + .replaceAll("[^0-9A-Za-z_]", "")))); + } } } - /** A simple logical type that has no argument. */ - private static class NullArgumentLogicalType implements Schema.LogicalType { + /** A portable logical type that has no argument. */ + private static class PortableNullArgLogicalType extends NullArgumentLogicalType { public static final String IDENTIFIER = "beam:logical_type:null_argument:v1"; - public NullArgumentLogicalType() {} + @Override + public String getIdentifier() { + return IDENTIFIER; + } + } + + /** A non-portable (Java SDK) logical type that has no argument. */ + private static class NullArgumentLogicalType implements Schema.LogicalType { + public static final String IDENTIFIER = "NULL_ARGUMENT"; @Override public String toBaseType(String input) { diff --git a/sdks/java/io/jdbc/src/main/java/org/apache/beam/sdk/io/jdbc/LogicalTypes.java b/sdks/java/io/jdbc/src/main/java/org/apache/beam/sdk/io/jdbc/LogicalTypes.java index 674b274f907b..6e8e46b7afa2 100644 --- a/sdks/java/io/jdbc/src/main/java/org/apache/beam/sdk/io/jdbc/LogicalTypes.java +++ b/sdks/java/io/jdbc/src/main/java/org/apache/beam/sdk/io/jdbc/LogicalTypes.java @@ -19,7 +19,6 @@ import java.sql.JDBCType; import java.time.Instant; -import java.util.Objects; import org.apache.beam.sdk.schemas.Schema; import org.apache.beam.sdk.schemas.Schema.FieldType; import org.apache.beam.sdk.schemas.logicaltypes.FixedBytes; @@ -30,11 +29,11 @@ import org.apache.beam.sdk.schemas.logicaltypes.VariableBytes; import org.apache.beam.sdk.schemas.logicaltypes.VariableString; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.annotations.VisibleForTesting; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; /** Beam {@link org.apache.beam.sdk.schemas.Schema.LogicalType} implementations of JDBC types. */ class LogicalTypes { + // Logical types of the following static members are not portable and are preserved for + // compatibility reason. Consider using portable logical types when adding new ones. static final Schema.FieldType JDBC_BIT_TYPE = Schema.FieldType.logicalType( new PassThroughLogicalType( @@ -110,69 +109,4 @@ static Schema.LogicalType fixedOrVariableBytes(String name, int return FixedBytes.of(name, length); } } - - /** Base class for JDBC logical types. */ - abstract static class JdbcLogicalType - implements Schema.LogicalType { - protected final String identifier; - protected final Schema.FieldType argumentType; - protected final Schema.FieldType baseType; - protected final Object argument; - - protected JdbcLogicalType( - String identifier, - Schema.FieldType argumentType, - Schema.FieldType baseType, - Object argument) { - this.identifier = identifier; - this.argumentType = argumentType; - this.baseType = baseType; - this.argument = argument; - } - - @Override - public String getIdentifier() { - return identifier; - } - - @Override - public FieldType getArgumentType() { - return argumentType; - } - - @Override - @SuppressWarnings("TypeParameterUnusedInFormals") - public ArgumentT getArgument() { - return (ArgumentT) argument; - } - - @Override - public Schema.FieldType getBaseType() { - return baseType; - } - - @Override - public T toBaseType(T input) { - return input; - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (!(o instanceof JdbcLogicalType)) { - return false; - } - JdbcLogicalType that = (JdbcLogicalType) o; - return Objects.equals(identifier, that.identifier) - && Objects.equals(baseType, that.baseType) - && Objects.equals(argument, that.argument); - } - - @Override - public int hashCode() { - return Objects.hash(identifier, baseType, argument); - } - } } diff --git a/sdks/python/apache_beam/io/external/xlang_jdbcio_it_test.py b/sdks/python/apache_beam/io/external/xlang_jdbcio_it_test.py index ed8745ec2ac1..54a473d1b52b 100644 --- a/sdks/python/apache_beam/io/external/xlang_jdbcio_it_test.py +++ b/sdks/python/apache_beam/io/external/xlang_jdbcio_it_test.py @@ -17,6 +17,7 @@ # pytype: skip-file +import datetime import logging import time import typing @@ -60,7 +61,8 @@ "JdbcTestRow", [("f_id", int), ("f_float", float), ("f_char", str), ("f_varchar", str), ("f_bytes", bytes), ("f_varbytes", bytes), ("f_timestamp", Timestamp), - ("f_decimal", Decimal)], + ("f_decimal", Decimal), ("f_date", datetime.date), + ("f_time", datetime.time)], ) coders.registry.register_coder(JdbcTestRow, coders.RowCoder) @@ -132,7 +134,7 @@ def test_xlang_jdbc_write_read(self, database): "f_float DOUBLE PRECISION, " + "f_char CHAR(10), " + "f_varchar VARCHAR(10), " + f"f_bytes {binary_type[0]}, " + f"f_varbytes {binary_type[1]}, " + "f_timestamp TIMESTAMP(3), " + - "f_decimal DECIMAL(10, 2))") + "f_decimal DECIMAL(10, 2), " + "f_date DATE, " + "f_time TIME(3))") inserted_rows = [ JdbcTestRow( i, @@ -144,7 +146,11 @@ def test_xlang_jdbc_write_read(self, database): # In alignment with Java Instant which supports milli precision. Timestamp.of(seconds=round(time.time(), 3)), # Test both positive and negative numbers. - Decimal(f'{i-1}.23')) for i in range(ROW_COUNT) + Decimal(f'{i-1}.23'), + # Test both date before or after EPOCH + datetime.date(1969 + i, i % 12 + 1, i % 31 + 1), + datetime.time(i % 24, i % 60, i % 60, (i * 1000) % 1_000_000)) + for i in range(ROW_COUNT) ] expected_row = [] for row in inserted_rows: @@ -163,7 +169,9 @@ def test_xlang_jdbc_write_read(self, database): f_bytes, row.f_bytes, row.f_timestamp, - row.f_decimal)) + row.f_decimal, + row.f_date, + row.f_time)) with TestPipeline() as p: p.not_use_test_runner_api = True diff --git a/sdks/python/apache_beam/io/jdbc.py b/sdks/python/apache_beam/io/jdbc.py index f8f24ddeb8d2..903b0d1b0fef 100644 --- a/sdks/python/apache_beam/io/jdbc.py +++ b/sdks/python/apache_beam/io/jdbc.py @@ -86,6 +86,7 @@ # pytype: skip-file +import datetime import typing import numpy as np @@ -94,7 +95,10 @@ from apache_beam.transforms.external import BeamJarExpansionService from apache_beam.transforms.external import ExternalTransform from apache_beam.transforms.external import NamedTupleBasedPayloadBuilder +from apache_beam.typehints.schemas import LogicalType +from apache_beam.typehints.schemas import MillisInstant from apache_beam.typehints.schemas import typing_to_runner_api +from apache_beam.utils.timestamp import Timestamp __all__ = [ 'WriteToJdbc', @@ -355,3 +359,99 @@ def __init__( ), expansion_service or default_io_expansion_service(classpath), ) + + +@LogicalType.register_logical_type +class JdbcDateType(LogicalType[datetime.date, MillisInstant, str]): + """ + For internal use only; no backwards-compatibility guarantees. + + Support of Legacy JdbcIO DATE logical type. Deemed to change when Java JDBCIO + has been migrated to Beam portable logical types. + """ + def __init__(self, argument=""): + pass + + @classmethod + def representation_type(cls): + # type: () -> type + return Timestamp + + @classmethod + def urn(cls): + return "beam:logical_type:javasdk_date:v1" + + @classmethod + def language_type(cls): + return datetime.date + + def to_representation_type(self, value): + # type: (datetime.date) -> Timestamp + return Timestamp.from_utc_datetime( + datetime.datetime.combine( + value, datetime.datetime.min.time(), tzinfo=datetime.timezone.utc)) + + def to_language_type(self, value): + # type: (Timestamp) -> datetime.date + + return value.to_utc_datetime().date() + + @classmethod + def argument_type(cls): + return str + + def argument(self): + return "" + + @classmethod + def _from_typing(cls, typ): + return cls() + + +@LogicalType.register_logical_type +class JdbcTimeType(LogicalType[datetime.time, MillisInstant, str]): + """ + For internal use only; no backwards-compatibility guarantees. + + Support of Legacy JdbcIO TIME logical type. . Deemed to change when Java + JDBCIO has been migrated to Beam portable logical types. + """ + def __init__(self, argument=""): + pass + + @classmethod + def representation_type(cls): + # type: () -> type + return Timestamp + + @classmethod + def urn(cls): + return "beam:logical_type:javasdk_time:v1" + + @classmethod + def language_type(cls): + return datetime.time + + def to_representation_type(self, value): + # type: (datetime.date) -> Timestamp + return Timestamp.from_utc_datetime( + datetime.datetime.combine( + datetime.datetime.utcfromtimestamp(0), + value, + tzinfo=datetime.timezone.utc)) + + def to_language_type(self, value): + # type: (Timestamp) -> datetime.date + + return value.to_utc_datetime().time() + + @classmethod + def argument_type(cls): + return str + + def argument(self): + return "" + + @classmethod + def _from_typing(cls, typ): + return cls() From 1fc54fdfb91faf13d7ab5b35a83fa5da2a30730a Mon Sep 17 00:00:00 2001 From: Vitaly Terentyev Date: Mon, 18 Sep 2023 19:35:58 +0400 Subject: [PATCH 043/105] Add Load Tests CoGBK Dataflow Streaming Java github action (#28372) * remove sonarqube jenkins report * Add Load Tests CoGBK Dataflow Streaming Java github action * Change cron * Update workflow --------- Co-authored-by: Vlado Djerek --- .github/workflows/README.md | 1 + ...oadTests_Java_CoGBK_Dataflow_Streaming.yml | 126 ++++++++++++++++++ ...g_CoGBK_Java_Streaming_2GB_MultipleKey.txt | 34 +++++ ...BK_Java_Streaming_2GB_Reiteration_10KB.txt | 34 +++++ ...GBK_Java_Streaming_2GB_Reiteration_2MB.txt | 34 +++++ ...fig_CoGBK_Java_Streaming_2GB_SingleKey.txt | 34 +++++ 6 files changed, 263 insertions(+) create mode 100644 .github/workflows/beam_LoadTests_Java_CoGBK_Dataflow_Streaming.yml create mode 100644 .github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_MultipleKey.txt create mode 100644 .github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_Reiteration_10KB.txt create mode 100644 .github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_Reiteration_2MB.txt create mode 100644 .github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_SingleKey.txt diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 9444ba6bcbe9..98138cd9fb4a 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -180,6 +180,7 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex ```Run Python PreCommit (3.8)``` | Workflow name | Matrix | Trigger Phrase | Cron Status | |:-------------:|:------:|:--------------:|:-----------:| +| [ Load Tests CoGBK Dataflow Streaming Java ](https://github.com/apache/beam/actions/workflows/beam_LoadTests_Java_CoGBK_Dataflow_Streaming.yml) | N/A |`Run Load Tests Java CoGBK Dataflow Streaming`| [![.github/workflows/beam_LoadTests_Java_CoGBK_Dataflow_Streaming](https://github.com/apache/beam/actions/workflows/beam_LoadTests_Java_CoGBK_Dataflow_Streaming.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_LoadTests_Java_CoGBK_Dataflow_Streaming.yml) | [ PostCommit BeamMetrics Publish ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_BeamMetrics_Publish.yml) | N/A |`Run Beam Metrics Deployment`| [![.github/workflows/beam_PostCommit_BeamMetrics_Publish.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_BeamMetrics_Publish.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_BeamMetrics_Publish.yml) | [ PostCommit Go ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go.yml) | N/A |`Run Go PostCommit`| [![.github/workflows/beam_PostCommit_Go.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go.yml) | | [ PostCommit Go Dataflow ARM](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_Dataflow_ARM.yml) | N/A |`Run Go PostCommit Dataflow ARM`| [![.github/workflows/beam_PostCommit_Go_Dataflow_ARM.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_Dataflow_ARM.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_Dataflow_ARM.yml) |[label](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_Dataflow_ARM.yml) diff --git a/.github/workflows/beam_LoadTests_Java_CoGBK_Dataflow_Streaming.yml b/.github/workflows/beam_LoadTests_Java_CoGBK_Dataflow_Streaming.yml new file mode 100644 index 000000000000..9bbaaaa3404a --- /dev/null +++ b/.github/workflows/beam_LoadTests_Java_CoGBK_Dataflow_Streaming.yml @@ -0,0 +1,126 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Load Tests CoGBK Dataflow Streaming Java + +on: + issue_comment: + types: [created] + schedule: + - cron: '50 10 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login }}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_LoadTests_Java_CoGBK_Dataflow_Streaming: + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Load Tests Java CoGBK Dataflow Streaming' + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 240 + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + strategy: + matrix: + job_name: [ "beam_LoadTests_Java_CoGBK_Dataflow_Streaming" ] + job_phrase: ["Run Load Tests Java CoGBK Dataflow Streaming"] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Prepare configs + id: set_configs + shell: bash + run: | + CURCONFIG=$(grep -v "^#.*" ./.github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_SingleKey.txt | tr '\n' ' ') + echo "prepared_config_1=$CURCONFIG" >> $GITHUB_OUTPUT + CURCONFIG=$(grep -v "^#.*" ./.github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_MultipleKey.txt | tr '\n' ' ') + echo "prepared_config_2=$CURCONFIG" >> $GITHUB_OUTPUT + CURCONFIG=$(grep -v "^#.*" ./.github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_Reiteration_10KB.txt | tr '\n' ' ') + echo "prepared_config_3=$CURCONFIG" >> $GITHUB_OUTPUT + CURCONFIG=$(grep -v "^#.*" ./.github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_Reiteration_2MB.txt | tr '\n' ' ') + echo "prepared_config_4=$CURCONFIG" >> $GITHUB_OUTPUT + - name: run CoGBK Dataflow Streaming Java Load Test 1 (single key) + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:java:testing:load-tests:run + arguments: | + -PloadTest.mainClass=org.apache.beam.sdk.loadtests.CoGroupByKeyLoadTest \ + -Prunner=:runners:google-cloud-dataflow-java \ + '-PloadTest.args=${{ steps.set_configs.outputs.prepared_config_1 }}' \ + - name: run CoGBK Dataflow Streaming Java Load Test 2 (multiple key) + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:java:testing:load-tests:run + arguments: | + -PloadTest.mainClass=org.apache.beam.sdk.loadtests.CoGroupByKeyLoadTest \ + -Prunner=:runners:google-cloud-dataflow-java \ + '-PloadTest.args=${{ steps.set_configs.outputs.prepared_config_2 }}' \ + - name: run CoGBK Dataflow Streaming Java Load Test 3 (reiteration 10KB value) + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:java:testing:load-tests:run + arguments: | + -PloadTest.mainClass=org.apache.beam.sdk.loadtests.CoGroupByKeyLoadTest \ + -Prunner=:runners:google-cloud-dataflow-java \ + '-PloadTest.args=${{ steps.set_configs.outputs.prepared_config_3 }}' \ + - name: run CoGBK Dataflow Streaming Java Load Test 4 (reiteration 2MB value) + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:java:testing:load-tests:run + arguments: | + -PloadTest.mainClass=org.apache.beam.sdk.loadtests.CoGroupByKeyLoadTest \ + -Prunner=:runners:google-cloud-dataflow-java \ + '-PloadTest.args=${{ steps.set_configs.outputs.prepared_config_4 }}' \ + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_MultipleKey.txt b/.github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_MultipleKey.txt new file mode 100644 index 000000000000..5fd9518bc8d0 --- /dev/null +++ b/.github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_MultipleKey.txt @@ -0,0 +1,34 @@ +############################################################################### +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +--project=apache-beam-testing +--region=us-central1 +--appName=load_tests_Java_Dataflow_streaming_CoGBK_2 +--tempLocation=gs://temp-storage-for-perf-tests/loadtests +--influxMeasurement=java_streaming_cogbk_2 +--publishToInfluxDB=true +--sourceOptions={"numRecords":20000000,"keySizeBytes":10,"valueSizeBytes":90,"numHotKeys":5} +--coSourceOptions={"numRecords":2000000,"keySizeBytes":10,"valueSizeBytes":90,"numHotKeys":1000} +--iterations=1 +--numWorkers=5 +--autoscalingAlgorithm=NONE +--streaming=true +--inputWindowDurationSec=1200 +--coInputWindowDurationSec=1200 +--influxDatabase=beam_test_metrics +--influxHost=http://10.128.0.96:8086 +--runner=DataflowRunner \ No newline at end of file diff --git a/.github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_Reiteration_10KB.txt b/.github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_Reiteration_10KB.txt new file mode 100644 index 000000000000..2840fe75d5af --- /dev/null +++ b/.github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_Reiteration_10KB.txt @@ -0,0 +1,34 @@ +############################################################################### +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +--project=apache-beam-testing +--region=us-central1 +--appName=load_tests_Java_Dataflow_streaming_CoGBK_3 +--tempLocation=gs://temp-storage-for-perf-tests/loadtests +--influxMeasurement=java_streaming_cogbk_3 +--publishToInfluxDB=true +--sourceOptions={"numRecords":2000000,"keySizeBytes":10,"valueSizeBytes":90,"numHotKeys":200000} +--coSourceOptions={"numRecords":2000000,"keySizeBytes":10,"valueSizeBytes":90,"numHotKeys":1000} +--iterations=4 +--numWorkers=5 +--autoscalingAlgorithm=NONE +--streaming=true +--inputWindowDurationSec=1200 +--coInputWindowDurationSec=1200 +--influxDatabase=beam_test_metrics +--influxHost=http://10.128.0.96:8086 +--runner=DataflowRunner \ No newline at end of file diff --git a/.github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_Reiteration_2MB.txt b/.github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_Reiteration_2MB.txt new file mode 100644 index 000000000000..bcc8a36cf31f --- /dev/null +++ b/.github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_Reiteration_2MB.txt @@ -0,0 +1,34 @@ +############################################################################### +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +--project=apache-beam-testing +--region=us-central1 +--appName=load_tests_Java_Dataflow_streaming_CoGBK_4 +--tempLocation=gs://temp-storage-for-perf-tests/loadtests +--influxMeasurement=java_streaming_cogbk_4 +--publishToInfluxDB=true +--sourceOptions={"numRecords":2000000,"keySizeBytes":10,"valueSizeBytes":90,"numHotKeys":1000} +--coSourceOptions={"numRecords":2000000,"keySizeBytes":10,"valueSizeBytes":90,"numHotKeys":1000} +--iterations=4 +--numWorkers=5 +--autoscalingAlgorithm=NONE +--streaming=true +--inputWindowDurationSec=1200 +--coInputWindowDurationSec=1200 +--influxDatabase=beam_test_metrics +--influxHost=http://10.128.0.96:8086 +--runner=DataflowRunner \ No newline at end of file diff --git a/.github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_SingleKey.txt b/.github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_SingleKey.txt new file mode 100644 index 000000000000..afae1a1bd6bf --- /dev/null +++ b/.github/workflows/load-tests-job-configs/config_CoGBK_Java_Streaming_2GB_SingleKey.txt @@ -0,0 +1,34 @@ +############################################################################### +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +--project=apache-beam-testing +--region=us-central1 +--appName=load_tests_Java_Dataflow_streaming_CoGBK_1 +--tempLocation=gs://temp-storage-for-perf-tests/loadtests +--influxMeasurement=java_streaming_cogbk_1 +--publishToInfluxDB=true +--sourceOptions={"numRecords":20000000,"keySizeBytes":10,"valueSizeBytes":90,"numHotKeys":1} +--coSourceOptions={"numRecords":2000000,"keySizeBytes":10,"valueSizeBytes":90,"numHotKeys":1000} +--iterations=1 +--numWorkers=5 +--autoscalingAlgorithm=NONE +--streaming=true +--inputWindowDurationSec=1200 +--coInputWindowDurationSec=1200 +--influxDatabase=beam_test_metrics +--influxHost=http://10.128.0.96:8086 +--runner=DataflowRunner \ No newline at end of file From ffde6888ee096befe7034ae41f3b52bd8a8c98f1 Mon Sep 17 00:00:00 2001 From: Aleksandr Dudko <116064902+aleksandr-dudko@users.noreply.github.com> Date: Mon, 18 Sep 2023 19:38:02 +0400 Subject: [PATCH 044/105] Add GitHub Workflow Replacement for Jenkins job_PostCommit_Python (#28301) * Add GitHub Workflow Replacement for Jenkins job_PostCommit_Python38, job_PostCommit_Python39, job_PostCommit_Python310 and job_PostCommit_Python311 * Add fix for beam_PostCommit_Sickbay_Python * Update name for beam_PostCommit_Sickbay_Python --- .github/workflows/README.md | 1 + .github/workflows/beam_PostCommit_Python.yml | 103 ++++++++++++++++++ .../beam_PostCommit_Sickbay_Python.yml | 16 +-- 3 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/beam_PostCommit_Python.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 98138cd9fb4a..b213a13e70b7 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -222,6 +222,7 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | [ PostCommit Java ValidatesRunner ULR ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml) | N/A |`Run ULR Loopback ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml) | | [ PostCommit PortableJar Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_PortableJar_Flink.yml) | N/A |`Run PortableJar_Flink PostCommit`| [![.github/workflows/beam_PostCommit_PortableJar_Flink.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_PortableJar_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_PortableJar_Flink.yml) | | [ PostCommit PortableJar Spark ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_PortableJar_Spark.yml) | N/A |`Run PortableJar_Spark PostCommit`| [![.github/workflows/beam_PostCommit_PortableJar_Spark.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_PortableJar_Spark.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_PortableJar_Spark.yml) | +| [ PostCommit Python ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python.yml) | ['3.8','3.9','3.10','3.11'] |`Run Python PostCommit (matrix_element)`| [![.github/workflows/beam_PostCommit_Python.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python.yml) | | [ PostCommit Python Examples Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Dataflow.yml) | N/A |`Run Python Examples_Dataflow`| [![.github/workflows/beam_PostCommit_Python_Examples_Dataflow.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Dataflow.yml) | | [ PostCommit Python Examples Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml) | ['3.8','3.9','3.10','3.11'] |`Run Python Examples_Direct (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_Examples_Direct.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Direct.yml) | | [ PostCommit Python Examples Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml) | ['3.8','3.11'] |`Run Python Examples_Flink (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_Examples_Flink.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml) | diff --git a/.github/workflows/beam_PostCommit_Python.yml b/.github/workflows/beam_PostCommit_Python.yml new file mode 100644 index 000000000000..23f322709efb --- /dev/null +++ b/.github/workflows/beam_PostCommit_Python.yml @@ -0,0 +1,103 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Python + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Python: + name: ${{matrix.job_name}} (${{matrix.job_phrase}} ${{matrix.python_version}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 240 + strategy: + fail-fast: false + matrix: + job_name: [beam_PostCommit_Python] + job_phrase: [Run Python PostCommit] + python_version: ['3.8', '3.9', '3.10', '3.11'] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Python PostCommit' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: ${{matrix.python_version}} + - name: Install docker compose + run: | + sudo curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose + sudo chmod +x /usr/local/bin/docker-compose + - name: Set PY_VER_CLEAN + id: set_py_ver_clean + run: | + PY_VER=${{ matrix.python_version }} + PY_VER_CLEAN=${PY_VER//.} + echo "py_ver_clean=$PY_VER_CLEAN" >> $GITHUB_OUTPUT + - name: run PostCommit Python ${{ matrix.python_version }} script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :python${{steps.set_py_ver_clean.outputs.py_ver_clean}}PostCommit + arguments: | + -PuseWheelDistribution \ + -PpythonVersion=${{ matrix.python_version }} \ + env: + CLOUDSDK_CONFIG: ${{ env.KUBELET_GCLOUD_CONFIG_PATH}} + - name: Archive code coverage results + uses: actions/upload-artifact@v3 + with: + name: python-code-coverage-report + path: "**/pytest*.xml" \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Sickbay_Python.yml b/.github/workflows/beam_PostCommit_Sickbay_Python.yml index af5fda81a4ec..c791053520a4 100644 --- a/.github/workflows/beam_PostCommit_Sickbay_Python.yml +++ b/.github/workflows/beam_PostCommit_Sickbay_Python.yml @@ -21,12 +21,12 @@ on: issue_comment: types: [created] schedule: - - cron: '0 */6 * * *' + - cron: '0 0 * * *' workflow_dispatch: # This allows a subsequently queued workflow run to interrupt previous runs concurrency: - group: '${{ github.workflow }} @ ${{ github.sha || github.head_ref || github.ref }}-${{ github.event.sender.login }}-${{ github.event.schedule }}' + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' cancel-in-progress: true #Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event @@ -52,27 +52,29 @@ env: jobs: beam_PostCommit_Sickbay_Python: - name: ${{matrix.job_name}} (${{matrix.job_phrase}} ${{matrix.python_version}}) + name: ${{matrix.job_name}} (${{matrix.job_phrase_1}} ${{matrix.python_version}} ${{matrix.job_phrase_2}}) runs-on: [self-hosted, ubuntu-20.04, main] timeout-minutes: 180 strategy: fail-fast: false matrix: job_name: [beam_PostCommit_Sickbay_Python] - job_phrase: [Run Python PostCommit Sickbay tests] + job_phrase_1: [Run Python] + job_phrase_2: [PostCommit Sickbay] python_version: ['3.8', '3.9', '3.10', '3.11'] if: | github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || - github.event.comment.body == 'Run Python PostCommit Sickbay tests' + (startswith(github.event.comment.body, 'Run Python') && + endswith(github.event.comment.body, 'PostCommit Sickbay')) steps: - uses: actions/checkout@v3 - name: Setup repository uses: ./.github/actions/setup-action with: - comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} + comment_phrase: ${{ matrix.job_phrase_1 }} ${{matrix.python_version}} ${{ matrix.job_phrase_2 }} github_token: ${{ secrets.GITHUB_TOKEN }} - github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) + github_job: ${{ matrix.job_name }} ${{ matrix.job_phrase_1 }} ${{matrix.python_version}} ${{ matrix.job_phrase_2 }} - name: Install Python uses: actions/setup-python@v4 with: From de10fbd5e5b6d627e090335a56429fd4c3ec2307 Mon Sep 17 00:00:00 2001 From: Aleksandr Dudko <116064902+aleksandr-dudko@users.noreply.github.com> Date: Mon, 18 Sep 2023 19:38:18 +0400 Subject: [PATCH 045/105] Add GitHub Workflow Replacement for Jenkins job_PostCommit_Java_PVR_* (#28412) * Add GitHub Workflow Replacement for Jenkins job_PostCommit_Java_PVR_Flink_Streaming, job_PostCommit_Java_PVR_Samza, job_PostCommit_Java_PVR_Spark3_Streaming, job_PostCommit_Java_PVR_Spark_Batch * Fix for PostCommit Java Sickbay --- .github/workflows/README.md | 6 +- ...am_PostCommit_Java_PVR_Flink_Streaming.yml | 77 ++++++++++++++++ .../beam_PostCommit_Java_PVR_Samza.yml | 90 +++++++++++++++++++ ...m_PostCommit_Java_PVR_Spark3_Streaming.yml | 88 ++++++++++++++++++ .../beam_PostCommit_Java_PVR_Spark_Batch.yml | 90 +++++++++++++++++++ .../beam_PostCommit_Java_Sickbay.yml | 2 +- 6 files changed, 351 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/beam_PostCommit_Java_PVR_Flink_Streaming.yml create mode 100644 .github/workflows/beam_PostCommit_Java_PVR_Samza.yml create mode 100644 .github/workflows/beam_PostCommit_Java_PVR_Spark3_Streaming.yml create mode 100644 .github/workflows/beam_PostCommit_Java_PVR_Spark_Batch.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index b213a13e70b7..fffb5b09bf05 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -203,7 +203,10 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | [ PostCommit Java Jpms Direct Java17 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml) | N/A |`Run Jpms Direct Java 17 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java17](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml) | | [ PostCommit Java Jpms Flink Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml) | N/A |`Run Jpms Flink Java 11 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Flink_Java11](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml) | | [ PostCommit Java Jpms Spark Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml) | N/A |`Run Jpms Spark Java 11 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Spark_Java11](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml) | -| [ PostCommit Website Test](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Test.yml) | N/A |`Run Full Website Test`| [![.github/workflows/beam_PostCommit_Website_Test](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Test.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Test.yml) | +| [ PostCommit Java PVR Flink Streaming ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Flink_Streaming.yml) | N/A |`Run Java Flink PortableValidatesRunner Streaming`| [![PostCommit Java PVR Flink Streaming](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Flink_Streaming.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Flink_Streaming.yml) | +| [ PostCommit Java PVR Samza ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Samza.yml) | N/A |`Run Java Samza PortableValidatesRunner`| [![PostCommit Java PVR Samza](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Samza.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Samza.yml) | +| [ PostCommit Java PVR Spark3 Streaming ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Spark3_Streaming.yml) | N/A |`Run Java Spark v3 PortableValidatesRunner Streaming`| [![PostCommit Java PVR Spark3 Streaming](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Spark3_Streaming.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Spark3_Streaming.yml) | +| [ PostCommit Java PVR Spark Batch ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Spark_Batch.yml) | N/A |`Run Java Spark PortableValidatesRunner Batch`| [![PostCommit Java PVR Spark Batch](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Spark_Batch.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Spark_Batch.yml) | | [ PostCommit Java Sickbay ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Sickbay.yml) | N/A |`Run Java Sickbay`| [![.github/workflows/beam_PostCommit_Java_Sickbay.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Sickbay.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Sickbay.yml) | | [ PostCommit Java ValidatesRunner Dataflow JavaVersions ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Java.yml) | ['11','17'] |`Run Dataflow ValidatesRunner Java (matrix_element)`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Java.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Java.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Java.yml) | | [ PostCommit Java ValidatesRunner Dataflow Streaming ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml) | N/A |`Run Dataflow Streaming ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml) | @@ -241,6 +244,7 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | [ PostCommit SQL ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_SQL.yml) | N/A |`Run SQL PostCommit`| [![.github/workflows/beam_PostCommit_SQL.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_SQL.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_SQL.yml) | | [ PostCommit TransformService Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_TransformService_Direct.yml) | N/A |`Run TransformService_Direct PostCommit`| [![.github/workflows/beam_PostCommit_TransformService_Direct.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_TransformService_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_TransformService_Direct.yml) | [ PostCommit Website Publish ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml) | N/A | N/A | [![.github/workflows/beam_PostCommit_Website_Publish.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Publish.yml) | +| [ PostCommit Website Test](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Test.yml) | N/A |`Run Full Website Test`| [![.github/workflows/beam_PostCommit_Website_Test](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Test.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Website_Test.yml) | | [ PostCommit XVR GoUsingJava Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml) | N/A |`Run XVR_GoUsingJava_Dataflow PostCommit`| [![.github/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml) | | [ PostCommit XVR Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_Direct.yml) | N/A |`Run XVR_Direct PostCommit`| [![.github/workflows/beam_PostCommit_XVR_Direct](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_Direct.yml) | | [ PostCommit XVR Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_Flink.yml) | N/A |`Run XVR_Flink PostCommit`| [![.github/workflows/beam_PostCommit_XVR_Flink](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_XVR_Flink.yml) | diff --git a/.github/workflows/beam_PostCommit_Java_PVR_Flink_Streaming.yml b/.github/workflows/beam_PostCommit_Java_PVR_Flink_Streaming.yml new file mode 100644 index 000000000000..a649adc1ea08 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_PVR_Flink_Streaming.yml @@ -0,0 +1,77 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java PVR Flink Streaming + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_PVR_Flink_Streaming: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 120 + strategy: + matrix: + job_name: [beam_PostCommit_Java_PVR_Flink_Streaming] + job_phrase: [Run Java Flink PortableValidatesRunner Streaming] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Java Flink PortableValidatesRunner Streaming' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: run PostCommit Java Flink PortableValidatesRunner Streaming script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: runners:flink:1.15:job-server:validatesPortableRunnerStreaming diff --git a/.github/workflows/beam_PostCommit_Java_PVR_Samza.yml b/.github/workflows/beam_PostCommit_Java_PVR_Samza.yml new file mode 100644 index 000000000000..f4defac94b87 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_PVR_Samza.yml @@ -0,0 +1,90 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java PVR Samza + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_PVR_Samza: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 120 + strategy: + matrix: + job_name: [beam_PostCommit_Java_PVR_Samza] + job_phrase: [Run Java Samza PortableValidatesRunner] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Java Samza PortableValidatesRunner' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: run PostCommit Java Samza script + env: + CLOUDSDK_CONFIG: ${{ env.KUBELET_GCLOUD_CONFIG_PATH}} + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:samza:job-server:validatesPortableRunner + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_PVR_Spark3_Streaming.yml b/.github/workflows/beam_PostCommit_Java_PVR_Spark3_Streaming.yml new file mode 100644 index 000000000000..dfbce38b1f6f --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_PVR_Spark3_Streaming.yml @@ -0,0 +1,88 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java PVR Spark3 Streaming + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_PVR_Spark3_Streaming: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 120 + strategy: + matrix: + job_name: [beam_PostCommit_Java_PVR_Spark3_Streaming] + job_phrase: [Run Java Spark v3 PortableValidatesRunner Streaming] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Java Spark v3 PortableValidatesRunner Streaming' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: run PostCommit Java PortableValidatesRunner Spark3 Streaming script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :runners:spark:3:job-server:validatesPortableRunnerStreaming + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' diff --git a/.github/workflows/beam_PostCommit_Java_PVR_Spark_Batch.yml b/.github/workflows/beam_PostCommit_Java_PVR_Spark_Batch.yml new file mode 100644 index 000000000000..130117da9740 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_PVR_Spark_Batch.yml @@ -0,0 +1,90 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java PVR Spark Batch + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Java_PVR_Spark_Batch: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 240 + strategy: + matrix: + job_name: [beam_PostCommit_Java_PVR_Spark_Batch] + job_phrase: [Run Java Spark PortableValidatesRunner Batch] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Java Spark PortableValidatesRunner Batch' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: run PostCommit Java PortableValidatesRunner Spark Batch script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: | + :runners:spark:3:job-server:validatesPortableRunnerBatch + :runners:spark:3:job-server:validatesPortableRunnerDocker + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Sickbay.yml b/.github/workflows/beam_PostCommit_Java_Sickbay.yml index c3943ac5deff..6851371c42ae 100644 --- a/.github/workflows/beam_PostCommit_Java_Sickbay.yml +++ b/.github/workflows/beam_PostCommit_Java_Sickbay.yml @@ -21,7 +21,7 @@ on: issue_comment: types: [created] schedule: - - cron: '29 6 * * *' + - cron: '0 0 * * *' workflow_dispatch: # This allows a subsequently queued workflow run to interrupt previous runs From 7e830593e61ba1fbff16411b5825bfb4aea53ba2 Mon Sep 17 00:00:00 2001 From: Damon Date: Mon, 18 Sep 2023 16:10:20 +0000 Subject: [PATCH 046/105] Remove TableSchema to JSON conversion. (#28274) * Rethrow error converting TableSchema to JSON * Remove need to parse TableSchema to/from JSON * Remove GenericDatumTransformer's JSON string param * Remove unused TableSchemaFunction --- .../beam/sdk/io/gcp/bigquery/BigQueryIO.java | 40 +++++-------------- .../io/gcp/bigquery/BigQueryIOReadTest.java | 18 +++------ 2 files changed, 15 insertions(+), 43 deletions(-) diff --git a/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIO.java b/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIO.java index 58d769312444..3c006d24d037 100644 --- a/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIO.java +++ b/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIO.java @@ -19,6 +19,7 @@ import static org.apache.beam.sdk.io.gcp.bigquery.BigQueryHelpers.resolveTempLocation; import static org.apache.beam.sdk.io.gcp.bigquery.BigQueryResourceNaming.createTempTableReference; +import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull; import static org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkArgument; import static org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkState; @@ -49,7 +50,6 @@ import com.google.protobuf.DynamicMessage; import com.google.protobuf.Message; import java.io.IOException; -import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.List; @@ -132,13 +132,10 @@ import org.apache.beam.sdk.values.TypeDescriptors; import org.apache.beam.sdk.values.ValueInSingleWindow; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.annotations.VisibleForTesting; -import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Function; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.MoreObjects; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Predicates; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Strings; -import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Supplier; -import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Suppliers; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.ImmutableList; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.Iterables; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.Lists; @@ -649,29 +646,19 @@ public static TypedRead readTableRowsWithSchema() { BigQueryUtils.tableRowFromBeamRow()); } - private static class TableSchemaFunction - implements Serializable, Function<@Nullable String, @Nullable TableSchema> { - @Override - public @Nullable TableSchema apply(@Nullable String input) { - return BigQueryHelpers.fromJsonString(input, TableSchema.class); - } - } - @VisibleForTesting static class GenericDatumTransformer implements DatumReader { private final SerializableFunction parseFn; - private final Supplier tableSchema; + private final TableSchema tableSchema; private GenericDatumReader reader; private org.apache.avro.Schema writerSchema; public GenericDatumTransformer( SerializableFunction parseFn, - String tableSchema, + TableSchema tableSchema, org.apache.avro.Schema writer) { this.parseFn = parseFn; - this.tableSchema = - Suppliers.memoize( - Suppliers.compose(new TableSchemaFunction(), Suppliers.ofInstance(tableSchema))); + this.tableSchema = tableSchema; this.writerSchema = writer; this.reader = new GenericDatumReader<>(this.writerSchema); } @@ -689,7 +676,7 @@ public void setSchema(org.apache.avro.Schema schema) { @Override public T read(T reuse, Decoder in) throws IOException { GenericRecord record = (GenericRecord) this.reader.read(reuse, in); - return parseFn.apply(new SchemaAndRecord(record, this.tableSchema.get())); + return parseFn.apply(new SchemaAndRecord(record, this.tableSchema)); } } @@ -721,16 +708,9 @@ public static TypedRead read(SerializableFunction par .setDatumReaderFactory( (SerializableFunction>) input -> { - try { - String jsonTableSchema = BigQueryIO.JSON_FACTORY.toString(input); - return (AvroSource.DatumReaderFactory) - (writer, reader) -> - new GenericDatumTransformer<>(parseFn, jsonTableSchema, writer); - } catch (IOException e) { - LOG.warn( - String.format("Error while converting table schema %s to JSON!", input), e); - return null; - } + TableSchema safeInput = checkStateNotNull(input); + return (AvroSource.DatumReaderFactory) + (writer, reader) -> new GenericDatumTransformer<>(parseFn, safeInput, writer); }) // TODO: Remove setParseFn once https://github.com/apache/beam/issues/21076 is fixed. .setParseFn(parseFn) @@ -3386,9 +3366,7 @@ private WriteResult expandTyped( @SuppressWarnings({"unchecked", "nullness"}) Descriptors.Descriptor descriptor = (Descriptors.Descriptor) - org.apache.beam.sdk.util.Preconditions.checkStateNotNull( - writeProtoClass.getMethod("getDescriptor")) - .invoke(null); + checkStateNotNull(writeProtoClass.getMethod("getDescriptor")).invoke(null); TableSchema tableSchema = TableRowToStorageApiProto.protoSchemaToTableSchema( TableRowToStorageApiProto.tableSchemaFromDescriptor(descriptor)); diff --git a/sdks/java/io/google-cloud-platform/src/test/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIOReadTest.java b/sdks/java/io/google-cloud-platform/src/test/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIOReadTest.java index bc75ba8bd9ba..e274a8ac68ef 100644 --- a/sdks/java/io/google-cloud-platform/src/test/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIOReadTest.java +++ b/sdks/java/io/google-cloud-platform/src/test/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIOReadTest.java @@ -19,6 +19,7 @@ import static org.apache.beam.sdk.io.gcp.bigquery.BigQueryResourceNaming.createTempTableReference; import static org.apache.beam.sdk.transforms.display.DisplayDataMatchers.hasDisplayItem; +import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.junit.Assert.assertEquals; @@ -143,18 +144,11 @@ public void evaluate() throws Throwable { private SerializableFunction> datumReaderFactoryFn = - (SerializableFunction>) - input -> { - try { - String jsonSchema = BigQueryIO.JSON_FACTORY.toString(input); - return (AvroSource.DatumReaderFactory) - (writer, reader) -> - new BigQueryIO.GenericDatumTransformer<>( - BigQueryIO.TableRowParser.INSTANCE, jsonSchema, writer); - } catch (IOException e) { - return null; - } - }; + input -> + (AvroSource.DatumReaderFactory) + (writer, reader) -> + new BigQueryIO.GenericDatumTransformer<>( + BigQueryIO.TableRowParser.INSTANCE, checkStateNotNull(input), writer); private static class MyData implements Serializable { private String name; From 99743347470171f9785579aa1db357eb464043ac Mon Sep 17 00:00:00 2001 From: Vlado Djerek Date: Mon, 18 Sep 2023 18:13:20 +0200 Subject: [PATCH 047/105] migrate CancelStaleDataflowJobs to GitHub (#28408) * migrate CancelStaleDataflowJobs to GitHub * fix and readme * fix auth step --- .github/workflows/README.md | 1 + .../beam_CancelStaleDataflowJobs.yml | 84 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 .github/workflows/beam_CancelStaleDataflowJobs.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index fffb5b09bf05..51375a191cae 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -323,3 +323,4 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | [ PreCommit GoPortable ](https://github.com/apache/beam/actions/workflows/beam_PreCommit_GoPortable.yml) | N/A |`Run GoPortable PreCommit`| [![.github/workflows/beam_PreCommit_GoPortable.yml](https://github.com/apache/beam/actions/workflows/beam_PreCommit_GoPortable.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PreCommit_GoPortable.yml) | | [ PreCommit Kotlin Examples ](https://github.com/apache/beam/actions/workflows/beam_PreCommit_Kotlin_Examples.yml) | N/A | `Run Kotlin_Examples PreCommit` | [![.github/workflows/beam_PreCommit_Kotlin_Examples.yml](https://github.com/apache/beam/actions/workflows/beam_PreCommit_Kotlin_Examples.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PreCommit_Kotlin_Examples.yml) | | [ PreCommit Portable Python ](https://github.com/apache/beam/actions/workflows/beam_PreCommit_Portable_Python.yml) | ['3.8','3.11'] | `Run Portable_Python PreCommit` | [![.github/workflows/beam_PreCommit_Portable_Python.yml](https://github.com/apache/beam/actions/workflows/beam_PreCommit_Portable_Python.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PreCommit_Portable_Python.yml) | +| [ Cancel Stale Dataflow Jobs ](https://github.com/apache/beam/actions/workflows/beam_CancelStaleDataflowJobs.yml) | N/A | `Run Cancel Stale Dataflow Jobs` | [![.github/workflows/beam_CancelStaleDataflowJobs.yml](https://github.com/apache/beam/actions/workflows/beam_CancelStaleDataflowJobs.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_CancelStaleDataflowJobs.yml) | diff --git a/.github/workflows/beam_CancelStaleDataflowJobs.yml b/.github/workflows/beam_CancelStaleDataflowJobs.yml new file mode 100644 index 000000000000..07ffbe1260e8 --- /dev/null +++ b/.github/workflows/beam_CancelStaleDataflowJobs.yml @@ -0,0 +1,84 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: Cancel Stale Dataflow Jobs + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */4 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +jobs: + beam_CancelStaleDataflowJobs: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 120 + strategy: + matrix: + job_name: [beam_CancelStaleDataflowJobs] + job_phrase: [Run Cancel Stale Dataflow Jobs] + if: | + github.event_name == 'schedule' || + github.event_name == 'workflow_dispatch' || + github.event.comment.body == 'Run Cancel Stale Dataflow Jobs' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Authenticate on GCP + id: auth + uses: google-github-actions/auth@v1 + with: + credentials_json: ${{ secrets.GCP_SA_KEY }} + project_id: ${{ secrets.GCP_PROJECT_ID }} + - name: run cancel stale dataflow jobs + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :beam-test-tools:cancelStaleDataflowJobs + From 90059bdbc57980f236c1e2a8d173d3b44e3244d5 Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Mon, 18 Sep 2023 10:13:41 -0700 Subject: [PATCH 048/105] mypy fix --- sdks/python/apache_beam/runners/render_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdks/python/apache_beam/runners/render_test.py b/sdks/python/apache_beam/runners/render_test.py index 27961e34ed5f..67e7afc1c7b9 100644 --- a/sdks/python/apache_beam/runners/render_test.py +++ b/sdks/python/apache_beam/runners/render_test.py @@ -90,7 +90,7 @@ def setUpClass(cls): cls._dot_installed = True def setUp(self) -> None: - if not self._dot_installed: + if not self._dot_installed: # type: ignore[attr-defined] self.skipTest('dot executable not installed') def test_run_portable_pipeline(self): From b6f432dbaadc7b4c5ac9fd957964059a63a1d91b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 10:14:14 -0700 Subject: [PATCH 049/105] Bump google.golang.org/api from 0.140.0 to 0.141.0 in /sdks (#28463) Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.140.0 to 0.141.0. - [Release notes](https://github.com/googleapis/google-api-go-client/releases) - [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md) - [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.140.0...v0.141.0) --- updated-dependencies: - dependency-name: google.golang.org/api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdks/go.mod | 2 +- sdks/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sdks/go.mod b/sdks/go.mod index 3cf0b4eb5c8b..e28e54896b87 100644 --- a/sdks/go.mod +++ b/sdks/go.mod @@ -57,7 +57,7 @@ require ( golang.org/x/sync v0.3.0 golang.org/x/sys v0.12.0 golang.org/x/text v0.13.0 - google.golang.org/api v0.140.0 + google.golang.org/api v0.141.0 google.golang.org/genproto v0.0.0-20230821184602-ccc8af3d0e93 google.golang.org/grpc v1.58.1 google.golang.org/protobuf v1.31.0 diff --git a/sdks/go.sum b/sdks/go.sum index b53fdd5c3f69..f60c5c87fb1a 100644 --- a/sdks/go.sum +++ b/sdks/go.sum @@ -647,8 +647,8 @@ google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.140.0 h1:CaXNdYOH5oQQI7l6iKTHHiMTdxZca4/02hRg2U8c2hM= -google.golang.org/api v0.140.0/go.mod h1:aGbCiFgtwb2P6badchFbSBUurV6oR5d50Af4iNJtDdI= +google.golang.org/api v0.141.0 h1:Df6vfMgDoIM6ss0m7H4MPwFwY87WNXHfBIda/Bmfl4E= +google.golang.org/api v0.141.0/go.mod h1:iZqLkdPlXKyG0b90eu6KxVSE4D/ccRF2e/doKD2CnQQ= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= From 0d37c6f45d810f01907bdbcc424b621185a0033f Mon Sep 17 00:00:00 2001 From: Robert Burke Date: Mon, 18 Sep 2023 11:18:36 -0700 Subject: [PATCH 050/105] [#28187] Add standalone prism validates runner precommit (#28487) * Add container using standalone prism go precommit. * Add GoPrism precommit action. * Don't serve prism UI by default. --------- Co-authored-by: lostluck <13907733+lostluck@users.noreply.github.com> --- .github/workflows/beam_PreCommit_GoPrism.yml | 89 +++++++++++++++++++ build.gradle.kts | 4 + sdks/go/README.md | 1 + sdks/go/cmd/prism/prism.go | 10 ++- .../runners/prism/internal/environments.go | 16 ++-- .../beam/runners/prism/internal/execute.go | 4 + .../runners/prism/internal/jobservices/job.go | 4 + .../runners/prism/internal/worker/worker.go | 11 +-- sdks/go/pkg/beam/runners/prism/prism.go | 6 +- sdks/go/test/build.gradle | 24 +++++ sdks/go/test/integration/integration.go | 4 +- sdks/go/test/run_validatesrunner_tests.sh | 16 ++-- 12 files changed, 164 insertions(+), 25 deletions(-) create mode 100644 .github/workflows/beam_PreCommit_GoPrism.yml diff --git a/.github/workflows/beam_PreCommit_GoPrism.yml b/.github/workflows/beam_PreCommit_GoPrism.yml new file mode 100644 index 000000000000..8091c23792c6 --- /dev/null +++ b/.github/workflows/beam_PreCommit_GoPrism.yml @@ -0,0 +1,89 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: PreCommit GoPrism + +on: + push: + tags: ['v*'] + branches: ['master', 'release-*'] + paths: ['model/**', 'sdks/go.**', 'release/**','.github/workflows/beam_PreCommit_GoPrism.yml'] + pull_request_target: + branches: ['master', 'release-*'] + paths: ['model/**', 'sdks/go.**', 'release/**'] + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +# Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +jobs: + beam_PreCommit_GoPrism: + name: ${{matrix.job_name}} (${{ matrix.job_phrase }}) + runs-on: [self-hosted, ubuntu-20.04, main] + strategy: + matrix: + job_name: [beam_PreCommit_GoPrism] + job_phrase: [Run GoPrism PreCommit] + timeout-minutes: 120 + if: | + github.event_name == 'push' || + github.event_name == 'pull_request_target' || + github.event_name == 'schedule' || + github.event_name == 'workflow_dispatch' || + github.event.comment.body == 'Run GoPrism PreCommit' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Setup self-hosted + uses: ./.github/actions/setup-self-hosted-action + with: + requires-py-39: false + requires-go: false + - name: Run goPrismPreCommit script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :goPrismPreCommit \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 0d23861a495b..7bd847895293 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -443,6 +443,10 @@ tasks.register("goPortablePreCommit") { dependsOn(":sdks:go:test:ulrValidatesRunner") } +tasks.register("goPrismPreCommit") { + dependsOn(":sdks:go:test:prismValidatesRunner") +} + tasks.register("goPostCommitDataflowARM") { dependsOn(":sdks:go:test:dataflowValidatesRunnerARM64") } diff --git a/sdks/go/README.md b/sdks/go/README.md index a3b03c2e6184..7734d58d9eb9 100644 --- a/sdks/go/README.md +++ b/sdks/go/README.md @@ -131,6 +131,7 @@ Executing all unit tests for the SDK is possible from the `\sdks\go` To test your change as Jenkins would execute it from a PR, from the beam root directory, run: * `./gradlew :sdks:go:goTest` executes the unit tests. + * `./gradlew :sdks:go:test:prismValidatesRunner` validates the SDK against the Go Prism runner as a stand alone binary, with containers. * `./gradlew :sdks:go:test:ulrValidatesRunner` validates the SDK against the Portable Python runner. * `./gradlew :sdks:go:test:flinkValidatesRunner` validates the SDK against the Flink runner. diff --git a/sdks/go/cmd/prism/prism.go b/sdks/go/cmd/prism/prism.go index f00a16c9b2d0..804ae0c2ab2d 100644 --- a/sdks/go/cmd/prism/prism.go +++ b/sdks/go/cmd/prism/prism.go @@ -30,6 +30,8 @@ import ( ) var ( + jobPort = flag.Int("job_port", 8073, "specify the job management service port") + webPort = flag.Int("web_port", 8074, "specify the web ui port") jobManagerEndpoint = flag.String("jm_override", "", "set to only stand up a web ui that refers to a seperate JobManagement endpoint") serveHTTP = flag.Bool("serve_http", true, "enable or disable the web ui") ) @@ -37,12 +39,12 @@ var ( func main() { flag.Parse() ctx := context.Background() - cli, err := makeJobClient(ctx, *jobManagerEndpoint) + cli, err := makeJobClient(ctx, prism.Options{Port: *jobPort}, *jobManagerEndpoint) if err != nil { log.Fatalf("error creating job server: %v", err) } if *serveHTTP { - if err := prism.CreateWebServer(ctx, cli, prism.Options{Port: 8074}); err != nil { + if err := prism.CreateWebServer(ctx, cli, prism.Options{Port: *webPort}); err != nil { log.Fatalf("error creating web server: %v", err) } } else { @@ -51,7 +53,7 @@ func main() { } } -func makeJobClient(ctx context.Context, endpoint string) (jobpb.JobServiceClient, error) { +func makeJobClient(ctx context.Context, opts prism.Options, endpoint string) (jobpb.JobServiceClient, error) { if endpoint != "" { clientConn, err := grpc.DialContext(ctx, endpoint, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()) if err != nil { @@ -59,7 +61,7 @@ func makeJobClient(ctx context.Context, endpoint string) (jobpb.JobServiceClient } return jobpb.NewJobServiceClient(clientConn), nil } - cli, err := prism.CreateJobServer(ctx, prism.Options{Port: 8073}) + cli, err := prism.CreateJobServer(ctx, opts) if err != nil { return nil, fmt.Errorf("error creating local job server: %v", err) } diff --git a/sdks/go/pkg/beam/runners/prism/internal/environments.go b/sdks/go/pkg/beam/runners/prism/internal/environments.go index 5830325bd054..d4fb6ad5b3e1 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/environments.go +++ b/sdks/go/pkg/beam/runners/prism/internal/environments.go @@ -129,13 +129,15 @@ func dockerEnvironment(ctx context.Context, logger *slog.Logger, dp *pipepb.Dock envs = append(envs, credEnv) } } - - if rc, err := cli.ImagePull(ctx, dp.GetContainerImage(), dtyp.ImagePullOptions{}); err == nil { - // Copy the output, but discard it so we can wait until the image pull is finished. - io.Copy(io.Discard, rc) - rc.Close() - } else { - logger.Warn("unable to pull image", "error", err) + if _, _, err := cli.ImageInspectWithRaw(ctx, dp.GetContainerImage()); err != nil { + // We don't have a local image, so we should pull it. + if rc, err := cli.ImagePull(ctx, dp.GetContainerImage(), dtyp.ImagePullOptions{}); err == nil { + // Copy the output, but discard it so we can wait until the image pull is finished. + io.Copy(io.Discard, rc) + rc.Close() + } else { + logger.Warn("unable to pull image and it's not local", "error", err) + } } ccr, err := cli.ContainerCreate(ctx, &container.Config{ diff --git a/sdks/go/pkg/beam/runners/prism/internal/execute.go b/sdks/go/pkg/beam/runners/prism/internal/execute.go index e0c67105d451..cf04381b9cbe 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/execute.go +++ b/sdks/go/pkg/beam/runners/prism/internal/execute.go @@ -81,10 +81,14 @@ func RunPipeline(j *jobservices.Job) { // makeWorker creates a worker for that environment. func makeWorker(env string, j *jobservices.Job) (*worker.W, error) { wk := worker.New(j.String()+"_"+env, env) + wk.EnvPb = j.Pipeline.GetComponents().GetEnvironments()[env] + wk.PipelineOptions = j.PipelineOptions() wk.JobKey = j.JobKey() wk.ArtifactEndpoint = j.ArtifactEndpoint() + go wk.Serve() + if err := runEnvironment(j.RootCtx, j, env, wk); err != nil { return nil, fmt.Errorf("failed to start environment %v for job %v: %w", env, j, err) } diff --git a/sdks/go/pkg/beam/runners/prism/internal/jobservices/job.go b/sdks/go/pkg/beam/runners/prism/internal/jobservices/job.go index 87b0ec007bfb..cd302a70fcc0 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/jobservices/job.go +++ b/sdks/go/pkg/beam/runners/prism/internal/jobservices/job.go @@ -94,6 +94,10 @@ func (j *Job) ArtifactEndpoint() string { return j.artifactEndpoint } +func (j *Job) PipelineOptions() *structpb.Struct { + return j.options +} + // ContributeTentativeMetrics returns the datachannel read index, and any unknown monitoring short ids. func (j *Job) ContributeTentativeMetrics(payloads *fnpb.ProcessBundleProgressResponse) (int64, []string) { return j.metrics.ContributeTentativeMetrics(payloads) diff --git a/sdks/go/pkg/beam/runners/prism/internal/worker/worker.go b/sdks/go/pkg/beam/runners/prism/internal/worker/worker.go index 3a862a143b73..f33ff178c46d 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/worker/worker.go +++ b/sdks/go/pkg/beam/runners/prism/internal/worker/worker.go @@ -43,6 +43,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/encoding/prototext" + "google.golang.org/protobuf/types/known/structpb" ) // A W manages worker environments, sending them work @@ -59,6 +60,7 @@ type W struct { JobKey, ArtifactEndpoint string EnvPb *pipepb.Environment + PipelineOptions *structpb.Struct // Server management lis net.Listener @@ -163,7 +165,6 @@ func (wk *W) GetProvisionInfo(_ context.Context, _ *fnpb.GetProvisionInfoRequest } resp := &fnpb.GetProvisionInfoResponse{ Info: &fnpb.ProvisionInfo{ - // TODO: Add the job's Pipeline options // TODO: Include runner capabilities with the per job configuration. RunnerCapabilities: []string{ urns.CapabilityMonitoringInfoShortIDs, @@ -174,14 +175,14 @@ func (wk *W) GetProvisionInfo(_ context.Context, _ *fnpb.GetProvisionInfoRequest Url: wk.ArtifactEndpoint, }, - RetrievalToken: wk.JobKey, - Dependencies: wk.EnvPb.GetDependencies(), - - // TODO add this job's artifact Dependencies + RetrievalToken: wk.JobKey, + Dependencies: wk.EnvPb.GetDependencies(), + PipelineOptions: wk.PipelineOptions, Metadata: map[string]string{ "runner": "prism", "runner_version": core.SdkVersion, + "variant": "test", }, }, } diff --git a/sdks/go/pkg/beam/runners/prism/prism.go b/sdks/go/pkg/beam/runners/prism/prism.go index 0be35ad5cc33..bcb7a3fb689f 100644 --- a/sdks/go/pkg/beam/runners/prism/prism.go +++ b/sdks/go/pkg/beam/runners/prism/prism.go @@ -49,9 +49,9 @@ func Execute(ctx context.Context, p *beam.Pipeline) (beam.PipelineResult, error) s := jobservices.NewServer(0, internal.RunPipeline) *jobopts.Endpoint = s.Endpoint() go s.Serve() - } - if !jobopts.IsLoopback() { - *jobopts.EnvironmentType = "loopback" + if !jobopts.IsLoopback() { + *jobopts.EnvironmentType = "loopback" + } } return universal.Execute(ctx, p) } diff --git a/sdks/go/test/build.gradle b/sdks/go/test/build.gradle index d53491194753..5b39cf81400f 100644 --- a/sdks/go/test/build.gradle +++ b/sdks/go/test/build.gradle @@ -173,6 +173,30 @@ tasks.register("ulrValidatesRunner") { } } +// ValidatesRunner tests for Prism. Runs tests in the integration directory +// with prism in docker mod to validate that the runner behaves as expected. +task prismValidatesRunner { + group = "Verification" + + dependsOn ":sdks:go:test:goBuild" + dependsOn ":sdks:go:container:docker" + dependsOn ":sdks:java:container:java8:docker" + dependsOn ":sdks:java:testing:expansion-service:buildTestExpansionServiceJar" + doLast { + def pipelineOptions = [ // Pipeline options piped directly to Go SDK flags. + "--expansion_jar=test:${project(":sdks:java:testing:expansion-service").buildTestExpansionServiceJar.archivePath}", + ] + def options = [ + "--runner prism", + "--pipeline_opts \"${pipelineOptions.join(' ')}\"", + ] + exec { + executable "sh" + args "-c", "./run_validatesrunner_tests.sh ${options.join(' ')}" + } + } +} + // A method for configuring a cross-language validates runner test task, // intended to be used in calls to createCrossLanguageValidatesRunnerTask. ext.goIoValidatesRunnerTask = { proj, name, scriptOpts, pipelineOpts -> diff --git a/sdks/go/test/integration/integration.go b/sdks/go/test/integration/integration.go index dee161dcb2af..bb7f5275a163 100644 --- a/sdks/go/test/integration/integration.go +++ b/sdks/go/test/integration/integration.go @@ -38,11 +38,11 @@ package integration import ( "fmt" "math/rand" + "os" "regexp" "strings" "testing" "time" - "os" // common runner flag. "github.com/apache/beam/sdks/v2/go/pkg/beam/options/jobopts" @@ -140,6 +140,8 @@ var portableFilters = []string{ } var prismFilters = []string{ + // The prism runner does not yet support cross-language. + "TestXLang.*", // The prism runner does not support the TestStream primitive "TestTestStream.*", // The trigger and pane tests uses TestStream diff --git a/sdks/go/test/run_validatesrunner_tests.sh b/sdks/go/test/run_validatesrunner_tests.sh index c25d59bd57b0..fd4856f25a0f 100755 --- a/sdks/go/test/run_validatesrunner_tests.sh +++ b/sdks/go/test/run_validatesrunner_tests.sh @@ -257,8 +257,10 @@ print(s.getsockname()[1]) s.close() " +TMPDIR=$(mktemp -d) + # Set up environment based on runner. -if [[ "$RUNNER" == "flink" || "$RUNNER" == "spark" || "$RUNNER" == "samza" || "$RUNNER" == "portable" ]]; then +if [[ "$RUNNER" == "flink" || "$RUNNER" == "spark" || "$RUNNER" == "samza" || "$RUNNER" == "portable" || "$RUNNER" == "prism" ]]; then if [[ -z "$ENDPOINT" ]]; then JOB_PORT=$(python3 -c "$SOCKET_SCRIPT") ENDPOINT="localhost:$JOB_PORT" @@ -288,6 +290,10 @@ if [[ "$RUNNER" == "flink" || "$RUNNER" == "spark" || "$RUNNER" == "samza" || "$ python3 \ -m apache_beam.runners.portability.local_job_service_main \ --port $JOB_PORT & + elif [[ "$RUNNER" == "prism" ]]; then + PRISMBIN=$TMPDIR/prismbin + ./sdks/go/run_with_go_version.sh build -o $PRISMBIN sdks/go/cmd/prism/*.go + $PRISMBIN --job_port $JOB_PORT & else echo "Unknown runner: $RUNNER" exit 1; @@ -340,7 +346,6 @@ if [[ "$RUNNER" == "dataflow" ]]; then gcloud --version # ensure gcloud is version 186 or above - TMPDIR=$(mktemp -d) gcloud_ver=$(gcloud -v | head -1 | awk '{print $4}') if [[ "$gcloud_ver" < "186" ]] then @@ -402,6 +407,7 @@ fi ARGS="$ARGS -p $SIMULTANEOUS" # Assemble test arguments and pipeline options. +ARGS="$ARGS -v" ARGS="$ARGS -timeout $TIMEOUT" ARGS="$ARGS --runner=$RUNNER" ARGS="$ARGS --project=$DATAFLOW_PROJECT" @@ -449,9 +455,9 @@ if [[ "$RUNNER" == "dataflow" ]]; then docker rmi $JAVA_CONTAINER:$JAVA_TAG || echo "Failed to remove container" gcloud --quiet container images delete $JAVA_CONTAINER:$JAVA_TAG || echo "Failed to delete container" fi - - # Clean up tempdir - rm -rf $TMPDIR fi +# Clean up tempdir +rm -rf $TMPDIR + exit $TEST_EXIT_CODE From d6068ad66b0a28a6dd628bb8ef48f1e2182acb4b Mon Sep 17 00:00:00 2001 From: Ahmed Abualsaud <65791736+ahmedabu98@users.noreply.github.com> Date: Mon, 18 Sep 2023 18:27:08 +0000 Subject: [PATCH 051/105] Updating Storage API Autosharding documentation to include that it doesn't work on Runner V2 (#28233) * add documentation * doc for python too --- .../content/en/documentation/io/built-in/google-bigquery.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/www/site/content/en/documentation/io/built-in/google-bigquery.md b/website/www/site/content/en/documentation/io/built-in/google-bigquery.md index 24314dc11800..eae98b84d2c1 100644 --- a/website/www/site/content/en/documentation/io/built-in/google-bigquery.md +++ b/website/www/site/content/en/documentation/io/built-in/google-bigquery.md @@ -788,6 +788,8 @@ BigQuery Storage Write API for Python SDK currently has some limitations on supp {{< paragraph class="language-py" >}} **Note:** If you want to run WriteToBigQuery with Storage Write API from the source code, you need to run `./gradlew :sdks:java:io:google-cloud-platform:expansion-service:build` to build the expansion-service jar. If you are running from a released Beam SDK, the jar will already be included. +**Note:** Auto sharding is not currently supported for Python's Storage Write API. + {{< /paragraph >}} #### Exactly-once semantics @@ -877,6 +879,8 @@ explicitly enable this using [`withAutoSharding`](https://beam.apache.org/releas ***Note:*** `STORAGE_WRITE_API` will default to dynamic sharding when `numStorageWriteApiStreams` is set to 0 or is unspecified. + +***Note:*** Auto sharding with `STORAGE_WRITE_API` is supported on Dataflow's legacy runner, but **not** on Runner V2 {{< /paragraph >}} When using `STORAGE_WRITE_API`, the PCollection returned by From 3024ec24006959fe4d3031468f83ff779bafaf87 Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Mon, 18 Sep 2023 11:48:16 -0700 Subject: [PATCH 052/105] Better errors when inputs are omitted. (#28289) It's not always possible to know if a transform consumes inputs, or can act as a root transform (and in fact some may be able to do both depending on their configuration), but when a transform expecting inputs doesn't get them the error can be quite obscure. This adds best-effort checking and a better error in that case. We also allow explicitly setting empty imputs to work around this error (which is where most of the complexity of this change lies). Importantly, sources (no matter their name) are not required to have inputs. --- sdks/python/apache_beam/yaml/yaml_provider.py | 89 +++++++++++++------ .../python/apache_beam/yaml/yaml_transform.py | 84 +++++++++++++---- .../yaml/yaml_transform_scope_test.py | 31 +------ .../apache_beam/yaml/yaml_transform_test.py | 47 ++++++++++ .../yaml/yaml_transform_unit_test.py | 4 + 5 files changed, 180 insertions(+), 75 deletions(-) diff --git a/sdks/python/apache_beam/yaml/yaml_provider.py b/sdks/python/apache_beam/yaml/yaml_provider.py index 6e035811d4b9..382dcfd97dca 100644 --- a/sdks/python/apache_beam/yaml/yaml_provider.py +++ b/sdks/python/apache_beam/yaml/yaml_provider.py @@ -65,6 +65,16 @@ def provided_transforms(self) -> Iterable[str]: """Returns a list of transform type names this provider can handle.""" raise NotImplementedError(type(self)) + def requires_inputs(self, typ: str, args: Mapping[str, Any]) -> bool: + """Returns whether this transform requires inputs. + + Specifically, if this returns True and inputs are not provided than an error + will be thrown. + + This is best-effort, primarily for better and earlier error messages. + """ + return not typ.startswith('Read') + def create_transform( self, typ: str, @@ -129,9 +139,7 @@ def __init__(self, urns, service): def provided_transforms(self): return self._urns.keys() - def create_transform(self, type, args, yaml_create_transform): - if callable(self._service): - self._service = self._service() + def schema_transforms(self): if self._schema_transforms is None: try: self._schema_transforms = { @@ -142,8 +150,19 @@ def create_transform(self, type, args, yaml_create_transform): except Exception: # It's possible this service doesn't vend schema transforms. self._schema_transforms = {} + return self._schema_transforms + + def requires_inputs(self, typ, args): + if self._urns[type] in self.schema_transforms(): + return bool(self.schema_transforms()[self._urns[type]].inputs) + else: + return super().requires_inputs(typ, args) + + def create_transform(self, type, args, yaml_create_transform): + if callable(self._service): + self._service = self._service() urn = self._urns[type] - if urn in self._schema_transforms: + if urn in self.schema_transforms(): return external.SchemaAwareExternalTransform( urn, self._service, rearrange_based_on_discovery=True, **args) else: @@ -359,8 +378,9 @@ def fn_takes_side_inputs(fn): class InlineProvider(Provider): - def __init__(self, transform_factories): + def __init__(self, transform_factories, no_input_transforms=()): self._transform_factories = transform_factories + self._no_input_transforms = set(no_input_transforms) def available(self): return True @@ -377,6 +397,14 @@ def create_transform(self, type, args, yaml_create_transform): def to_json(self): return {'type': "InlineProvider"} + def requires_inputs(self, typ, args): + if typ in self._no_input_transforms: + return False + elif hasattr(self._transform_factories[typ], '_yaml_requires_inputs'): + return self._transform_factories[typ]._yaml_requires_inputs + else: + return super().requires_inputs(typ, args) + class MetaInlineProvider(InlineProvider): def create_transform(self, type, args, yaml_create_transform): @@ -508,30 +536,30 @@ def _parse_window_spec(spec): # TODO: Triggering, etc. return beam.WindowInto(window_fn) - return InlineProvider( - dict({ - 'Create': create, - 'PyMap': lambda fn: beam.Map( - python_callable.PythonCallableWithSource(fn)), - 'PyMapTuple': lambda fn: beam.MapTuple( - python_callable.PythonCallableWithSource(fn)), - 'PyFlatMap': lambda fn: beam.FlatMap( - python_callable.PythonCallableWithSource(fn)), - 'PyFlatMapTuple': lambda fn: beam.FlatMapTuple( - python_callable.PythonCallableWithSource(fn)), - 'PyFilter': lambda keep: beam.Filter( - python_callable.PythonCallableWithSource(keep)), - 'PyTransform': fully_qualified_named_transform, - 'PyToRow': lambda fields: beam.Select( - **{ - name: python_callable.PythonCallableWithSource(fn) - for (name, fn) in fields.items() - }), - 'WithSchema': with_schema, - 'Flatten': Flatten, - 'WindowInto': WindowInto, - 'GroupByKey': beam.GroupByKey, - })) + return InlineProvider({ + 'Create': create, + 'PyMap': lambda fn: beam.Map( + python_callable.PythonCallableWithSource(fn)), + 'PyMapTuple': lambda fn: beam.MapTuple( + python_callable.PythonCallableWithSource(fn)), + 'PyFlatMap': lambda fn: beam.FlatMap( + python_callable.PythonCallableWithSource(fn)), + 'PyFlatMapTuple': lambda fn: beam.FlatMapTuple( + python_callable.PythonCallableWithSource(fn)), + 'PyFilter': lambda keep: beam.Filter( + python_callable.PythonCallableWithSource(keep)), + 'PyTransform': fully_qualified_named_transform, + 'PyToRow': lambda fields: beam.Select( + **{ + name: python_callable.PythonCallableWithSource(fn) + for (name, fn) in fields.items() + }), + 'WithSchema': with_schema, + 'Flatten': Flatten, + 'WindowInto': WindowInto, + 'GroupByKey': beam.GroupByKey, + }, + no_input_transforms=('Create', )) class PypiExpansionService: @@ -639,6 +667,9 @@ def available(self) -> bool: def provided_transforms(self) -> Iterable[str]: return self._transforms.keys() + def requires_inputs(self, typ, args): + return self._underlying_provider.requires_inputs(typ, args) + def create_transform( self, typ: str, diff --git a/sdks/python/apache_beam/yaml/yaml_transform.py b/sdks/python/apache_beam/yaml/yaml_transform.py index 8bee2ccf2b98..da9bf526cd59 100644 --- a/sdks/python/apache_beam/yaml/yaml_transform.py +++ b/sdks/python/apache_beam/yaml/yaml_transform.py @@ -76,6 +76,28 @@ def only_element(xs): return x +# These allow a user to explicitly pass no input to a transform (i.e. use it +# as a root transform) without an error even if the transform is not known to +# handle it. +def explicitly_empty(): + return {'__explicitly_empty__': None} + + +def is_explicitly_empty(io): + return io == explicitly_empty() + + +def is_empty(io): + return not io or is_explicitly_empty(io) + + +def empty_if_explicitly_empty(io): + if is_explicitly_empty(io): + return {} + else: + return io + + class SafeLineLoader(SafeLoader): """A yaml loader that attaches line information to mappings and strings.""" class TaggedString(str): @@ -186,7 +208,7 @@ def followers(self, transform_name): # TODO(yaml): Also trace through outputs and composites. for transform in self._transforms: if transform['type'] != 'composite': - for input in transform.get('input').values(): + for input in empty_if_explicitly_empty(transform['input']).values(): transform_id, _ = self.get_transform_id_and_output_name(input) self._all_followers[transform_id].append(transform['__uuid__']) return self._all_followers[self.get_transform_id(transform_name)] @@ -324,6 +346,12 @@ def create_ptransform(self, spec, input_pcolls): raise ValueError( 'Config for transform at %s must be a mapping.' % identify_object(spec)) + + if (not input_pcolls and not is_explicitly_empty(spec.get('input', {})) and + provider.requires_inputs(spec['type'], config)): + raise ValueError( + f'Missing inputs for transform at {identify_object(spec)}') + try: # pylint: disable=undefined-loop-variable ptransform = provider.create_transform( @@ -402,7 +430,7 @@ def expand_leaf_transform(spec, scope): spec = normalize_inputs_outputs(spec) inputs_dict = { key: scope.get_pcollection(value) - for (key, value) in spec['input'].items() + for (key, value) in empty_if_explicitly_empty(spec['input']).items() } input_type = spec.get('input_type', 'default') if input_type == 'list': @@ -442,10 +470,10 @@ def expand_composite_transform(spec, scope): spec = normalize_inputs_outputs(normalize_source_sink(spec)) inner_scope = Scope( - scope.root, { + scope.root, + { key: scope.get_pcollection(value) - for key, - value in spec['input'].items() + for (key, value) in empty_if_explicitly_empty(spec['input']).items() }, spec['transforms'], yaml_provider.merge_providers( @@ -470,8 +498,7 @@ def expand(inputs): _LOGGER.info("Expanding %s ", identify_object(spec)) return ({ key: scope.get_pcollection(value) - for key, - value in spec['input'].items() + for (key, value) in empty_if_explicitly_empty(spec['input']).items() } or scope.root) | scope.unique_name(spec, None) >> CompositePTransform() @@ -496,12 +523,25 @@ def is_not_output_of_last_transform(new_transforms, value): composite_spec = normalize_inputs_outputs(spec) new_transforms = [] for ix, transform in enumerate(composite_spec['transforms']): - if any(io in transform for io in ('input', 'output', 'input', 'output')): - raise ValueError( - f'Transform {identify_object(transform)} is part of a chain, ' - 'must have implicit inputs and outputs.') + if any(io in transform for io in ('input', 'output')): + if (ix == 0 and 'input' in transform and 'output' not in transform and + is_explicitly_empty(transform['input'])): + # This is OK as source clause sets an explicitly empty input. + pass + else: + raise ValueError( + f'Transform {identify_object(transform)} is part of a chain, ' + 'must have implicit inputs and outputs.') if ix == 0: - transform['input'] = {key: key for key in composite_spec['input'].keys()} + if is_explicitly_empty(transform.get('input', None)): + pass + elif is_explicitly_empty(composite_spec['input']): + transform['input'] = composite_spec['input'] + else: + transform['input'] = { + key: key + for key in composite_spec['input'].keys() + } else: transform['input'] = new_transforms[-1]['__uuid__'] new_transforms.append(transform) @@ -554,6 +594,8 @@ def normalize_source_sink(spec): spec = dict(spec) spec['transforms'] = list(spec.get('transforms', [])) if 'source' in spec: + if 'input' not in spec['source']: + spec['source']['input'] = explicitly_empty() spec['transforms'].insert(0, spec.pop('source')) if 'sink' in spec: spec['transforms'].append(spec.pop('sink')) @@ -567,6 +609,13 @@ def preprocess_source_sink(spec): return spec +def tag_explicit_inputs(spec): + if 'input' in spec and not SafeLineLoader.strip_metadata(spec['input']): + return dict(spec, input=explicitly_empty()) + else: + return spec + + def normalize_inputs_outputs(spec): spec = dict(spec) @@ -611,7 +660,7 @@ def push_windowing_to_roots(spec): scope = LightweightScope(spec['transforms']) consumed_outputs_by_transform = collections.defaultdict(set) for transform in spec['transforms']: - for _, input_ref in transform['input'].items(): + for _, input_ref in empty_if_explicitly_empty(transform['input']).items(): try: transform_id, output = scope.get_transform_id_and_output_name(input_ref) consumed_outputs_by_transform[transform_id].add(output) @@ -620,7 +669,7 @@ def push_windowing_to_roots(spec): pass for transform in spec['transforms']: - if not transform['input'] and 'windowing' not in transform: + if is_empty(transform['input']) and 'windowing' not in transform: transform['windowing'] = spec['windowing'] transform['__consumed_outputs'] = consumed_outputs_by_transform[ transform['__uuid__']] @@ -647,7 +696,7 @@ def preprocess_windowing(spec): spec = push_windowing_to_roots(spec) windowing = spec.pop('windowing') - if spec['input']: + if not is_empty(spec['input']): # Apply the windowing to all inputs by wrapping it in a transform that # first applies windowing and then applies the original transform. original_inputs = spec['input'] @@ -778,7 +827,7 @@ def ensure_errors_consumed(spec): raise ValueError( f'Missing output in error_handling of {identify_object(t)}') to_handle[t['__uuid__'], config['error_handling']['output']] = t - for _, input in t['input'].items(): + for _, input in empty_if_explicitly_empty(t['input']).items(): if input not in spec['input']: consumed.add(scope.get_transform_id_and_output_name(input)) for error_pcoll, t in to_handle.items(): @@ -815,7 +864,7 @@ def preprocess(spec, verbose=False, known_transforms=None): def apply(phase, spec): spec = phase(spec) - if spec['type'] in {'composite', 'chain'}: + if spec['type'] in {'composite', 'chain'} and 'transforms' in spec: spec = dict( spec, transforms=[apply(phase, t) for t in spec['transforms']]) return spec @@ -835,6 +884,7 @@ def ensure_transforms_have_providers(spec): ensure_transforms_have_providers, preprocess_source_sink, preprocess_chain, + tag_explicit_inputs, normalize_inputs_outputs, preprocess_flattened_inputs, ensure_errors_consumed, diff --git a/sdks/python/apache_beam/yaml/yaml_transform_scope_test.py b/sdks/python/apache_beam/yaml/yaml_transform_scope_test.py index 733f47583a7f..85a99623332c 100644 --- a/sdks/python/apache_beam/yaml/yaml_transform_scope_test.py +++ b/sdks/python/apache_beam/yaml/yaml_transform_scope_test.py @@ -88,40 +88,13 @@ def test_create_ptransform(self): spec = ''' transforms: - type: PyMap + input: something config: fn: "lambda x: x*x" ''' scope, spec = self.get_scope_by_spec(p, spec) - result = scope.create_ptransform(spec['transforms'][0], []) - self.assertIsInstance(result, beam.transforms.ParDo) - self.assertEqual(result.label, 'Map(lambda x: x*x)') - - result_annotations = {**result.annotations()} - target_annotations = { - 'yaml_type': 'PyMap', - 'yaml_args': '{"fn": "lambda x: x*x"}', - 'yaml_provider': '{"type": "InlineProvider"}' - } - - # Check if target_annotations is a subset of result_annotations - self.assertDictEqual( - result_annotations, { - **result_annotations, **target_annotations - }) - - def test_create_ptransform_with_inputs(self): - with beam.Pipeline(options=beam.options.pipeline_options.PipelineOptions( - pickle_library='cloudpickle')) as p: - spec = ''' - transforms: - - type: PyMap - config: - fn: "lambda x: x*x" - ''' - scope, spec = self.get_scope_by_spec(p, spec) - - result = scope.create_ptransform(spec['transforms'][0], []) + result = scope.create_ptransform(spec['transforms'][0], ['something']) self.assertIsInstance(result, beam.transforms.ParDo) self.assertEqual(result.label, 'Map(lambda x: x*x)') diff --git a/sdks/python/apache_beam/yaml/yaml_transform_test.py b/sdks/python/apache_beam/yaml/yaml_transform_test.py index 26baebec86e4..993f9ea6639b 100644 --- a/sdks/python/apache_beam/yaml/yaml_transform_test.py +++ b/sdks/python/apache_beam/yaml/yaml_transform_test.py @@ -250,6 +250,51 @@ def test_name_is_ambiguous(self): output: AnotherFilter ''') + def test_empty_inputs_throws_error(self): + with beam.Pipeline(options=beam.options.pipeline_options.PipelineOptions( + pickle_library='cloudpickle')) as p: + with self.assertRaisesRegex(ValueError, + 'Missing inputs for transform at ' + '"EmptyInputOkButYamlDoesntKnow" at line .*'): + _ = p | YamlTransform( + ''' + type: composite + transforms: + - type: PyTransform + name: EmptyInputOkButYamlDoesntKnow + config: + constructor: apache_beam.Impulse + ''') + + def test_empty_inputs_ok_in_source(self): + with beam.Pipeline(options=beam.options.pipeline_options.PipelineOptions( + pickle_library='cloudpickle')) as p: + # Does not throw an error like it does above. + _ = p | YamlTransform( + ''' + type: composite + source: + type: PyTransform + name: EmptyInputOkButYamlDoesntKnow + config: + constructor: apache_beam.Impulse + ''') + + def test_empty_inputs_ok_if_explicit(self): + with beam.Pipeline(options=beam.options.pipeline_options.PipelineOptions( + pickle_library='cloudpickle')) as p: + # Does not throw an error like it does above. + _ = p | YamlTransform( + ''' + type: composite + transforms: + - type: PyTransform + name: EmptyInputOkButYamlDoesntKnow + input: {} + config: + constructor: apache_beam.Impulse + ''') + def test_annotations(self): t = LinearTransform(5, b=100) annotations = t.annotations() @@ -269,6 +314,8 @@ def test_annotations(self): class CreateTimestamped(beam.PTransform): + _yaml_requires_inputs = False + def __init__(self, elements): self._elements = elements diff --git a/sdks/python/apache_beam/yaml/yaml_transform_unit_test.py b/sdks/python/apache_beam/yaml/yaml_transform_unit_test.py index d57a77d326fb..0d89360c6c3d 100644 --- a/sdks/python/apache_beam/yaml/yaml_transform_unit_test.py +++ b/sdks/python/apache_beam/yaml/yaml_transform_unit_test.py @@ -347,6 +347,7 @@ def test_normalize_source_sink(self): expected = ''' transforms: - type: Create + input: {'__explicitly_empty__': null} config: elements: [0,1,2] - type: PyMap @@ -376,6 +377,7 @@ def test_normalize_source_sink_only_source(self): expected = ''' transforms: - type: Create + input: {'__explicitly_empty__': null} config: elements: [0,1,2] - type: PyMap @@ -445,6 +447,7 @@ def test_preprocess_source_sink_composite(self): type: composite transforms: - type: Create + input: {'__explicitly_empty__': null} config: elements: [0,1,2] - type: PyMap @@ -472,6 +475,7 @@ def test_preprocess_source_sink_chain(self): type: chain transforms: - type: Create + input: {'__explicitly_empty__': null} config: elements: [0,1,2] - type: PyMap From cd65aac2be73cd23e04fcede8eb63d94f830bc15 Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Mon, 18 Sep 2023 15:45:08 -0400 Subject: [PATCH 053/105] Fix broken link (#28510) --- .../site/content/en/documentation/sdks/python-dependencies.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/www/site/content/en/documentation/sdks/python-dependencies.md b/website/www/site/content/en/documentation/sdks/python-dependencies.md index a96d722d05c9..09c56adac430 100644 --- a/website/www/site/content/en/documentation/sdks/python-dependencies.md +++ b/website/www/site/content/en/documentation/sdks/python-dependencies.md @@ -32,7 +32,7 @@ Dependencies for your Beam SDK version are listed in `setup.py` in the Beam repo https://raw.githubusercontent.com/apache/beam/v/sdks/python/setup.py ``` -

Replace `<VERSION_NUMBER>` with the major.minor.patch version of the SDK. For example, https://raw.githubusercontent.com/apache/beam/v{{< param release_latest >}}/sdks/python/setup.py will provide the dependencies for the {{< param release_latest >}} release.

+

Replace `<VERSION_NUMBER>` with the major.minor.patch version of the SDK. For example, https://raw.githubusercontent.com/apache/beam/v{{< param release_latest >}}/sdks/python/setup.py will provide the dependencies for the {{< param release_latest >}} release.

2. Review the core dependency list under `REQUIRED_PACKAGES`. From 6d7c6999a01ac4ef59384c6ec90f8366bd8418cb Mon Sep 17 00:00:00 2001 From: Robert Burke Date: Mon, 18 Sep 2023 12:53:49 -0700 Subject: [PATCH 054/105] Fix building prism in action. (#28509) * Fix building prism in action. * test against this branch. * Disable prism http server in test run. * rm override. --------- Co-authored-by: lostluck <13907733+lostluck@users.noreply.github.com> --- sdks/go/test/run_validatesrunner_tests.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sdks/go/test/run_validatesrunner_tests.sh b/sdks/go/test/run_validatesrunner_tests.sh index fd4856f25a0f..60dd0cd97f11 100755 --- a/sdks/go/test/run_validatesrunner_tests.sh +++ b/sdks/go/test/run_validatesrunner_tests.sh @@ -292,8 +292,12 @@ if [[ "$RUNNER" == "flink" || "$RUNNER" == "spark" || "$RUNNER" == "samza" || "$ --port $JOB_PORT & elif [[ "$RUNNER" == "prism" ]]; then PRISMBIN=$TMPDIR/prismbin - ./sdks/go/run_with_go_version.sh build -o $PRISMBIN sdks/go/cmd/prism/*.go - $PRISMBIN --job_port $JOB_PORT & + cd sdks + ./go/run_with_go_version.sh build -o $PRISMBIN go/cmd/prism/*.go + $PRISMBIN \ + --serve_http=false \ + --job_port $JOB_PORT & + cd .. else echo "Unknown runner: $RUNNER" exit 1; From 8871a4e8d2c6c1880ce4b17100882fb4053c78f4 Mon Sep 17 00:00:00 2001 From: caneff Date: Mon, 18 Sep 2023 16:48:37 -0400 Subject: [PATCH 055/105] Remove internal series.append call from frames.py (#28499) --- sdks/python/apache_beam/dataframe/frames.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sdks/python/apache_beam/dataframe/frames.py b/sdks/python/apache_beam/dataframe/frames.py index e2390bda28be..a0a99c12ecdd 100644 --- a/sdks/python/apache_beam/dataframe/frames.py +++ b/sdks/python/apache_beam/dataframe/frames.py @@ -1471,8 +1471,10 @@ def compute_idx(s): index = pd.Index([], dtype=index_dtype) proxy = self._expr.proxy().copy() proxy.index = index - proxy = proxy.append( - pd.Series([1], index=np.asarray(['0']).astype(proxy.index.dtype))) + proxy = pd.concat([ + proxy, + pd.Series([1], index=np.asarray(['0']).astype(proxy.index.dtype)) + ]) idx_func = expressions.ComputedExpression( 'idx_func', From 71c68ca041d15a53e481238ad2d6da11ee6a6820 Mon Sep 17 00:00:00 2001 From: caneff Date: Mon, 18 Sep 2023 18:02:10 -0400 Subject: [PATCH 056/105] Fix numeric_only logic in frames_test for Pandas 2 (#28422) --- .../apache_beam/dataframe/frame_base.py | 22 +++-- .../apache_beam/dataframe/frame_base_test.py | 47 ++++++++++- sdks/python/apache_beam/dataframe/frames.py | 4 +- .../apache_beam/dataframe/frames_test.py | 80 ++++++++++++++----- 4 files changed, 123 insertions(+), 30 deletions(-) diff --git a/sdks/python/apache_beam/dataframe/frame_base.py b/sdks/python/apache_beam/dataframe/frame_base.py index 1da12ececff6..48a4c29d0589 100644 --- a/sdks/python/apache_beam/dataframe/frame_base.py +++ b/sdks/python/apache_beam/dataframe/frame_base.py @@ -500,6 +500,8 @@ def wrap(func): removed_arg_names = removed_args if removed_args is not None else [] + # We would need to add position only arguments if they ever become a thing + # in Pandas (as of 2.1 currently they aren't). base_arg_spec = getfullargspec(unwrap(getattr(base_type, func.__name__))) base_arg_names = base_arg_spec.args # Some arguments are keyword only and we still want to check against those. @@ -514,6 +516,9 @@ def wrap(func): @functools.wraps(func) def wrapper(*args, **kwargs): + if len(args) > len(base_arg_names): + raise TypeError(f"{func.__name__} got too many positioned arguments.") + for name, value in zip(base_arg_names, args): if name in kwargs: raise TypeError( @@ -523,7 +528,7 @@ def wrapper(*args, **kwargs): # Still have to populate these for the Beam function signature. if removed_args: for name in removed_args: - if not name in kwargs: + if name not in kwargs: kwargs[name] = None return func(**kwargs) @@ -646,13 +651,18 @@ def wrap(func): return func base_argspec = getfullargspec(unwrap(getattr(base_type, func.__name__))) - if not base_argspec.defaults: + if not base_argspec.defaults and not base_argspec.kwonlydefaults: return func - arg_to_default = dict( - zip( - base_argspec.args[-len(base_argspec.defaults):], - base_argspec.defaults)) + arg_to_default = {} + if base_argspec.defaults: + arg_to_default.update( + zip( + base_argspec.args[-len(base_argspec.defaults):], + base_argspec.defaults)) + + if base_argspec.kwonlydefaults: + arg_to_default.update(base_argspec.kwonlydefaults) unwrapped_func = unwrap(func) # args that do not have defaults in func, but do have defaults in base diff --git a/sdks/python/apache_beam/dataframe/frame_base_test.py b/sdks/python/apache_beam/dataframe/frame_base_test.py index 2d16d02ba1ea..b3077320720f 100644 --- a/sdks/python/apache_beam/dataframe/frame_base_test.py +++ b/sdks/python/apache_beam/dataframe/frame_base_test.py @@ -72,7 +72,7 @@ def add_one(frame): def test_args_to_kwargs(self): class Base(object): - def func(self, a=1, b=2, c=3): + def func(self, a=1, b=2, c=3, *, kw_only=4): pass class Proxy(object): @@ -87,6 +87,9 @@ def func(self, **kwargs): self.assertEqual(proxy.func(2, 4, 6), {'a': 2, 'b': 4, 'c': 6}) self.assertEqual(proxy.func(2, c=6), {'a': 2, 'c': 6}) self.assertEqual(proxy.func(c=6, a=2), {'a': 2, 'c': 6}) + self.assertEqual(proxy.func(2, kw_only=20), {'a': 2, 'kw_only': 20}) + with self.assertRaises(TypeError): # got too many positioned arguments + proxy.func(2, 4, 6, 8) def test_args_to_kwargs_populates_defaults(self): class Base(object): @@ -129,6 +132,48 @@ def func_removed_args(self, a, c, **kwargs): proxy.func_removed_args() self.assertEqual(proxy.func_removed_args(12, d=100), {'a': 12, 'd': 100}) + def test_args_to_kwargs_populates_default_handles_kw_only(self): + class Base(object): + def func(self, a, b=2, c=3, *, kw_only=4): + pass + + class ProxyUsesKwOnly(object): + @frame_base.args_to_kwargs(Base) + @frame_base.populate_defaults(Base) + def func(self, a, kw_only, **kwargs): + return dict(kwargs, a=a, kw_only=kw_only) + + proxy = ProxyUsesKwOnly() + + # pylint: disable=too-many-function-args,no-value-for-parameter + with self.assertRaises(TypeError): # missing 1 required positional argument + proxy.func() + + self.assertEqual(proxy.func(100), {'a': 100, 'kw_only': 4}) + self.assertEqual( + proxy.func(2, 4, 6, kw_only=8), { + 'a': 2, 'b': 4, 'c': 6, 'kw_only': 8 + }) + with self.assertRaises(TypeError): + proxy.func(2, 4, 6, 8) # got too many positioned arguments + + class ProxyDoesntUseKwOnly(object): + @frame_base.args_to_kwargs(Base) + @frame_base.populate_defaults(Base) + def func(self, a, **kwargs): + return dict(kwargs, a=a) + + proxy = ProxyDoesntUseKwOnly() + + # pylint: disable=too-many-function-args,no-value-for-parameter + with self.assertRaises(TypeError): # missing 1 required positional argument + proxy.func() + self.assertEqual(proxy.func(100), {'a': 100}) + self.assertEqual( + proxy.func(2, 4, 6, kw_only=8), { + 'a': 2, 'b': 4, 'c': 6, 'kw_only': 8 + }) + if __name__ == '__main__': unittest.main() diff --git a/sdks/python/apache_beam/dataframe/frames.py b/sdks/python/apache_beam/dataframe/frames.py index a0a99c12ecdd..34fc17553b07 100644 --- a/sdks/python/apache_beam/dataframe/frames.py +++ b/sdks/python/apache_beam/dataframe/frames.py @@ -907,7 +907,7 @@ def sort_index(self, axis, **kwargs): return frame_base.DeferredFrame.wrap( expressions.ComputedExpression( 'sort_index', - lambda df: df.sort_index(axis, **kwargs), + lambda df: df.sort_index(axis=axis, **kwargs), [self._expr], requires_partition_by=partitionings.Arbitrary(), preserves_partition_by=partitionings.Arbitrary(), @@ -2689,7 +2689,7 @@ def set_axis(self, labels, axis, **kwargs): return frame_base.DeferredFrame.wrap( expressions.ComputedExpression( 'set_axis', - lambda df: df.set_axis(labels, axis, **kwargs), + lambda df: df.set_axis(labels, axis=axis, **kwargs), [self._expr], requires_partition_by=partitionings.Arbitrary(), preserves_partition_by=partitionings.Arbitrary())) diff --git a/sdks/python/apache_beam/dataframe/frames_test.py b/sdks/python/apache_beam/dataframe/frames_test.py index 4e59d1da5de4..b8b8cb733ef1 100644 --- a/sdks/python/apache_beam/dataframe/frames_test.py +++ b/sdks/python/apache_beam/dataframe/frames_test.py @@ -17,6 +17,7 @@ import re import unittest import warnings +from typing import Dict import numpy as np import pandas as pd @@ -1634,6 +1635,30 @@ def test_pivot_no_index_provided_on_multiindex(self): # https://github.com/pandas-dev/pandas/issues/40139 ALL_GROUPING_AGGREGATIONS = sorted( set(frames.ALL_AGGREGATIONS) - set(('kurt', 'kurtosis'))) +AGGREGATIONS_WHERE_NUMERIC_ONLY_DEFAULTS_TO_TRUE_IN_PANDAS_1 = set( + frames.ALL_AGGREGATIONS) - set(( + 'nunique', + 'size', + 'count', + 'idxmin', + 'idxmax', + 'mode', + 'rank', + 'all', + 'any', + 'describe')) + + +def numeric_only_kwargs_for_pandas_2(agg_type: str) -> Dict[str, bool]: + """Get proper arguments for numeric_only. + + Behavior for numeric_only in these methods changed in Pandas 2 to default + to False instead of True, so explicitly make it True in Pandas 2.""" + if PD_VERSION >= (2, 0) and ( + agg_type in AGGREGATIONS_WHERE_NUMERIC_ONLY_DEFAULTS_TO_TRUE_IN_PANDAS_1): + return {'numeric_only': True} + else: + return {} class GroupByTest(_AbstractFrameTest): @@ -1650,8 +1675,9 @@ def test_groupby_agg(self, agg_type): self.skipTest( "https://github.com/apache/beam/issues/20967: proxy generation of " "DataFrameGroupBy.describe fails in pandas < 1.2") + kwargs = numeric_only_kwargs_for_pandas_2(agg_type) self._run_test( - lambda df: df.groupby('group').agg(agg_type), + lambda df: df.groupby('group').agg(agg_type, **kwargs), GROUPBY_DF, check_proxy=False) @@ -1661,8 +1687,10 @@ def test_groupby_with_filter(self, agg_type): self.skipTest( "https://github.com/apache/beam/issues/20967: proxy generation of " "DataFrameGroupBy.describe fails in pandas < 1.2") + kwargs = numeric_only_kwargs_for_pandas_2(agg_type) self._run_test( - lambda df: getattr(df[df.foo > 30].groupby('group'), agg_type)(), + lambda df: getattr(df[df.foo > 30].groupby('group'), agg_type) + (**kwargs), GROUPBY_DF, check_proxy=False) @@ -1673,8 +1701,9 @@ def test_groupby(self, agg_type): "https://github.com/apache/beam/issues/20967: proxy generation of " "DataFrameGroupBy.describe fails in pandas < 1.2") + kwargs = numeric_only_kwargs_for_pandas_2(agg_type) self._run_test( - lambda df: getattr(df.groupby('group'), agg_type)(), + lambda df: getattr(df.groupby('group'), agg_type)(**kwargs), GROUPBY_DF, check_proxy=False) @@ -1685,8 +1714,10 @@ def test_groupby_series(self, agg_type): "https://github.com/apache/beam/issues/20967: proxy generation of " "DataFrameGroupBy.describe fails in pandas < 1.2") + kwargs = numeric_only_kwargs_for_pandas_2(agg_type) self._run_test( - lambda df: getattr(df[df.foo > 40].groupby(df.group), agg_type)(), + lambda df: getattr(df[df.foo > 40].groupby(df.group), agg_type) + (**kwargs), GROUPBY_DF, check_proxy=False) @@ -1717,12 +1748,15 @@ def test_groupby_project_series(self, agg_type): "https://github.com/apache/beam/issues/20895: " "SeriesGroupBy.{corr, cov} do not raise the expected error.") - self._run_test(lambda df: getattr(df.groupby('group').foo, agg_type)(), df) - self._run_test(lambda df: getattr(df.groupby('group').bar, agg_type)(), df) + kwargs = numeric_only_kwargs_for_pandas_2(agg_type) self._run_test( - lambda df: getattr(df.groupby('group')['foo'], agg_type)(), df) + lambda df: getattr(df.groupby('group').foo, agg_type)(**kwargs), df) self._run_test( - lambda df: getattr(df.groupby('group')['bar'], agg_type)(), df) + lambda df: getattr(df.groupby('group').bar, agg_type)(**kwargs), df) + self._run_test( + lambda df: getattr(df.groupby('group')['foo'], agg_type)(**kwargs), df) + self._run_test( + lambda df: getattr(df.groupby('group')['bar'], agg_type)(**kwargs), df) @parameterized.expand(ALL_GROUPING_AGGREGATIONS) def test_groupby_project_dataframe(self, agg_type): @@ -1730,8 +1764,10 @@ def test_groupby_project_dataframe(self, agg_type): self.skipTest( "https://github.com/apache/beam/issues/20967: proxy generation of " "DataFrameGroupBy.describe fails in pandas < 1.2") + kwargs = numeric_only_kwargs_for_pandas_2(agg_type) self._run_test( - lambda df: getattr(df.groupby('group')[['bar', 'baz']], agg_type)(), + lambda df: getattr(df.groupby('group')[['bar', 'baz']], agg_type) + (**kwargs), GROUPBY_DF, check_proxy=False) @@ -1760,9 +1796,10 @@ def test_groupby_errors_non_existent_label(self): def test_groupby_callable(self): df = GROUPBY_DF - - self._run_test(lambda df: df.groupby(lambda x: x % 2).foo.sum(), df) - self._run_test(lambda df: df.groupby(lambda x: x % 5).median(), df) + kwargs = numeric_only_kwargs_for_pandas_2('sum') + self._run_test(lambda df: df.groupby(lambda x: x % 2).foo.sum(**kwargs), df) + kwargs = numeric_only_kwargs_for_pandas_2('median') + self._run_test(lambda df: df.groupby(lambda x: x % 5).median(**kwargs), df) def test_groupby_apply(self): df = GROUPBY_DF @@ -1817,8 +1854,9 @@ def test_groupby_transform(self): def test_groupby_pipe(self): df = GROUPBY_DF - - self._run_test(lambda df: df.groupby('group').pipe(lambda x: x.sum()), df) + kwargs = numeric_only_kwargs_for_pandas_2('sum') + self._run_test( + lambda df: df.groupby('group').pipe(lambda x: x.sum(**kwargs)), df) self._run_test( lambda df: df.groupby('group')['bool'].pipe(lambda x: x.any()), df) self._run_test( @@ -1900,14 +1938,14 @@ def test_dataframe_groupby_series(self, agg_type): self.skipTest( "https://github.com/apache/beam/issues/20967: proxy generation of " "DataFrameGroupBy.describe fails in pandas < 1.2") + + def agg(df, group_by): + kwargs = numeric_only_kwargs_for_pandas_2(agg_type) + return df[df.foo > 40].groupby(group_by).agg(agg_type, **kwargs) + + self._run_test(lambda df: agg(df, df.group), GROUPBY_DF, check_proxy=False) self._run_test( - lambda df: df[df.foo > 40].groupby(df.group).agg(agg_type), - GROUPBY_DF, - check_proxy=False) - self._run_test( - lambda df: df[df.foo > 40].groupby(df.foo % 3).agg(agg_type), - GROUPBY_DF, - check_proxy=False) + lambda df: agg(df, df.foo % 3), GROUPBY_DF, check_proxy=False) @parameterized.expand(ALL_GROUPING_AGGREGATIONS) def test_series_groupby_series(self, agg_type): From a722740e2cce855540ad4b3b2943276ad4563f47 Mon Sep 17 00:00:00 2001 From: caneff Date: Mon, 18 Sep 2023 19:15:09 -0400 Subject: [PATCH 057/105] Support kw only arguments for frame methods for Pandas 2 (#28454) From d79f537927c4e63b9d9088d327077ac39fcee36d Mon Sep 17 00:00:00 2001 From: caneff Date: Mon, 18 Sep 2023 19:43:00 -0400 Subject: [PATCH 058/105] Suppress FutureWarnings for Pandas < 2 (#28481) --- sdks/python/apache_beam/dataframe/frames_test.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sdks/python/apache_beam/dataframe/frames_test.py b/sdks/python/apache_beam/dataframe/frames_test.py index b8b8cb733ef1..5d904855f87e 100644 --- a/sdks/python/apache_beam/dataframe/frames_test.py +++ b/sdks/python/apache_beam/dataframe/frames_test.py @@ -21,6 +21,7 @@ import numpy as np import pandas as pd +import pytest from parameterized import parameterized import apache_beam as beam @@ -45,6 +46,10 @@ 'str': [str(i) for i in range(100)], }) +if PD_VERSION < (2, 0): + # All these are things that are fixed in the Pandas 2 transition. + pytestmark = pytest.mark.filterwarnings("ignore::FutureWarning") + def _get_deferred_args(*args): return [ From 08a976715160d3e18e5fb623aaa9f485f1cda4bb Mon Sep 17 00:00:00 2001 From: Arvind Ram Date: Mon, 18 Sep 2023 18:43:52 -0700 Subject: [PATCH 059/105] Sets recommended max Xmx 32G when set_recommended_max_xmx experiment is enabled (#28442) * Sets recommended max Xmx 32G when set_recommended_max_xmx experiment is enabled * Fixing lint errors * Adding comment to reason why 32G is recomemnded value --- sdks/java/container/boot.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/sdks/java/container/boot.go b/sdks/java/container/boot.go index 0e39d907f075..f7fd7437c88a 100644 --- a/sdks/java/container/boot.go +++ b/sdks/java/container/boot.go @@ -159,8 +159,9 @@ func main() { cp = append(cp, filepath.Join(dir, filepath.FromSlash(name))) } + var setRecommendedMaxXmx = strings.Contains(options, "set_recommended_max_xmx") args := []string{ - "-Xmx" + strconv.FormatUint(heapSizeLimit(info), 10), + "-Xmx" + strconv.FormatUint(heapSizeLimit(info, setRecommendedMaxXmx), 10), // ParallelGC the most adequate for high throughput and lower CPU utilization // It is the default GC in Java 8, but not on newer versions "-XX:+UseParallelGC", @@ -266,9 +267,14 @@ func makePipelineOptionsFile(options string) error { // it returns 70% of the physical memory on the machine. If it cannot determine // that value, it returns 1GB. This is an imperfect heuristic. It aims to // ensure there is memory for non-heap use and other overhead, while also not -// underutilizing the machine. -func heapSizeLimit(info *fnpb.ProvisionInfo) uint64 { - if size, err := syscallx.PhysicalMemorySize(); err == nil { +// underutilizing the machine. if set_recommended_max_xmx experiment is enabled, +// sets xmx to 32G. Under 32G JVM enables CompressedOops. CompressedOops +// utilizes memory more efficiently, and has positive impact on GC performance +// and cache hit rate. +func heapSizeLimit(info *fnpb.ProvisionInfo, setRecommendedMaxXmx bool) uint64 { + if setRecommendedMaxXmx { + return 32 << 30 + } else if size, err := syscallx.PhysicalMemorySize(); err == nil { return (size * 70) / 100 } return 1 << 30 From 5ffddb82f720d35870ded5d8a3e7ba3c094b2f39 Mon Sep 17 00:00:00 2001 From: Vitaly Terentyev Date: Tue, 19 Sep 2023 18:05:45 +0400 Subject: [PATCH 060/105] Add PerformanceTests BigQueryIO actions (#28325) * Add PerformanceTests BigQueryIO actions * Add License headers * Update concurrency * Update cron * Update workflows * Update publish junit results --- .github/workflows/README.md | 3 + ...rmanceTests_BigQueryIO_Batch_Java_Avro.yml | 101 ++++++++++++++++++ ...rmanceTests_BigQueryIO_Batch_Java_Json.yml | 101 ++++++++++++++++++ ...ormanceTests_BigQueryIO_Streaming_Java.yml | 101 ++++++++++++++++++ .../config_BigQueryIO_Batch_Java_Avro.txt | 38 +++++++ .../config_BigQueryIO_Batch_Java_Json.txt | 38 +++++++ .../config_BigQueryIO_Streaming_Java.txt | 39 +++++++ 7 files changed, 421 insertions(+) create mode 100644 .github/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Avro.yml create mode 100644 .github/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Json.yml create mode 100644 .github/workflows/beam_PerformanceTests_BigQueryIO_Streaming_Java.yml create mode 100644 .github/workflows/performance-tests-job-configs/config_BigQueryIO_Batch_Java_Avro.txt create mode 100644 .github/workflows/performance-tests-job-configs/config_BigQueryIO_Batch_Java_Json.txt create mode 100644 .github/workflows/performance-tests-job-configs/config_BigQueryIO_Streaming_Java.txt diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 51375a191cae..d0897602f5e0 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -181,6 +181,9 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | Workflow name | Matrix | Trigger Phrase | Cron Status | |:-------------:|:------:|:--------------:|:-----------:| | [ Load Tests CoGBK Dataflow Streaming Java ](https://github.com/apache/beam/actions/workflows/beam_LoadTests_Java_CoGBK_Dataflow_Streaming.yml) | N/A |`Run Load Tests Java CoGBK Dataflow Streaming`| [![.github/workflows/beam_LoadTests_Java_CoGBK_Dataflow_Streaming](https://github.com/apache/beam/actions/workflows/beam_LoadTests_Java_CoGBK_Dataflow_Streaming.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_LoadTests_Java_CoGBK_Dataflow_Streaming.yml) +| [ Performance Tests BigQueryIO Batch Java Avro ](https://github.com/apache/beam/actions/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Avro.yml) | N/A |`Run BigQueryIO Batch Performance Test Java Avro`| [![.github/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Avro](https://github.com/apache/beam/actions/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Avro.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Avro.yml) +| [ Performance Tests BigQueryIO Batch Java Json ](https://github.com/apache/beam/actions/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Json.yml) | N/A |`Run BigQueryIO Batch Performance Test Java Json`| [![.github/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Json](https://github.com/apache/beam/actions/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Json.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Json.yml) +| [ Performance Tests BigQueryIO Streaming Java ](https://github.com/apache/beam/actions/workflows/beam_PerformanceTests_BigQueryIO_Streaming_Java.yml) | N/A |`Run BigQueryIO Streaming Performance Test Java`| [![.github/workflows/beam_PerformanceTests_BigQueryIO_Streaming_Java](https://github.com/apache/beam/actions/workflows/beam_PerformanceTests_BigQueryIO_Streaming_Java.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PerformanceTests_BigQueryIO_Streaming_Java.yml) | [ PostCommit BeamMetrics Publish ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_BeamMetrics_Publish.yml) | N/A |`Run Beam Metrics Deployment`| [![.github/workflows/beam_PostCommit_BeamMetrics_Publish.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_BeamMetrics_Publish.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_BeamMetrics_Publish.yml) | [ PostCommit Go ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go.yml) | N/A |`Run Go PostCommit`| [![.github/workflows/beam_PostCommit_Go.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go.yml) | | [ PostCommit Go Dataflow ARM](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_Dataflow_ARM.yml) | N/A |`Run Go PostCommit Dataflow ARM`| [![.github/workflows/beam_PostCommit_Go_Dataflow_ARM.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_Dataflow_ARM.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_Dataflow_ARM.yml) |[label](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_Dataflow_ARM.yml) diff --git a/.github/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Avro.yml b/.github/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Avro.yml new file mode 100644 index 000000000000..cc78482b18c2 --- /dev/null +++ b/.github/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Avro.yml @@ -0,0 +1,101 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Performance Tests BigQueryIO Batch Java Avro + +on: + issue_comment: + types: [created] + schedule: + - cron: '10 1,13 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login }}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PerformanceTests_BigQueryIO_Batch_Java_Avro: + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run BigQueryIO Batch Performance Test Java Avro' + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 100 + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + strategy: + matrix: + job_name: ["beam_PerformanceTests_BigQueryIO_Batch_Java_Avro"] + job_phrase: ["Run BigQueryIO Batch Performance Test Java Avro"] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Prepare config + id: set_config + shell: bash + run: | + CURDATE=$(date '+%m%d%H%M%S' --utc) + CURCONFIG=$(grep -v "^#.*" ./.github/workflows/performance-tests-job-configs/config_BigQueryIO_Batch_Java_Avro.txt | tr '\n' ' ') + CONFIGWITHDATE=$(echo "${CURCONFIG/bqio_write_10GB_java_avro_/bqio_write_10GB_java_avro_$CURDATE}") + echo "prepared_config=$CONFIGWITHDATE" >> $GITHUB_OUTPUT + - name: run Java BigQueryIO Batch Avro Performance Test + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:java:io:bigquery-io-perf-tests:integrationTest + arguments: | + --tests org.apache.beam.sdk.bigqueryioperftests.BigQueryIOIT \ + --info \ + -DintegrationTestRunner=dataflow \ + -DintegrationTestPipelineOptions=${{ steps.set_config.outputs.prepared_config }} \ + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Json.yml b/.github/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Json.yml new file mode 100644 index 000000000000..af71511838f9 --- /dev/null +++ b/.github/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Json.yml @@ -0,0 +1,101 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Performance Tests BigQueryIO Batch Java Json + +on: + issue_comment: + types: [created] + schedule: + - cron: '30 8,20 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login }}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PerformanceTests_BigQueryIO_Batch_Java_Json: + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run BigQueryIO Batch Performance Test Java Json' + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 100 + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + strategy: + matrix: + job_name: ["beam_PerformanceTests_BigQueryIO_Batch_Java_Json"] + job_phrase: ["Run BigQueryIO Batch Performance Test Java Json"] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Prepare config + id: set_config + shell: bash + run: | + CURDATE=$(date '+%m%d%H%M%S' --utc) + CURCONFIG=$(grep -v "^#.*" ./.github/workflows/performance-tests-job-configs/config_BigQueryIO_Batch_Java_Json.txt | tr '\n' ' ') + CONFIGWITHDATE=$(echo "${CURCONFIG/bqio_write_10GB_java_json_/bqio_write_10GB_java_json_$CURDATE}") + echo "prepared_config=$CONFIGWITHDATE" >> $GITHUB_OUTPUT + - name: run Java BigQueryIO Batch Json Performance Test + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:java:io:bigquery-io-perf-tests:integrationTest + arguments: | + --tests org.apache.beam.sdk.bigqueryioperftests.BigQueryIOIT \ + --info \ + -DintegrationTestRunner=dataflow \ + -DintegrationTestPipelineOptions=${{ steps.set_config.outputs.prepared_config }} \ + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/beam_PerformanceTests_BigQueryIO_Streaming_Java.yml b/.github/workflows/beam_PerformanceTests_BigQueryIO_Streaming_Java.yml new file mode 100644 index 000000000000..3eaac8194a3a --- /dev/null +++ b/.github/workflows/beam_PerformanceTests_BigQueryIO_Streaming_Java.yml @@ -0,0 +1,101 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Performance Tests BigQueryIO Streaming Java + +on: + issue_comment: + types: [created] + schedule: + - cron: '20 15,22 * * *' + workflow_dispatch: + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login }}' + cancel-in-progress: true + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PerformanceTests_BigQueryIO_Streaming_Java: + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run BigQueryIO Streaming Performance Test Java' + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 100 + name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + strategy: + matrix: + job_name: ["beam_PerformanceTests_BigQueryIO_Streaming_Java"] + job_phrase: ["Run BigQueryIO Streaming Performance Test Java"] + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Prepare config + id: set_config + shell: bash + run: | + CURDATE=$(date '+%m%d%H%M%S' --utc) + CURCONFIG=$(grep -v "^#.*" ./.github/workflows/performance-tests-job-configs/config_BigQueryIO_Streaming_Java.txt | tr '\n' ' ') + CONFIGWITHDATE=$(echo "${CURCONFIG/bqio_write_10GB_java_stream_/bqio_write_10GB_java_stream_$CURDATE}") + echo "prepared_config=$CONFIGWITHDATE" >> $GITHUB_OUTPUT + - name: run Java BigQueryIO Streaming Performance Test + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:java:io:bigquery-io-perf-tests:integrationTest + arguments: | + --tests org.apache.beam.sdk.bigqueryioperftests.BigQueryIOIT \ + --info \ + -DintegrationTestRunner=dataflow \ + -DintegrationTestPipelineOptions=${{ steps.set_config.outputs.prepared_config }} \ + - name: Archive JUnit Test Results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: JUnit Test Results + path: "**/build/reports/tests/" + - name: Publish JUnit Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + commit: '${{ env.prsha || env.GITHUB_SHA }}' + comment_mode: ${{ github.event_name == 'issue_comment' && 'always' || 'off' }} + files: '**/build/test-results/**/*.xml' \ No newline at end of file diff --git a/.github/workflows/performance-tests-job-configs/config_BigQueryIO_Batch_Java_Avro.txt b/.github/workflows/performance-tests-job-configs/config_BigQueryIO_Batch_Java_Avro.txt new file mode 100644 index 000000000000..5e7e53821231 --- /dev/null +++ b/.github/workflows/performance-tests-job-configs/config_BigQueryIO_Batch_Java_Avro.txt @@ -0,0 +1,38 @@ +############################################################################### +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +'["--tempLocation=gs://temp-storage-for-perf-tests/loadtests", +"--project=apache-beam-testing", +"--tempRoot=gs://temp-storage-for-perf-tests/loadtests", +"--writeMethod=FILE_LOADS", +"--writeFormat=AVRO", +"--testBigQueryDataset=beam_performance", +"--testBigQueryTable=bqio_write_10GB_java_avro_", +"--metricsBigQueryDataset=beam_performance", +"--metricsBigQueryTable=bqio_10GB_results_java_batch_avro", +"--influxMeasurement=bqio_10GB_results_java_batch_avro", +"--sourceOptions={ +\"numRecords\":\"10485760\", +\"keySizeBytes\":\"1\", +\"valueSizeBytes\":\"1024\" +}", +"--runner=DataflowRunner", +"--maxNumWorkers=5", +"--numWorkers=5", +"--autoscalingAlgorithm=NONE", +"--influxDatabase=beam_test_metrics", +"--influxHost=http://10.128.0.96:8086"]' \ No newline at end of file diff --git a/.github/workflows/performance-tests-job-configs/config_BigQueryIO_Batch_Java_Json.txt b/.github/workflows/performance-tests-job-configs/config_BigQueryIO_Batch_Java_Json.txt new file mode 100644 index 000000000000..7bd9c30ae738 --- /dev/null +++ b/.github/workflows/performance-tests-job-configs/config_BigQueryIO_Batch_Java_Json.txt @@ -0,0 +1,38 @@ +############################################################################### +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +'["--tempLocation=gs://temp-storage-for-perf-tests/loadtests", +"--project=apache-beam-testing", +"--tempRoot=gs://temp-storage-for-perf-tests/loadtests", +"--writeMethod=FILE_LOADS", +"--writeFormat=JSON", +"--testBigQueryDataset=beam_performance", +"--testBigQueryTable=bqio_write_10GB_java_json_", +"--metricsBigQueryDataset=beam_performance", +"--metricsBigQueryTable=bqio_10GB_results_java_batch_json", +"--influxMeasurement=bqio_10GB_results_java_batch_json", +"--sourceOptions={ +\"numRecords\":\"10485760\", +\"keySizeBytes\":\"1\", +\"valueSizeBytes\":\"1024\" +}", +"--runner=DataflowRunner", +"--maxNumWorkers=5", +"--numWorkers=5", +"--autoscalingAlgorithm=NONE", +"--influxDatabase=beam_test_metrics", +"--influxHost=http://10.128.0.96:8086"]' \ No newline at end of file diff --git a/.github/workflows/performance-tests-job-configs/config_BigQueryIO_Streaming_Java.txt b/.github/workflows/performance-tests-job-configs/config_BigQueryIO_Streaming_Java.txt new file mode 100644 index 000000000000..8bddea5fcb8b --- /dev/null +++ b/.github/workflows/performance-tests-job-configs/config_BigQueryIO_Streaming_Java.txt @@ -0,0 +1,39 @@ +############################################################################### +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +'["--tempLocation=gs://temp-storage-for-perf-tests/loadtests", +"--project=apache-beam-testing", +"--tempRoot=gs://temp-storage-for-perf-tests/loadtests", +"--writeMethod=STREAMING_INSERTS", +"--writeFormat=JSON", +"--pipelineTimeout=1200", +"--testBigQueryDataset=beam_performance", +"--testBigQueryTable=bqio_write_10GB_java_stream_", +"--metricsBigQueryDataset=beam_performance", +"--metricsBigQueryTable=bqio_10GB_results_java_stream", +"--influxMeasurement=bqio_10GB_results_java_stream", +"--sourceOptions={ +\"numRecords\":\"10485760\", +\"keySizeBytes\":\"1\", +\"valueSizeBytes\":\"1024\" +}", +"--runner=DataflowRunner", +"--maxNumWorkers=5", +"--numWorkers=5", +"--autoscalingAlgorithm=NONE", +"--influxDatabase=beam_test_metrics", +"--influxHost=http://10.128.0.96:8086"]' \ No newline at end of file From 49283e31ece9ec43fdbdbc6b1b2776bd0164c1e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Sep 2023 10:08:50 -0400 Subject: [PATCH 061/105] Bump archive from 3.3.7 to 3.3.8 in /playground/frontend (#28351) Bumps [archive](https://github.com/brendan-duncan/archive) from 3.3.7 to 3.3.8. - [Changelog](https://github.com/brendan-duncan/archive/blob/main/CHANGELOG.md) - [Commits](https://github.com/brendan-duncan/archive/compare/3.3.7...3.3.8) --- updated-dependencies: - dependency-name: archive dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- playground/frontend/pubspec.lock | 48 +++++++++++++++++++------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/playground/frontend/pubspec.lock b/playground/frontend/pubspec.lock index 425e33e4de7d..e49850e7a820 100644 --- a/playground/frontend/pubspec.lock +++ b/playground/frontend/pubspec.lock @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: archive - sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a" + sha256: "49b1fad315e57ab0bbc15bcbb874e83116a1d78f77ebd500a4af6c9407d6b28e" url: "https://pub.dev" source: hosted - version: "3.3.7" + version: "3.3.8" args: dependency: transitive description: @@ -189,10 +189,10 @@ packages: dependency: "direct main" description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.17.2" connectivity_plus: dependency: transitive description: @@ -600,10 +600,10 @@ packages: dependency: "direct main" description: name: intl - sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.18.0" + version: "0.18.1" io: dependency: transitive description: @@ -672,18 +672,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: @@ -1059,10 +1059,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: @@ -1115,26 +1115,26 @@ packages: dependency: transitive description: name: test - sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4" + sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" url: "https://pub.dev" source: hosted - version: "1.24.1" + version: "1.24.3" test_api: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.0" test_core: dependency: transitive description: name: test_core - sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93" + sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.5.3" timing: dependency: transitive description: @@ -1299,10 +1299,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f6deed8ed625c52864792459709183da231ebf66ff0cf09e69b573227c377efe + sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f url: "https://pub.dev" source: hosted - version: "11.3.0" + version: "11.7.1" watcher: dependency: transitive description: @@ -1311,6 +1311,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" web_browser_detect: dependency: transitive description: From 79f46e00184fc5fcea7c9c4a85e2ed8467ef1a71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Sep 2023 10:09:08 -0400 Subject: [PATCH 062/105] Bump archive from 3.3.7 to 3.3.8 in /learning/tour-of-beam/frontend (#28350) Bumps [archive](https://github.com/brendan-duncan/archive) from 3.3.7 to 3.3.8. - [Changelog](https://github.com/brendan-duncan/archive/blob/main/CHANGELOG.md) - [Commits](https://github.com/brendan-duncan/archive/compare/3.3.7...3.3.8) --- updated-dependencies: - dependency-name: archive dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- learning/tour-of-beam/frontend/pubspec.lock | 48 ++++++++++++--------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/learning/tour-of-beam/frontend/pubspec.lock b/learning/tour-of-beam/frontend/pubspec.lock index 5cde8a54211f..49bdc9ef95a8 100644 --- a/learning/tour-of-beam/frontend/pubspec.lock +++ b/learning/tour-of-beam/frontend/pubspec.lock @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: archive - sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a" + sha256: "49b1fad315e57ab0bbc15bcbb874e83116a1d78f77ebd500a4af6c9407d6b28e" url: "https://pub.dev" source: hosted - version: "3.3.7" + version: "3.3.8" args: dependency: transitive description: @@ -189,10 +189,10 @@ packages: dependency: "direct main" description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.17.2" color: dependency: transitive description: @@ -656,10 +656,10 @@ packages: dependency: transitive description: name: intl - sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.18.0" + version: "0.18.1" io: dependency: transitive description: @@ -728,18 +728,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: @@ -1115,10 +1115,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: @@ -1171,26 +1171,26 @@ packages: dependency: transitive description: name: test - sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4" + sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" url: "https://pub.dev" source: hosted - version: "1.24.1" + version: "1.24.3" test_api: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.0" test_core: dependency: transitive description: name: test_core - sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93" + sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.5.3" time: dependency: transitive description: @@ -1347,10 +1347,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f6deed8ed625c52864792459709183da231ebf66ff0cf09e69b573227c377efe + sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f url: "https://pub.dev" source: hosted - version: "11.3.0" + version: "11.7.1" watcher: dependency: transitive description: @@ -1359,6 +1359,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" web_socket_channel: dependency: transitive description: From 34024902746af90e7bf41e28729ec031dbab58d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Sep 2023 10:11:49 -0400 Subject: [PATCH 063/105] Bump actions/setup-python from 2 to 4 (#28275) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2 to 4. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v2...v4) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/beam_Release_Python_NightlySnapshot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/beam_Release_Python_NightlySnapshot.yml b/.github/workflows/beam_Release_Python_NightlySnapshot.yml index 2787de9eefef..63c9f7a8497f 100644 --- a/.github/workflows/beam_Release_Python_NightlySnapshot.yml +++ b/.github/workflows/beam_Release_Python_NightlySnapshot.yml @@ -70,7 +70,7 @@ jobs: with: cache-read-only: false - name: Setup Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: '3.8' - name: Authenticate on GCP From 8f3a38e7d09ded861f676f25ee8328e08fef11d1 Mon Sep 17 00:00:00 2001 From: Vlado Djerek Date: Tue, 19 Sep 2023 16:31:50 +0200 Subject: [PATCH 064/105] kubectl install direct and use only auth plugin from gcloud (#28501) --- .../gh-actions-self-hosted-runners/arc/images/Dockerfile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/gh-actions-self-hosted-runners/arc/images/Dockerfile b/.github/gh-actions-self-hosted-runners/arc/images/Dockerfile index 41f8061d44a9..2cbfea75ab55 100644 --- a/.github/gh-actions-self-hosted-runners/arc/images/Dockerfile +++ b/.github/gh-actions-self-hosted-runners/arc/images/Dockerfile @@ -59,10 +59,15 @@ RUN curl -OL https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-c rm google-cloud-sdk-367.0.0-linux-x86_64.tar.gz && \ mv google-cloud-sdk /usr/local/google-cloud-sdk && \ /usr/local/google-cloud-sdk/install.sh --quiet && \ - /usr/local/google-cloud-sdk/bin/gcloud components install kubectl && \ + /usr/local/google-cloud-sdk/bin/gcloud components install gke-gcloud-auth-plugin && \ #revert permission chown -R runner:runner /home/runner/.config +ENV USE_GKE_GCLOUD_AUTH_PLUGIN=True ENV PATH="${PATH}:/usr/local/google-cloud-sdk/bin" +#Install Kubectl +RUN curl -OL https://dl.k8s.io/release/v1.28.1/bin/linux/amd64/kubectl && \ + chmod +x ./kubectl && \ + mv ./kubectl /usr/local/bin/kubectl #Install Apache Maven RUN curl -OL https://dlcdn.apache.org/maven/maven-3/3.9.4/binaries/apache-maven-3.9.4-bin.tar.gz && \ tar -xvf apache-maven-3.9.4-bin.tar.gz && \ @@ -73,4 +78,5 @@ ENV MAVEN_HOME="/usr/local/maven" # Needed to transfer path addtitions to runner environment RUN echo PATH=$PATH >> /runnertmp/.env +RUN echo USE_GKE_GCLOUD_AUTH_PLUGIN=$USE_GKE_GCLOUD_AUTH_PLUGIN >> /runnertmp/.env USER runner From 2ad06b3cdcd217d3068e2b473ea1112364e22f07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Sep 2023 10:56:10 -0400 Subject: [PATCH 065/105] Bump actions/checkout from 3 to 4 (#28302) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/assign_milestone.yml | 2 +- .../workflows/beam_CancelStaleDataflowJobs.yml | 2 +- ...m_LoadTests_Java_CoGBK_Dataflow_Streaming.yml | 2 +- ...rformanceTests_BigQueryIO_Batch_Java_Avro.yml | 2 +- ...rformanceTests_BigQueryIO_Batch_Java_Json.yml | 2 +- ...erformanceTests_BigQueryIO_Streaming_Java.yml | 2 +- .../beam_PostCommit_BeamMetrics_Publish.yml | 2 +- .github/workflows/beam_PostCommit_Go.yml | 2 +- .../beam_PostCommit_Go_Dataflow_ARM.yml | 2 +- .../workflows/beam_PostCommit_Go_VR_Flink.yml | 2 +- .../workflows/beam_PostCommit_Go_VR_Samza.yml | 2 +- .../workflows/beam_PostCommit_Go_VR_Spark.yml | 2 +- .github/workflows/beam_PostCommit_Java.yml | 2 +- .../beam_PostCommit_Java_Avro_Versions.yml | 2 +- .../beam_PostCommit_Java_DataflowV1.yml | 2 +- .../beam_PostCommit_Java_DataflowV2.yml | 2 +- ...eam_PostCommit_Java_Examples_Dataflow_ARM.yml | 2 +- ...am_PostCommit_Java_Examples_Dataflow_Java.yml | 2 +- ...beam_PostCommit_Java_Examples_Dataflow_V2.yml | 2 +- ...PostCommit_Java_Examples_Dataflow_V2_Java.yml | 2 +- .../beam_PostCommit_Java_Examples_Direct.yml | 2 +- .../beam_PostCommit_Java_Examples_Flink.yml | 2 +- .../beam_PostCommit_Java_Examples_Spark.yml | 2 +- .../beam_PostCommit_Java_Hadoop_Versions.yml | 2 +- ...beam_PostCommit_Java_IO_Performance_Tests.yml | 4 ++-- ...beam_PostCommit_Java_Jpms_Dataflow_Java11.yml | 2 +- ...beam_PostCommit_Java_Jpms_Dataflow_Java17.yml | 2 +- .../beam_PostCommit_Java_Jpms_Direct_Java11.yml | 2 +- .../beam_PostCommit_Java_Jpms_Direct_Java17.yml | 2 +- .../beam_PostCommit_Java_Jpms_Flink_Java11.yml | 2 +- .../beam_PostCommit_Java_Jpms_Spark_Java11.yml | 2 +- .../beam_PostCommit_Java_PVR_Flink_Streaming.yml | 2 +- .../workflows/beam_PostCommit_Java_PVR_Samza.yml | 2 +- ...beam_PostCommit_Java_PVR_Spark3_Streaming.yml | 2 +- .../beam_PostCommit_Java_PVR_Spark_Batch.yml | 2 +- .../workflows/beam_PostCommit_Java_Sickbay.yml | 2 +- ..._PostCommit_Java_ValidatesRunner_Dataflow.yml | 2 +- ...ava_ValidatesRunner_Dataflow_JavaVersions.yml | 2 +- ...t_Java_ValidatesRunner_Dataflow_Streaming.yml | 2 +- ...stCommit_Java_ValidatesRunner_Dataflow_V2.yml | 2 +- ...ava_ValidatesRunner_Dataflow_V2_Streaming.yml | 2 +- ...am_PostCommit_Java_ValidatesRunner_Direct.yml | 2 +- ..._Java_ValidatesRunner_Direct_JavaVersions.yml | 2 +- ...eam_PostCommit_Java_ValidatesRunner_Flink.yml | 2 +- ...tCommit_Java_ValidatesRunner_Flink_Java11.yml | 2 +- ...eam_PostCommit_Java_ValidatesRunner_Samza.yml | 2 +- ...eam_PostCommit_Java_ValidatesRunner_Spark.yml | 2 +- ..._ValidatesRunner_SparkStructuredStreaming.yml | 2 +- ...tCommit_Java_ValidatesRunner_Spark_Java11.yml | 2 +- ..._PostCommit_Java_ValidatesRunner_Twister2.yml | 2 +- .../beam_PostCommit_Java_ValidatesRunner_ULR.yml | 2 +- .../beam_PostCommit_PortableJar_Flink.yml | 2 +- .../beam_PostCommit_PortableJar_Spark.yml | 2 +- .github/workflows/beam_PostCommit_Python.yml | 2 +- .../beam_PostCommit_Python_Examples_Dataflow.yml | 2 +- .../beam_PostCommit_Python_Examples_Direct.yml | 2 +- .../beam_PostCommit_Python_Examples_Flink.yml | 2 +- .../beam_PostCommit_Python_Examples_Spark.yml | 2 +- .../beam_PostCommit_Python_MongoDBIO_IT.yml | 2 +- ...Commit_Python_ValidatesContainer_Dataflow.yml | 2 +- ...ython_ValidatesContainer_Dataflow_With_RC.yml | 2 +- ...ostCommit_Python_ValidatesRunner_Dataflow.yml | 2 +- ...m_PostCommit_Python_ValidatesRunner_Flink.yml | 2 +- ...m_PostCommit_Python_ValidatesRunner_Samza.yml | 2 +- ...m_PostCommit_Python_ValidatesRunner_Spark.yml | 2 +- ...beam_PostCommit_Python_Xlang_Gcp_Dataflow.yml | 2 +- .../beam_PostCommit_Python_Xlang_Gcp_Direct.yml | 2 +- .../beam_PostCommit_Python_Xlang_IO_Dataflow.yml | 2 +- .github/workflows/beam_PostCommit_SQL.yml | 2 +- .../workflows/beam_PostCommit_Sickbay_Python.yml | 2 +- .../beam_PostCommit_TransformService_Direct.yml | 2 +- .../beam_PostCommit_Website_Publish.yml | 2 +- .../workflows/beam_PostCommit_Website_Test.yml | 2 +- .github/workflows/beam_PostCommit_XVR_Direct.yml | 2 +- .github/workflows/beam_PostCommit_XVR_Flink.yml | 2 +- .../beam_PostCommit_XVR_GoUsingJava_Dataflow.yml | 2 +- ...m_PostCommit_XVR_JavaUsingPython_Dataflow.yml | 2 +- ...ostCommit_XVR_PythonUsingJavaSQL_Dataflow.yml | 2 +- ...m_PostCommit_XVR_PythonUsingJava_Dataflow.yml | 2 +- .github/workflows/beam_PostCommit_XVR_Samza.yml | 2 +- .github/workflows/beam_PostCommit_XVR_Spark3.yml | 2 +- .../beam_PreCommit_CommunityMetrics.yml | 2 +- .github/workflows/beam_PreCommit_Go.yml | 2 +- .github/workflows/beam_PreCommit_GoPortable.yml | 2 +- .github/workflows/beam_PreCommit_GoPrism.yml | 2 +- .github/workflows/beam_PreCommit_ItFramework.yml | 2 +- .github/workflows/beam_PreCommit_Java.yml | 2 +- ...ommit_Java_Amazon-Web-Services2_IO_Direct.yml | 2 +- ...Commit_Java_Amazon-Web-Services_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Amqp_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Azure_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Cassandra_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Cdap_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Clickhouse_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Csv_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Debezium_IO_Direct.yml | 2 +- ...am_PreCommit_Java_ElasticSearch_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Examples_Dataflow.yml | 2 +- ...m_PreCommit_Java_Examples_Dataflow_Java11.yml | 2 +- ...m_PreCommit_Java_Examples_Dataflow_Java17.yml | 2 +- ...mmit_Java_File-schema-transform_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Flink_Versions.yml | 2 +- .../beam_PreCommit_Java_GCP_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_HBase_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_HCatalog_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Hadoop_IO_Direct.yml | 2 +- .../workflows/beam_PreCommit_Java_IOs_Direct.yml | 2 +- .../beam_PreCommit_Java_InfluxDb_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_JDBC_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Jms_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Kafka_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Kinesis_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Kudu_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_MongoDb_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Mqtt_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Neo4j_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_PVR_Flink_Batch.yml | 2 +- .../beam_PreCommit_Java_PVR_Flink_Docker.yml | 2 +- .../beam_PreCommit_Java_Parquet_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Pulsar_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_RabbitMq_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Redis_IO_Direct.yml | 2 +- ...beam_PreCommit_Java_SingleStore_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Snowflake_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Solr_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Spark3_Versions.yml | 2 +- .../beam_PreCommit_Java_Splunk_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Thrift_IO_Direct.yml | 2 +- .../beam_PreCommit_Java_Tika_IO_Direct.yml | 2 +- .../workflows/beam_PreCommit_Kotlin_Examples.yml | 2 +- .../workflows/beam_PreCommit_Portable_Python.yml | 2 +- .github/workflows/beam_PreCommit_Python.yml | 2 +- .../workflows/beam_PreCommit_PythonDocker.yml | 2 +- .github/workflows/beam_PreCommit_PythonDocs.yml | 2 +- .../workflows/beam_PreCommit_PythonFormatter.yml | 2 +- .github/workflows/beam_PreCommit_PythonLint.yml | 2 +- .../workflows/beam_PreCommit_Python_Coverage.yml | 2 +- .../beam_PreCommit_Python_Dataframes.yml | 2 +- .../workflows/beam_PreCommit_Python_Examples.yml | 2 +- .../beam_PreCommit_Python_Integration.yml | 2 +- .../beam_PreCommit_Python_PVR_Flink.yml | 2 +- .../workflows/beam_PreCommit_Python_Runners.yml | 2 +- .../beam_PreCommit_Python_Transforms.yml | 2 +- .github/workflows/beam_PreCommit_RAT.yml | 2 +- .github/workflows/beam_PreCommit_SQL.yml | 2 +- .github/workflows/beam_PreCommit_SQL_Java11.yml | 2 +- .github/workflows/beam_PreCommit_SQL_Java17.yml | 2 +- .github/workflows/beam_PreCommit_Spotless.yml | 2 +- .github/workflows/beam_PreCommit_Typescript.yml | 2 +- .github/workflows/beam_PreCommit_Website.yml | 2 +- .../beam_PreCommit_Website_Stage_GCS.yml | 2 +- .github/workflows/beam_PreCommit_Whitespace.yml | 2 +- ...am_Python_ValidatesContainer_Dataflow_ARM.yml | 2 +- .../workflows/beam_Release_NightlySnapshot.yml | 2 +- .../beam_Release_Python_NightlySnapshot.yml | 2 +- .github/workflows/build_release_candidate.yml | 8 ++++---- .github/workflows/build_runner_image.yml | 2 +- .github/workflows/build_wheels.yml | 6 +++--- .github/workflows/cancel.yml | 2 +- .github/workflows/choose_rc_commit.yml | 2 +- .../workflows/code_completion_plugin_tests.yml | 4 ++-- .github/workflows/cut_release_branch.yml | 6 +++--- .github/workflows/dask_runner_tests.yml | 4 ++-- .github/workflows/git_tag_released_version.yml | 2 +- .github/workflows/go_tests.yml | 2 +- .github/workflows/issue-tagger.yml | 2 +- .github/workflows/java_tests.yml | 8 ++++---- .github/workflows/local_env_tests.yml | 4 ++-- .../workflows/playground_backend_precommit.yml | 2 +- .github/workflows/playground_frontend_test.yml | 2 +- .github/workflows/pr-bot-new-prs.yml | 2 +- .github/workflows/pr-bot-pr-updates.yml | 2 +- .../workflows/pr-bot-prs-needing-attention.yml | 2 +- .github/workflows/pr-bot-update-reviewers.yml | 2 +- .../workflows/publish_github_release_notes.yml | 4 ++-- .github/workflows/python_dependency_tests.yml | 2 +- .github/workflows/python_tests.yml | 10 +++++----- .github/workflows/reportGenerator.yml | 2 +- .github/workflows/run_perf_alert_tool.yml | 2 +- .github/workflows/run_rc_validation.yml | 16 ++++++++-------- .github/workflows/tour_of_beam_backend.yml | 2 +- .../tour_of_beam_backend_integration.yml | 2 +- .github/workflows/tour_of_beam_frontend_test.yml | 2 +- .github/workflows/typescript_tests.yml | 8 ++++---- .github/workflows/update_python_dependencies.yml | 4 ++-- 185 files changed, 215 insertions(+), 215 deletions(-) diff --git a/.github/workflows/assign_milestone.yml b/.github/workflows/assign_milestone.yml index 9be68ecd3d0f..17cc167ebcbb 100644 --- a/.github/workflows/assign_milestone.yml +++ b/.github/workflows/assign_milestone.yml @@ -31,7 +31,7 @@ jobs: issues: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 2 diff --git a/.github/workflows/beam_CancelStaleDataflowJobs.yml b/.github/workflows/beam_CancelStaleDataflowJobs.yml index 07ffbe1260e8..df896528c84e 100644 --- a/.github/workflows/beam_CancelStaleDataflowJobs.yml +++ b/.github/workflows/beam_CancelStaleDataflowJobs.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run Cancel Stale Dataflow Jobs' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_LoadTests_Java_CoGBK_Dataflow_Streaming.yml b/.github/workflows/beam_LoadTests_Java_CoGBK_Dataflow_Streaming.yml index 9bbaaaa3404a..0cb601522a81 100644 --- a/.github/workflows/beam_LoadTests_Java_CoGBK_Dataflow_Streaming.yml +++ b/.github/workflows/beam_LoadTests_Java_CoGBK_Dataflow_Streaming.yml @@ -62,7 +62,7 @@ jobs: job_name: [ "beam_LoadTests_Java_CoGBK_Dataflow_Streaming" ] job_phrase: ["Run Load Tests Java CoGBK Dataflow Streaming"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Avro.yml b/.github/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Avro.yml index cc78482b18c2..d29acbfc765f 100644 --- a/.github/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Avro.yml +++ b/.github/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Avro.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PerformanceTests_BigQueryIO_Batch_Java_Avro"] job_phrase: ["Run BigQueryIO Batch Performance Test Java Avro"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Json.yml b/.github/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Json.yml index af71511838f9..067d0e4b95b8 100644 --- a/.github/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Json.yml +++ b/.github/workflows/beam_PerformanceTests_BigQueryIO_Batch_Java_Json.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PerformanceTests_BigQueryIO_Batch_Java_Json"] job_phrase: ["Run BigQueryIO Batch Performance Test Java Json"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PerformanceTests_BigQueryIO_Streaming_Java.yml b/.github/workflows/beam_PerformanceTests_BigQueryIO_Streaming_Java.yml index 3eaac8194a3a..bf10d4be522e 100644 --- a/.github/workflows/beam_PerformanceTests_BigQueryIO_Streaming_Java.yml +++ b/.github/workflows/beam_PerformanceTests_BigQueryIO_Streaming_Java.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PerformanceTests_BigQueryIO_Streaming_Java"] job_phrase: ["Run BigQueryIO Streaming Performance Test Java"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_BeamMetrics_Publish.yml b/.github/workflows/beam_PostCommit_BeamMetrics_Publish.yml index dfc86953d4b9..3ce0a48824aa 100644 --- a/.github/workflows/beam_PostCommit_BeamMetrics_Publish.yml +++ b/.github/workflows/beam_PostCommit_BeamMetrics_Publish.yml @@ -66,7 +66,7 @@ jobs: job_name: ["beam_PostCommit_BeamMetrics_Publish"] job_phrase: ["Run Beam Metrics Deployment"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Go.yml b/.github/workflows/beam_PostCommit_Go.yml index 1b27c006125e..dc1180314d67 100644 --- a/.github/workflows/beam_PostCommit_Go.yml +++ b/.github/workflows/beam_PostCommit_Go.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_Go"] job_phrase: ["Run Go PostCommit"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Go_Dataflow_ARM.yml b/.github/workflows/beam_PostCommit_Go_Dataflow_ARM.yml index 223640bbbaa3..64b935888cf0 100644 --- a/.github/workflows/beam_PostCommit_Go_Dataflow_ARM.yml +++ b/.github/workflows/beam_PostCommit_Go_Dataflow_ARM.yml @@ -66,7 +66,7 @@ jobs: job_name: ["beam_PostCommit_Go_Dataflow_ARM"] job_phrase: ["Run Go PostCommit Dataflow ARM"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Go_VR_Flink.yml b/.github/workflows/beam_PostCommit_Go_VR_Flink.yml index 0f7d9b0c5355..21dcf7f8e72a 100644 --- a/.github/workflows/beam_PostCommit_Go_VR_Flink.yml +++ b/.github/workflows/beam_PostCommit_Go_VR_Flink.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_Go_VR_Flink"] job_phrase: ["Run Go Flink ValidatesRunner"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Go_VR_Samza.yml b/.github/workflows/beam_PostCommit_Go_VR_Samza.yml index e199d04b11f2..4e647962ccac 100644 --- a/.github/workflows/beam_PostCommit_Go_VR_Samza.yml +++ b/.github/workflows/beam_PostCommit_Go_VR_Samza.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_Go_VR_Samza"] job_phrase: ["Run Go Samza ValidatesRunner"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Go_VR_Spark.yml b/.github/workflows/beam_PostCommit_Go_VR_Spark.yml index eed15c1fe966..0fc0f8d400de 100644 --- a/.github/workflows/beam_PostCommit_Go_VR_Spark.yml +++ b/.github/workflows/beam_PostCommit_Go_VR_Spark.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_Go_VR_Spark"] job_phrase: ["Run Go Spark ValidatesRunner"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java.yml b/.github/workflows/beam_PostCommit_Java.yml index a95291ea4d92..3e3cd79e8477 100644 --- a/.github/workflows/beam_PostCommit_Java.yml +++ b/.github/workflows/beam_PostCommit_Java.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Java PostCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Avro_Versions.yml b/.github/workflows/beam_PostCommit_Java_Avro_Versions.yml index 1fc83db7398e..1d551dcebf22 100644 --- a/.github/workflows/beam_PostCommit_Java_Avro_Versions.yml +++ b/.github/workflows/beam_PostCommit_Java_Avro_Versions.yml @@ -59,7 +59,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Java Avro Versions PostCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_DataflowV1.yml b/.github/workflows/beam_PostCommit_Java_DataflowV1.yml index 5c2350a86a25..02896bd3a383 100644 --- a/.github/workflows/beam_PostCommit_Java_DataflowV1.yml +++ b/.github/workflows/beam_PostCommit_Java_DataflowV1.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run PostCommit_Java_Dataflow' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_DataflowV2.yml b/.github/workflows/beam_PostCommit_Java_DataflowV2.yml index 0bbee0bb7e2d..56b1e6dc91bb 100644 --- a/.github/workflows/beam_PostCommit_Java_DataflowV2.yml +++ b/.github/workflows/beam_PostCommit_Java_DataflowV2.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run PostCommit_Java_DataflowV2' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml index fbec73dd433e..ec771bd4cefd 100644 --- a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml +++ b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml @@ -78,7 +78,7 @@ jobs: github. event_name == 'workflow_dispatch' || startswith(github.event.comment.body, 'Run Java_Examples_Dataflow_ARM PostCommit') steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java.yml b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java.yml index f34ffc878ea5..f50ae9d30f6c 100644 --- a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java.yml +++ b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_Java.yml @@ -66,7 +66,7 @@ jobs: github.event_name == 'schedule' || startswith(github.event.comment.body, 'Run Java examples on Dataflow Java') steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_V2.yml b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_V2.yml index f267b9b5b598..af01933be446 100644 --- a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_V2.yml +++ b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_V2.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Java Examples on Dataflow Runner V2' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_V2_Java.yml b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_V2_Java.yml index 817c5c82dba1..0dfea680f824 100644 --- a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_V2_Java.yml +++ b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_V2_Java.yml @@ -68,7 +68,7 @@ jobs: (contains(github.event.comment.body, 'Run Java') && contains(github.event.comment.body, 'Examples on Dataflow Runner V2')) steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Direct.yml b/.github/workflows/beam_PostCommit_Java_Examples_Direct.yml index b21fa394a8e8..b8014342042c 100644 --- a/.github/workflows/beam_PostCommit_Java_Examples_Direct.yml +++ b/.github/workflows/beam_PostCommit_Java_Examples_Direct.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Java Examples_Direct' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Flink.yml b/.github/workflows/beam_PostCommit_Java_Examples_Flink.yml index 59cb66457ec5..71e761d32ae7 100644 --- a/.github/workflows/beam_PostCommit_Java_Examples_Flink.yml +++ b/.github/workflows/beam_PostCommit_Java_Examples_Flink.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Java Examples_Flink' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Spark.yml b/.github/workflows/beam_PostCommit_Java_Examples_Spark.yml index 1a46dd737428..fd5c60952b7b 100644 --- a/.github/workflows/beam_PostCommit_Java_Examples_Spark.yml +++ b/.github/workflows/beam_PostCommit_Java_Examples_Spark.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Java Examples_Spark' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Hadoop_Versions.yml b/.github/workflows/beam_PostCommit_Java_Hadoop_Versions.yml index 16b0373b8aa9..da4606400e8b 100644 --- a/.github/workflows/beam_PostCommit_Java_Hadoop_Versions.yml +++ b/.github/workflows/beam_PostCommit_Java_Hadoop_Versions.yml @@ -62,7 +62,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run PostCommit_Java_Hadoop_Versions' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_IO_Performance_Tests.yml b/.github/workflows/beam_PostCommit_Java_IO_Performance_Tests.yml index e5c6befe2e76..eb1f43c51ed8 100644 --- a/.github/workflows/beam_PostCommit_Java_IO_Performance_Tests.yml +++ b/.github/workflows/beam_PostCommit_Java_IO_Performance_Tests.yml @@ -67,7 +67,7 @@ jobs: job_phrase: ["Run Java PostCommit IO Performance Tests"] test_case: ["GCSPerformanceTest", "BigTablePerformanceTest"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: @@ -76,7 +76,7 @@ jobs: github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.test_case }}) - name: Checkout release branch if: github.event_name == 'schedule' #This has scheduled runs run against the latest release - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: v2.50.0 #TODO(https://github.com/apache/beam/issues/28330) automate updating this repository: apache/beam diff --git a/.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11.yml b/.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11.yml index 25d843561f8f..6c35a10e5e92 100644 --- a/.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11.yml +++ b/.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java11.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_Java_Jpms_Dataflow_Java11"] job_phrase: ["Run Jpms Dataflow Java 11 PostCommit"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17.yml b/.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17.yml index 90fe8af087e6..5d0d2273cf55 100644 --- a/.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17.yml +++ b/.github/workflows/beam_PostCommit_Java_Jpms_Dataflow_Java17.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_Java_Jpms_Dataflow_Java17"] job_phrase: ["Run Jpms Dataflow Java 17 PostCommit"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java11.yml b/.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java11.yml index 006a974e12ba..4e26f29276b2 100644 --- a/.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java11.yml +++ b/.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java11.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_Java_Jpms_Direct_Java11"] job_phrase: ["Run Jpms Direct Java 11 PostCommit"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml b/.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml index b898d62a0c5d..0a4801d46c6c 100644 --- a/.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml +++ b/.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_Java_Jpms_Direct_Java17"] job_phrase: ["Run Jpms Direct Java 17 PostCommit"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml b/.github/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml index 83063ea0e4d1..aa5e3b40c9dd 100644 --- a/.github/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml +++ b/.github/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_Java_Jpms_Flink_Java11"] job_phrase: ["Run Jpms Flink Java 11 PostCommit"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml b/.github/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml index a31802c0edab..a81df741d9cc 100644 --- a/.github/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml +++ b/.github/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_Java_Jpms_Spark_Java11"] job_phrase: ["Run Jpms Spark Java 11 PostCommit"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_PVR_Flink_Streaming.yml b/.github/workflows/beam_PostCommit_Java_PVR_Flink_Streaming.yml index a649adc1ea08..709ec8215983 100644 --- a/.github/workflows/beam_PostCommit_Java_PVR_Flink_Streaming.yml +++ b/.github/workflows/beam_PostCommit_Java_PVR_Flink_Streaming.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Java Flink PortableValidatesRunner Streaming' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_PVR_Samza.yml b/.github/workflows/beam_PostCommit_Java_PVR_Samza.yml index f4defac94b87..06b88ee5bcb8 100644 --- a/.github/workflows/beam_PostCommit_Java_PVR_Samza.yml +++ b/.github/workflows/beam_PostCommit_Java_PVR_Samza.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Java Samza PortableValidatesRunner' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_PVR_Spark3_Streaming.yml b/.github/workflows/beam_PostCommit_Java_PVR_Spark3_Streaming.yml index dfbce38b1f6f..e87c62674c40 100644 --- a/.github/workflows/beam_PostCommit_Java_PVR_Spark3_Streaming.yml +++ b/.github/workflows/beam_PostCommit_Java_PVR_Spark3_Streaming.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Java Spark v3 PortableValidatesRunner Streaming' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_PVR_Spark_Batch.yml b/.github/workflows/beam_PostCommit_Java_PVR_Spark_Batch.yml index 130117da9740..ce4e17dc25ea 100644 --- a/.github/workflows/beam_PostCommit_Java_PVR_Spark_Batch.yml +++ b/.github/workflows/beam_PostCommit_Java_PVR_Spark_Batch.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Java Spark PortableValidatesRunner Batch' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Sickbay.yml b/.github/workflows/beam_PostCommit_Java_Sickbay.yml index 6851371c42ae..dc719c2d53dd 100644 --- a/.github/workflows/beam_PostCommit_Java_Sickbay.yml +++ b/.github/workflows/beam_PostCommit_Java_Sickbay.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Java Sickbay' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml index 2300daa376e9..92ddeeb8663e 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Dataflow ValidatesRunner' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_JavaVersions.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_JavaVersions.yml index 2b61cb216c1c..37ec6b145f35 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_JavaVersions.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_JavaVersions.yml @@ -66,7 +66,7 @@ jobs: github.event_name == 'schedule' || startswith(github.event.comment.body, 'Run Dataflow ValidatesRunner Java') steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml index ad114f5e0d3a..708ef0b7ad16 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_Streaming.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Dataflow Streaming ValidatesRunner' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml index 8dde25a81b25..aa665508ddee 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Java Dataflow V2 ValidatesRunner' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml index 2e61a8a6c2de..ca380efa5cb4 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Dataflow_V2_Streaming.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Java Dataflow V2 ValidatesRunner Streaming' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml index e2980628f36e..24e2c8014336 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Direct ValidatesRunner' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_JavaVersions.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_JavaVersions.yml index 0a4bb4d27af6..168cd245e0db 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_JavaVersions.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Direct_JavaVersions.yml @@ -66,7 +66,7 @@ jobs: github.event_name == 'schedule' || startswith(github.event.comment.body, 'Run Direct ValidatesRunner Java') steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml index 7b2baaaefb64..dbee3bc30782 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml @@ -62,7 +62,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Flink ValidatesRunner' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml index e745c548851f..d5de5fbfa3ff 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink_Java11.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || startswith(github.event.comment.body, 'Run Flink ValidatesRunner Java 11') steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Samza.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Samza.yml index e5981899b046..0613f794a4f4 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Samza.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Samza.yml @@ -62,7 +62,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Samza ValidatesRunner' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark.yml index e4d3bb1602e2..146a88c921d2 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark.yml @@ -62,7 +62,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Spark ValidatesRunner' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml index e838bdd419a5..fcaa3c6eabd4 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml @@ -62,7 +62,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Spark StructuredStreaming ValidatesRunner' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark_Java11.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark_Java11.yml index 5bf5fce71060..e6fe19393d13 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark_Java11.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Spark_Java11.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || startswith(github.event.comment.body, 'Run Spark ValidatesRunner Java 11') steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml index 16c4800ef72c..89364f32865b 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml @@ -62,7 +62,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Twister2 ValidatesRunner' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml index 7c214f2cbed6..e5e397c8c9d3 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml @@ -62,7 +62,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run ULR Loopback ValidatesRunner' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_PortableJar_Flink.yml b/.github/workflows/beam_PostCommit_PortableJar_Flink.yml index f3670a377df5..38c68059bdbe 100644 --- a/.github/workflows/beam_PostCommit_PortableJar_Flink.yml +++ b/.github/workflows/beam_PostCommit_PortableJar_Flink.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_PortableJar_Flink"] job_phrase: ["Run PortableJar_Flink PostCommit"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_PortableJar_Spark.yml b/.github/workflows/beam_PostCommit_PortableJar_Spark.yml index a4079b654874..4cb99f85f0fb 100644 --- a/.github/workflows/beam_PostCommit_PortableJar_Spark.yml +++ b/.github/workflows/beam_PostCommit_PortableJar_Spark.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_PortableJar_Spark"] job_phrase: ["Run PortableJar_Spark PostCommit"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Python.yml b/.github/workflows/beam_PostCommit_Python.yml index 23f322709efb..7dde6ed150df 100644 --- a/.github/workflows/beam_PostCommit_Python.yml +++ b/.github/workflows/beam_PostCommit_Python.yml @@ -66,7 +66,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Python PostCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Python_Examples_Dataflow.yml b/.github/workflows/beam_PostCommit_Python_Examples_Dataflow.yml index 811f1f098ebf..c7122523b79b 100644 --- a/.github/workflows/beam_PostCommit_Python_Examples_Dataflow.yml +++ b/.github/workflows/beam_PostCommit_Python_Examples_Dataflow.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_Python_Examples_Dataflow"] job_phrase: ["Run Python Examples_Dataflow"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Python_Examples_Direct.yml b/.github/workflows/beam_PostCommit_Python_Examples_Direct.yml index 913bac96a174..719d764781f0 100644 --- a/.github/workflows/beam_PostCommit_Python_Examples_Direct.yml +++ b/.github/workflows/beam_PostCommit_Python_Examples_Direct.yml @@ -64,7 +64,7 @@ jobs: job_phrase: ["Run Python Examples_Direct"] python_version: ['3.8','3.9','3.10','3.11'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Python_Examples_Flink.yml b/.github/workflows/beam_PostCommit_Python_Examples_Flink.yml index 8d2bc47f267e..be523408d72b 100644 --- a/.github/workflows/beam_PostCommit_Python_Examples_Flink.yml +++ b/.github/workflows/beam_PostCommit_Python_Examples_Flink.yml @@ -64,7 +64,7 @@ jobs: job_phrase: ["Run Python Examples_Flink"] python_version: ['3.8', '3.11'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Python_Examples_Spark.yml b/.github/workflows/beam_PostCommit_Python_Examples_Spark.yml index 9435720a1af9..01579d595007 100644 --- a/.github/workflows/beam_PostCommit_Python_Examples_Spark.yml +++ b/.github/workflows/beam_PostCommit_Python_Examples_Spark.yml @@ -64,7 +64,7 @@ jobs: job_phrase: ["Run Python Examples_Spark"] python_version: ['3.8', '3.11'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml b/.github/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml index 9f75d7588fa8..7778be56f58e 100644 --- a/.github/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml +++ b/.github/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_Python_MongoDBIO_IT"] job_phrase: ["Run Python MongoDBIO_IT"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml b/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml index dc2d2554b9a1..9ea80ea0576a 100644 --- a/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml +++ b/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml @@ -64,7 +64,7 @@ jobs: job_phrase: ["Run Python Dataflow ValidatesContainer"] python_version: ['3.8','3.9','3.10','3.11'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml b/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml index a4c518c930c2..b048cc1563d4 100644 --- a/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml +++ b/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml @@ -64,7 +64,7 @@ jobs: job_phrase: ["Run Python RC Dataflow ValidatesContainer"] python_version: ['3.8','3.9','3.10','3.11'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml index 7e43717cb507..f91b17f456e4 100644 --- a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml +++ b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml @@ -64,7 +64,7 @@ jobs: job_phrase: ["Run Python Dataflow ValidatesRunner"] python_version: ['3.8', '3.11'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml index dfb19f50737f..8e8205f4a80c 100644 --- a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml +++ b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml @@ -64,7 +64,7 @@ jobs: job_phrase: ["Run Python Flink ValidatesRunner"] python_version: ['3.8', '3.11'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml index 5878e1c34a53..8a2b42d59e5b 100644 --- a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml +++ b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml @@ -64,7 +64,7 @@ jobs: job_phrase: ["Run Python Samza ValidatesRunner"] python_version: ['3.8', '3.11'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml index 20bc343cfcca..ba6336052fa4 100644 --- a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml +++ b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml @@ -64,7 +64,7 @@ jobs: job_phrase: ["Run Python Spark ValidatesRunner"] python_version: ['3.8', '3.9', '3.11'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Python_Xlang_Gcp_Dataflow.yml b/.github/workflows/beam_PostCommit_Python_Xlang_Gcp_Dataflow.yml index c52dd1869432..34364bb38bcc 100644 --- a/.github/workflows/beam_PostCommit_Python_Xlang_Gcp_Dataflow.yml +++ b/.github/workflows/beam_PostCommit_Python_Xlang_Gcp_Dataflow.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_Python_Xlang_Gcp_Dataflow"] job_phrase: ["Run Python_Xlang_Gcp_Dataflow PostCommit"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Python_Xlang_Gcp_Direct.yml b/.github/workflows/beam_PostCommit_Python_Xlang_Gcp_Direct.yml index e1b5e401438f..f753b3cf15df 100644 --- a/.github/workflows/beam_PostCommit_Python_Xlang_Gcp_Direct.yml +++ b/.github/workflows/beam_PostCommit_Python_Xlang_Gcp_Direct.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_Python_Xlang_Gcp_Direct"] job_phrase: ["Run Python_Xlang_Gcp_Direct PostCommit"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Python_Xlang_IO_Dataflow.yml b/.github/workflows/beam_PostCommit_Python_Xlang_IO_Dataflow.yml index 0937fcbadb6c..6cc132935e66 100644 --- a/.github/workflows/beam_PostCommit_Python_Xlang_IO_Dataflow.yml +++ b/.github/workflows/beam_PostCommit_Python_Xlang_IO_Dataflow.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_Python_Xlang_IO_Dataflow"] job_phrase: ["Run Python_Xlang_IO_Dataflow PostCommit"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_SQL.yml b/.github/workflows/beam_PostCommit_SQL.yml index dc6ed87514a9..d27c5718d6f9 100644 --- a/.github/workflows/beam_PostCommit_SQL.yml +++ b/.github/workflows/beam_PostCommit_SQL.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run SQL PostCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Sickbay_Python.yml b/.github/workflows/beam_PostCommit_Sickbay_Python.yml index c791053520a4..465dc9966023 100644 --- a/.github/workflows/beam_PostCommit_Sickbay_Python.yml +++ b/.github/workflows/beam_PostCommit_Sickbay_Python.yml @@ -68,7 +68,7 @@ jobs: (startswith(github.event.comment.body, 'Run Python') && endswith(github.event.comment.body, 'PostCommit Sickbay')) steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_TransformService_Direct.yml b/.github/workflows/beam_PostCommit_TransformService_Direct.yml index 346abcc9951a..1d8b35098e45 100644 --- a/.github/workflows/beam_PostCommit_TransformService_Direct.yml +++ b/.github/workflows/beam_PostCommit_TransformService_Direct.yml @@ -63,7 +63,7 @@ jobs: job_phrase: ["Run TransformService_Direct PostCommit"] python_version: ['3.8','3.11'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Website_Publish.yml b/.github/workflows/beam_PostCommit_Website_Publish.yml index 1493c506772f..4cb99532543d 100644 --- a/.github/workflows/beam_PostCommit_Website_Publish.yml +++ b/.github/workflows/beam_PostCommit_Website_Publish.yml @@ -55,7 +55,7 @@ jobs: timeout-minutes: 30 name: beam_PostCommit_Website_Publish steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: run PostCommit Website Publish script uses: ./.github/actions/gradle-command-self-hosted-action with: diff --git a/.github/workflows/beam_PostCommit_Website_Test.yml b/.github/workflows/beam_PostCommit_Website_Test.yml index a411953e01f9..6155a45ef6a6 100644 --- a/.github/workflows/beam_PostCommit_Website_Test.yml +++ b/.github/workflows/beam_PostCommit_Website_Test.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_Website_Test"] job_phrase: ["Run Full Website Test"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_XVR_Direct.yml b/.github/workflows/beam_PostCommit_XVR_Direct.yml index 0f74e817f4e9..988b153986e9 100644 --- a/.github/workflows/beam_PostCommit_XVR_Direct.yml +++ b/.github/workflows/beam_PostCommit_XVR_Direct.yml @@ -63,7 +63,7 @@ jobs: job_phrase: ["Run XVR_Direct PostCommit"] python_version: ['3.8','3.11'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_XVR_Flink.yml b/.github/workflows/beam_PostCommit_XVR_Flink.yml index fd2cb95d9c32..0ab819c57a02 100644 --- a/.github/workflows/beam_PostCommit_XVR_Flink.yml +++ b/.github/workflows/beam_PostCommit_XVR_Flink.yml @@ -64,7 +64,7 @@ jobs: job_phrase: ["Run XVR_Flink PostCommit"] python_version: ['3.8','3.11'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml b/.github/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml index 2a463afbf8e4..687c77e572f2 100644 --- a/.github/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml +++ b/.github/workflows/beam_PostCommit_XVR_GoUsingJava_Dataflow.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_XVR_GoUsingJava_Dataflow"] job_phrase: ["Run XVR_GoUsingJava_Dataflow PostCommit"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_XVR_JavaUsingPython_Dataflow.yml b/.github/workflows/beam_PostCommit_XVR_JavaUsingPython_Dataflow.yml index dbdfbe2e7c73..71b3d27473e1 100644 --- a/.github/workflows/beam_PostCommit_XVR_JavaUsingPython_Dataflow.yml +++ b/.github/workflows/beam_PostCommit_XVR_JavaUsingPython_Dataflow.yml @@ -63,7 +63,7 @@ jobs: job_phrase: ["Run XVR_JavaUsingPython_Dataflow PostCommit"] python_version: ['3.8','3.11'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_XVR_PythonUsingJavaSQL_Dataflow.yml b/.github/workflows/beam_PostCommit_XVR_PythonUsingJavaSQL_Dataflow.yml index 68239e8329c2..ea3fd2cb86b8 100644 --- a/.github/workflows/beam_PostCommit_XVR_PythonUsingJavaSQL_Dataflow.yml +++ b/.github/workflows/beam_PostCommit_XVR_PythonUsingJavaSQL_Dataflow.yml @@ -62,7 +62,7 @@ jobs: job_name: ["beam_PostCommit_XVR_PythonUsingJavaSQL_Dataflow"] job_phrase: ["Run XVR_PythonUsingJavaSQL_Dataflow PostCommit"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_XVR_PythonUsingJava_Dataflow.yml b/.github/workflows/beam_PostCommit_XVR_PythonUsingJava_Dataflow.yml index eb184bf0c905..d575bbeabbc8 100644 --- a/.github/workflows/beam_PostCommit_XVR_PythonUsingJava_Dataflow.yml +++ b/.github/workflows/beam_PostCommit_XVR_PythonUsingJava_Dataflow.yml @@ -63,7 +63,7 @@ jobs: job_phrase: ["Run XVR_PythonUsingJava_Dataflow PostCommit"] python_version: ['3.8','3.11'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_XVR_Samza.yml b/.github/workflows/beam_PostCommit_XVR_Samza.yml index 1ae684bc6911..f808456b8c97 100644 --- a/.github/workflows/beam_PostCommit_XVR_Samza.yml +++ b/.github/workflows/beam_PostCommit_XVR_Samza.yml @@ -63,7 +63,7 @@ jobs: job_phrase: ["Run XVR_Samza PostCommit"] python_version: ['3.8','3.11'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_XVR_Spark3.yml b/.github/workflows/beam_PostCommit_XVR_Spark3.yml index 3cf4300222b3..8ca97bb23edb 100644 --- a/.github/workflows/beam_PostCommit_XVR_Spark3.yml +++ b/.github/workflows/beam_PostCommit_XVR_Spark3.yml @@ -63,7 +63,7 @@ jobs: job_phrase: ["Run XVR_Spark3 PostCommit"] python_version: ['3.8','3.11'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_CommunityMetrics.yml b/.github/workflows/beam_PreCommit_CommunityMetrics.yml index 2b59e6290a58..7606cc7227e2 100644 --- a/.github/workflows/beam_PreCommit_CommunityMetrics.yml +++ b/.github/workflows/beam_PreCommit_CommunityMetrics.yml @@ -71,7 +71,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run CommunityMetrics PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Go.yml b/.github/workflows/beam_PreCommit_Go.yml index 50c2b3a265b1..4626552d748a 100644 --- a/.github/workflows/beam_PreCommit_Go.yml +++ b/.github/workflows/beam_PreCommit_Go.yml @@ -71,7 +71,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run Go PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_GoPortable.yml b/.github/workflows/beam_PreCommit_GoPortable.yml index 397dac39b3f0..5d18d7e1bb19 100644 --- a/.github/workflows/beam_PreCommit_GoPortable.yml +++ b/.github/workflows/beam_PreCommit_GoPortable.yml @@ -71,7 +71,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run GoPortable PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_GoPrism.yml b/.github/workflows/beam_PreCommit_GoPrism.yml index 8091c23792c6..073a5d7c4a62 100644 --- a/.github/workflows/beam_PreCommit_GoPrism.yml +++ b/.github/workflows/beam_PreCommit_GoPrism.yml @@ -71,7 +71,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run GoPrism PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_ItFramework.yml b/.github/workflows/beam_PreCommit_ItFramework.yml index b28015138d95..3d976e4d8f95 100644 --- a/.github/workflows/beam_PreCommit_ItFramework.yml +++ b/.github/workflows/beam_PreCommit_ItFramework.yml @@ -74,7 +74,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run It_Framework PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java.yml b/.github/workflows/beam_PreCommit_Java.yml index bd9ada0bbb21..22910f6f99fd 100644 --- a/.github/workflows/beam_PreCommit_Java.yml +++ b/.github/workflows/beam_PreCommit_Java.yml @@ -158,7 +158,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run Java PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Amazon-Web-Services2_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Amazon-Web-Services2_IO_Direct.yml index 9f9fd15a5a2b..220dea3208cc 100644 --- a/.github/workflows/beam_PreCommit_Java_Amazon-Web-Services2_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Amazon-Web-Services2_IO_Direct.yml @@ -92,7 +92,7 @@ jobs: github.event.comment.body == 'Run Java_Amazon-Web-Services2_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Amazon-Web-Services_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Amazon-Web-Services_IO_Direct.yml index 559486ee39e7..03ee673e2e24 100644 --- a/.github/workflows/beam_PreCommit_Java_Amazon-Web-Services_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Amazon-Web-Services_IO_Direct.yml @@ -92,7 +92,7 @@ jobs: github.event.comment.body == 'Run Java_Amazon-Web-Services_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Amqp_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Amqp_IO_Direct.yml index d50564469d45..fede8fa6d548 100644 --- a/.github/workflows/beam_PreCommit_Java_Amqp_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Amqp_IO_Direct.yml @@ -74,7 +74,7 @@ jobs: github.event.comment.body == 'Run Java_Amqp_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Azure_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Azure_IO_Direct.yml index 5fff744408a2..7e20dd043cad 100644 --- a/.github/workflows/beam_PreCommit_Java_Azure_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Azure_IO_Direct.yml @@ -92,7 +92,7 @@ jobs: github.event.comment.body == 'Run Java_Azure_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Cassandra_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Cassandra_IO_Direct.yml index 6d661333d98a..5f63a25440b8 100644 --- a/.github/workflows/beam_PreCommit_Java_Cassandra_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Cassandra_IO_Direct.yml @@ -74,7 +74,7 @@ jobs: github.event.comment.body == 'Run Java_Cassandra_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Cdap_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Cdap_IO_Direct.yml index 4cd4eb850590..caa1c475f5b0 100644 --- a/.github/workflows/beam_PreCommit_Java_Cdap_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Cdap_IO_Direct.yml @@ -78,7 +78,7 @@ jobs: github.event.comment.body == 'Run Java_Cdap_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Clickhouse_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Clickhouse_IO_Direct.yml index f281182fc287..8f2a5cde3749 100644 --- a/.github/workflows/beam_PreCommit_Java_Clickhouse_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Clickhouse_IO_Direct.yml @@ -74,7 +74,7 @@ jobs: github.event.comment.body == 'Run Java_Clickhouse_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Csv_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Csv_IO_Direct.yml index 573d434b9dad..1c304500b567 100644 --- a/.github/workflows/beam_PreCommit_Java_Csv_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Csv_IO_Direct.yml @@ -74,7 +74,7 @@ jobs: github.event.comment.body == 'Run Java_Csv_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Debezium_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Debezium_IO_Direct.yml index 4558e3eb3b7b..67c746daccc6 100644 --- a/.github/workflows/beam_PreCommit_Java_Debezium_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Debezium_IO_Direct.yml @@ -69,7 +69,7 @@ jobs: github.event.comment.body == 'Run Java_Debezium_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_ElasticSearch_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_ElasticSearch_IO_Direct.yml index 263b298ff9ff..93baa9518613 100644 --- a/.github/workflows/beam_PreCommit_Java_ElasticSearch_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_ElasticSearch_IO_Direct.yml @@ -71,7 +71,7 @@ jobs: github.event.comment.body == 'Run Java_ElasticSearch_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Examples_Dataflow.yml b/.github/workflows/beam_PreCommit_Java_Examples_Dataflow.yml index 34e52fd15b6b..ff1dc5172c29 100644 --- a/.github/workflows/beam_PreCommit_Java_Examples_Dataflow.yml +++ b/.github/workflows/beam_PreCommit_Java_Examples_Dataflow.yml @@ -85,7 +85,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run Java_Examples_Dataflow PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java11.yml b/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java11.yml index 28abc3cd7947..8484360de7b1 100644 --- a/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java11.yml +++ b/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java11.yml @@ -86,7 +86,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run Java_Examples_Dataflow_Java11 PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java17.yml b/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java17.yml index e209035a5fcb..d31d1a2c2c16 100644 --- a/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java17.yml +++ b/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java17.yml @@ -79,7 +79,7 @@ jobs: github.event.comment.body == 'Run Java_Examples_Dataflow_Java17 PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_File-schema-transform_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_File-schema-transform_IO_Direct.yml index 9ad288d42029..d8b4b1a4983c 100644 --- a/.github/workflows/beam_PreCommit_Java_File-schema-transform_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_File-schema-transform_IO_Direct.yml @@ -69,7 +69,7 @@ jobs: github.event.comment.body == 'Run Java_File-schema-transform_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Flink_Versions.yml b/.github/workflows/beam_PreCommit_Java_Flink_Versions.yml index f001dad86049..855e06c827d1 100644 --- a/.github/workflows/beam_PreCommit_Java_Flink_Versions.yml +++ b/.github/workflows/beam_PreCommit_Java_Flink_Versions.yml @@ -73,7 +73,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Java_Flink_Versions PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_GCP_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_GCP_IO_Direct.yml index d9a5deb3dcfb..840b753d8190 100644 --- a/.github/workflows/beam_PreCommit_Java_GCP_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_GCP_IO_Direct.yml @@ -92,7 +92,7 @@ jobs: github.event.comment.body == 'Run Java_GCP_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_HBase_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_HBase_IO_Direct.yml index 681a37588043..5734e0e9d453 100644 --- a/.github/workflows/beam_PreCommit_Java_HBase_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_HBase_IO_Direct.yml @@ -76,7 +76,7 @@ jobs: github.event.comment.body == 'Run Java_HBase_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_HCatalog_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_HCatalog_IO_Direct.yml index ef333637fe98..6230c8dbae8a 100644 --- a/.github/workflows/beam_PreCommit_Java_HCatalog_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_HCatalog_IO_Direct.yml @@ -76,7 +76,7 @@ jobs: github.event.comment.body == 'Run Java_HCatalog_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Hadoop_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Hadoop_IO_Direct.yml index efdd46ca98f0..ad69f5fb0895 100644 --- a/.github/workflows/beam_PreCommit_Java_Hadoop_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Hadoop_IO_Direct.yml @@ -100,7 +100,7 @@ jobs: github.event.comment.body == 'Run Java_Hadoop_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_IOs_Direct.yml b/.github/workflows/beam_PreCommit_Java_IOs_Direct.yml index f135d5804f1f..b20e9886e701 100644 --- a/.github/workflows/beam_PreCommit_Java_IOs_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_IOs_Direct.yml @@ -74,7 +74,7 @@ jobs: github.event.comment.body == 'Run Java_IOs_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_InfluxDb_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_InfluxDb_IO_Direct.yml index d45ae862f6c6..9acbe5f8705a 100644 --- a/.github/workflows/beam_PreCommit_Java_InfluxDb_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_InfluxDb_IO_Direct.yml @@ -74,7 +74,7 @@ jobs: github.event.comment.body == 'Run Java_InfluxDb_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_JDBC_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_JDBC_IO_Direct.yml index 4a9850bfba9a..869c189c1c9d 100644 --- a/.github/workflows/beam_PreCommit_Java_JDBC_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_JDBC_IO_Direct.yml @@ -74,7 +74,7 @@ jobs: github.event.comment.body == 'Run Java_JDBC_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Jms_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Jms_IO_Direct.yml index dbcf0c350532..160e01520845 100644 --- a/.github/workflows/beam_PreCommit_Java_Jms_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Jms_IO_Direct.yml @@ -74,7 +74,7 @@ jobs: github.event.comment.body == 'Run Java_Jms_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Kafka_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Kafka_IO_Direct.yml index 9d3c95d5ef3d..64fc4122923b 100644 --- a/.github/workflows/beam_PreCommit_Java_Kafka_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Kafka_IO_Direct.yml @@ -82,7 +82,7 @@ jobs: github.event.comment.body == 'Run Java_Kafka_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Kinesis_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Kinesis_IO_Direct.yml index 2d974f86d8c1..d1788288d3d5 100644 --- a/.github/workflows/beam_PreCommit_Java_Kinesis_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Kinesis_IO_Direct.yml @@ -92,7 +92,7 @@ jobs: github.event.comment.body == 'Run Java_Kinesis_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Kudu_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Kudu_IO_Direct.yml index de151381a739..693cead4b031 100644 --- a/.github/workflows/beam_PreCommit_Java_Kudu_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Kudu_IO_Direct.yml @@ -74,7 +74,7 @@ jobs: github.event.comment.body == 'Run Java_Kudu_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_MongoDb_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_MongoDb_IO_Direct.yml index 9badf5d11006..472b0f7f5531 100644 --- a/.github/workflows/beam_PreCommit_Java_MongoDb_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_MongoDb_IO_Direct.yml @@ -74,7 +74,7 @@ jobs: github.event.comment.body == 'Run Java_MongoDb_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Mqtt_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Mqtt_IO_Direct.yml index d7df7e57da59..ad0e5ac9feda 100644 --- a/.github/workflows/beam_PreCommit_Java_Mqtt_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Mqtt_IO_Direct.yml @@ -74,7 +74,7 @@ jobs: github.event.comment.body == 'Run Java_Mqtt_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Neo4j_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Neo4j_IO_Direct.yml index f07719ca01ef..b6f51b243a8f 100644 --- a/.github/workflows/beam_PreCommit_Java_Neo4j_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Neo4j_IO_Direct.yml @@ -76,7 +76,7 @@ jobs: github.event.comment.body == 'Run Java_Neo4j_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_PVR_Flink_Batch.yml b/.github/workflows/beam_PreCommit_Java_PVR_Flink_Batch.yml index 2eba747cd18a..10652edb9420 100644 --- a/.github/workflows/beam_PreCommit_Java_PVR_Flink_Batch.yml +++ b/.github/workflows/beam_PreCommit_Java_PVR_Flink_Batch.yml @@ -75,7 +75,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run Java_PVR_Flink_Batch PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_PVR_Flink_Docker.yml b/.github/workflows/beam_PreCommit_Java_PVR_Flink_Docker.yml index af2d20f721db..30dd710e1848 100644 --- a/.github/workflows/beam_PreCommit_Java_PVR_Flink_Docker.yml +++ b/.github/workflows/beam_PreCommit_Java_PVR_Flink_Docker.yml @@ -85,7 +85,7 @@ jobs: github.event.comment.body == 'Run Java_PVR_Flink_Docker PreCommit' timeout-minutes: 240 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Parquet_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Parquet_IO_Direct.yml index f3e0fc45dc42..f2a27da20dd2 100644 --- a/.github/workflows/beam_PreCommit_Java_Parquet_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Parquet_IO_Direct.yml @@ -74,7 +74,7 @@ jobs: github.event.comment.body == 'Run Java_Parquet_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Pulsar_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Pulsar_IO_Direct.yml index 9101e148b1e3..ed7c55c98f4d 100644 --- a/.github/workflows/beam_PreCommit_Java_Pulsar_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Pulsar_IO_Direct.yml @@ -92,7 +92,7 @@ jobs: github.event.comment.body == 'Run Java_Pulsar_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_RabbitMq_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_RabbitMq_IO_Direct.yml index bae9465b9423..962c1526c90a 100644 --- a/.github/workflows/beam_PreCommit_Java_RabbitMq_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_RabbitMq_IO_Direct.yml @@ -74,7 +74,7 @@ jobs: github.event.comment.body == 'Run Java_RabbitMq_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Redis_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Redis_IO_Direct.yml index 23ae58d7fdf7..efd8d472f8ae 100644 --- a/.github/workflows/beam_PreCommit_Java_Redis_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Redis_IO_Direct.yml @@ -74,7 +74,7 @@ jobs: github.event.comment.body == 'Run Java_Redis_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_SingleStore_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_SingleStore_IO_Direct.yml index 3cb79c90496e..ad64ff286aab 100644 --- a/.github/workflows/beam_PreCommit_Java_SingleStore_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_SingleStore_IO_Direct.yml @@ -76,7 +76,7 @@ jobs: github.event.comment.body == 'Run Java_SingleStore_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Snowflake_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Snowflake_IO_Direct.yml index 3d106246954f..f8eaec4c11c4 100644 --- a/.github/workflows/beam_PreCommit_Java_Snowflake_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Snowflake_IO_Direct.yml @@ -78,7 +78,7 @@ jobs: github.event.comment.body == 'Run Java_Snowflake_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Solr_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Solr_IO_Direct.yml index ce87f25fec57..8b834682ffd5 100644 --- a/.github/workflows/beam_PreCommit_Java_Solr_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Solr_IO_Direct.yml @@ -74,7 +74,7 @@ jobs: github.event.comment.body == 'Run Java_Solr_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Spark3_Versions.yml b/.github/workflows/beam_PreCommit_Java_Spark3_Versions.yml index d5952e9658a3..2bb53629614d 100644 --- a/.github/workflows/beam_PreCommit_Java_Spark3_Versions.yml +++ b/.github/workflows/beam_PreCommit_Java_Spark3_Versions.yml @@ -76,7 +76,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run Java_Spark3_Versions PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Splunk_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Splunk_IO_Direct.yml index 25e33ae8ed96..ff0a31eb0d0e 100644 --- a/.github/workflows/beam_PreCommit_Java_Splunk_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Splunk_IO_Direct.yml @@ -74,7 +74,7 @@ jobs: github.event.comment.body == 'Run Java_Splunk_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Thrift_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Thrift_IO_Direct.yml index b0f2355f7c15..649e4ad28c65 100644 --- a/.github/workflows/beam_PreCommit_Java_Thrift_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Thrift_IO_Direct.yml @@ -74,7 +74,7 @@ jobs: github.event.comment.body == 'Run Java_Thrift_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Java_Tika_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Tika_IO_Direct.yml index 98aae62683f4..42fe14cd8338 100644 --- a/.github/workflows/beam_PreCommit_Java_Tika_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Tika_IO_Direct.yml @@ -74,7 +74,7 @@ jobs: github.event.comment.body == 'Run Java_Tika_IO_Direct PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Kotlin_Examples.yml b/.github/workflows/beam_PreCommit_Kotlin_Examples.yml index 892d0935a26a..b30163b12a47 100644 --- a/.github/workflows/beam_PreCommit_Kotlin_Examples.yml +++ b/.github/workflows/beam_PreCommit_Kotlin_Examples.yml @@ -86,7 +86,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run Kotlin_Examples PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Portable_Python.yml b/.github/workflows/beam_PreCommit_Portable_Python.yml index 7da598d58310..35e2c535f4ea 100644 --- a/.github/workflows/beam_PreCommit_Portable_Python.yml +++ b/.github/workflows/beam_PreCommit_Portable_Python.yml @@ -91,7 +91,7 @@ jobs: github.event_name == 'schedule' || startsWith(github.event.comment.body, 'Run Portable_Python PreCommit') steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Python.yml b/.github/workflows/beam_PreCommit_Python.yml index fe0a15aa651b..743da499e81a 100644 --- a/.github/workflows/beam_PreCommit_Python.yml +++ b/.github/workflows/beam_PreCommit_Python.yml @@ -72,7 +72,7 @@ jobs: github.event_name == 'workflow_dispatch' || startsWith(github.event.comment.body, 'Run Python PreCommit') steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_PythonDocker.yml b/.github/workflows/beam_PreCommit_PythonDocker.yml index 94f3d0bc7592..19ecf593a67c 100644 --- a/.github/workflows/beam_PreCommit_PythonDocker.yml +++ b/.github/workflows/beam_PreCommit_PythonDocker.yml @@ -71,7 +71,7 @@ jobs: github.event_name == 'schedule' || startsWith(github.event.comment.body, 'Run PythonDocker PreCommit') steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_PythonDocs.yml b/.github/workflows/beam_PreCommit_PythonDocs.yml index e5e30c2a00cc..e3ffd5a70cd7 100644 --- a/.github/workflows/beam_PreCommit_PythonDocs.yml +++ b/.github/workflows/beam_PreCommit_PythonDocs.yml @@ -71,7 +71,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run PythonDocs PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_PythonFormatter.yml b/.github/workflows/beam_PreCommit_PythonFormatter.yml index 2bfdaff26f68..08fa9dabc662 100644 --- a/.github/workflows/beam_PreCommit_PythonFormatter.yml +++ b/.github/workflows/beam_PreCommit_PythonFormatter.yml @@ -70,7 +70,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run PythonFormatter PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_PythonLint.yml b/.github/workflows/beam_PreCommit_PythonLint.yml index 859c6532654d..f26f423101d9 100644 --- a/.github/workflows/beam_PreCommit_PythonLint.yml +++ b/.github/workflows/beam_PreCommit_PythonLint.yml @@ -70,7 +70,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run PythonLint PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Python_Coverage.yml b/.github/workflows/beam_PreCommit_Python_Coverage.yml index 36cd2a6b42b4..9bb0b83c7a63 100644 --- a/.github/workflows/beam_PreCommit_Python_Coverage.yml +++ b/.github/workflows/beam_PreCommit_Python_Coverage.yml @@ -70,7 +70,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run Python_Coverage PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Python_Dataframes.yml b/.github/workflows/beam_PreCommit_Python_Dataframes.yml index 5b1ec1492d34..ab7d07cc72ad 100644 --- a/.github/workflows/beam_PreCommit_Python_Dataframes.yml +++ b/.github/workflows/beam_PreCommit_Python_Dataframes.yml @@ -72,7 +72,7 @@ jobs: github.event_name == 'workflow_dispatch' || startsWith(github.event.comment.body, 'Run Python_Dataframes PreCommit') steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Python_Examples.yml b/.github/workflows/beam_PreCommit_Python_Examples.yml index b0dd7c16d296..f82aef86de52 100644 --- a/.github/workflows/beam_PreCommit_Python_Examples.yml +++ b/.github/workflows/beam_PreCommit_Python_Examples.yml @@ -72,7 +72,7 @@ jobs: github.event_name == 'workflow_dispatch' || startsWith(github.event.comment.body, 'Run Python_Examples PreCommit') steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Python_Integration.yml b/.github/workflows/beam_PreCommit_Python_Integration.yml index ffe53bcfc323..2ef26516d15c 100644 --- a/.github/workflows/beam_PreCommit_Python_Integration.yml +++ b/.github/workflows/beam_PreCommit_Python_Integration.yml @@ -72,7 +72,7 @@ jobs: github.event_name == 'workflow_dispatch' || startsWith(github.event.comment.body, 'Run Python_Integration PreCommit') steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Python_PVR_Flink.yml b/.github/workflows/beam_PreCommit_Python_PVR_Flink.yml index 3dabf84fd505..131ec8c634a8 100644 --- a/.github/workflows/beam_PreCommit_Python_PVR_Flink.yml +++ b/.github/workflows/beam_PreCommit_Python_PVR_Flink.yml @@ -87,7 +87,7 @@ jobs: github.event.comment.body == 'Run Python_PVR_Flink PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Python_Runners.yml b/.github/workflows/beam_PreCommit_Python_Runners.yml index e317902a3eb8..c9f462d385a9 100644 --- a/.github/workflows/beam_PreCommit_Python_Runners.yml +++ b/.github/workflows/beam_PreCommit_Python_Runners.yml @@ -72,7 +72,7 @@ jobs: github.event_name == 'workflow_dispatch' || startsWith(github.event.comment.body, 'Run Python_Runners PreCommit') steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Python_Transforms.yml b/.github/workflows/beam_PreCommit_Python_Transforms.yml index 95f6945caad7..0627d8d5393a 100644 --- a/.github/workflows/beam_PreCommit_Python_Transforms.yml +++ b/.github/workflows/beam_PreCommit_Python_Transforms.yml @@ -72,7 +72,7 @@ jobs: github.event_name == 'workflow_dispatch' || startsWith(github.event.comment.body, 'Run Python_Transforms PreCommit') steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_RAT.yml b/.github/workflows/beam_PreCommit_RAT.yml index 3a09b28af7da..5c87644858b7 100644 --- a/.github/workflows/beam_PreCommit_RAT.yml +++ b/.github/workflows/beam_PreCommit_RAT.yml @@ -69,7 +69,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run RAT PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_SQL.yml b/.github/workflows/beam_PreCommit_SQL.yml index cb024bb7e1a3..18d84b219d5b 100644 --- a/.github/workflows/beam_PreCommit_SQL.yml +++ b/.github/workflows/beam_PreCommit_SQL.yml @@ -71,7 +71,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run SQL PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_SQL_Java11.yml b/.github/workflows/beam_PreCommit_SQL_Java11.yml index 13b4f6a0b8b7..29a89d7f54a8 100644 --- a/.github/workflows/beam_PreCommit_SQL_Java11.yml +++ b/.github/workflows/beam_PreCommit_SQL_Java11.yml @@ -71,7 +71,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run SQL_Java11 PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_SQL_Java17.yml b/.github/workflows/beam_PreCommit_SQL_Java17.yml index 68464009df2c..b493dd7f5fee 100644 --- a/.github/workflows/beam_PreCommit_SQL_Java17.yml +++ b/.github/workflows/beam_PreCommit_SQL_Java17.yml @@ -71,7 +71,7 @@ jobs: github.event.comment.body == 'Run SQL_Java17 PreCommit' runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Spotless.yml b/.github/workflows/beam_PreCommit_Spotless.yml index a703454a74fe..ba748cd7b136 100644 --- a/.github/workflows/beam_PreCommit_Spotless.yml +++ b/.github/workflows/beam_PreCommit_Spotless.yml @@ -79,7 +79,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run Spotless PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Typescript.yml b/.github/workflows/beam_PreCommit_Typescript.yml index 728dede57106..e83e04a1bae4 100644 --- a/.github/workflows/beam_PreCommit_Typescript.yml +++ b/.github/workflows/beam_PreCommit_Typescript.yml @@ -72,7 +72,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run Typescript PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Website.yml b/.github/workflows/beam_PreCommit_Website.yml index 7dd58b27768a..81fc578bc345 100644 --- a/.github/workflows/beam_PreCommit_Website.yml +++ b/.github/workflows/beam_PreCommit_Website.yml @@ -71,7 +71,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run Website PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Website_Stage_GCS.yml b/.github/workflows/beam_PreCommit_Website_Stage_GCS.yml index 119ece802164..cf9423f81670 100644 --- a/.github/workflows/beam_PreCommit_Website_Stage_GCS.yml +++ b/.github/workflows/beam_PreCommit_Website_Stage_GCS.yml @@ -73,7 +73,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run Website_Stage_GCS PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PreCommit_Whitespace.yml b/.github/workflows/beam_PreCommit_Whitespace.yml index 04705d49928c..b8ef21c8b077 100644 --- a/.github/workflows/beam_PreCommit_Whitespace.yml +++ b/.github/workflows/beam_PreCommit_Whitespace.yml @@ -70,7 +70,7 @@ jobs: github.event_name == 'workflow_dispatch' || github.event.comment.body == 'Run Whitespace PreCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_Python_ValidatesContainer_Dataflow_ARM.yml b/.github/workflows/beam_Python_ValidatesContainer_Dataflow_ARM.yml index 7e929b751e35..b56d81f17ca8 100644 --- a/.github/workflows/beam_Python_ValidatesContainer_Dataflow_ARM.yml +++ b/.github/workflows/beam_Python_ValidatesContainer_Dataflow_ARM.yml @@ -69,7 +69,7 @@ jobs: runs-on: [self-hosted, ubuntu-20.04, main] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_Release_NightlySnapshot.yml b/.github/workflows/beam_Release_NightlySnapshot.yml index 1b572f4aa0f3..a80105d2a7cb 100644 --- a/.github/workflows/beam_Release_NightlySnapshot.yml +++ b/.github/workflows/beam_Release_NightlySnapshot.yml @@ -54,7 +54,7 @@ jobs: github.event_name == 'schedule' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_Release_Python_NightlySnapshot.yml b/.github/workflows/beam_Release_Python_NightlySnapshot.yml index 63c9f7a8497f..58f879d83148 100644 --- a/.github/workflows/beam_Release_Python_NightlySnapshot.yml +++ b/.github/workflows/beam_Release_Python_NightlySnapshot.yml @@ -53,7 +53,7 @@ jobs: github.event_name == 'schedule' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/build_release_candidate.yml b/.github/workflows/build_release_candidate.yml index 9e7cbe741a26..2d826d572d90 100644 --- a/.github/workflows/build_release_candidate.yml +++ b/.github/workflows/build_release_candidate.yml @@ -50,7 +50,7 @@ jobs: runs-on: [self-hosted, ubuntu-20.04, main] steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: "v${{ github.event.inputs.RELEASE }}-RC${{ github.event.inputs.RC }}" repository: apache/beam @@ -154,7 +154,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: "v${{ github.event.inputs.RELEASE }}-RC${{ github.event.inputs.RC }}" repository: apache/beam @@ -204,13 +204,13 @@ jobs: SITE_ROOT_DIR: ${{ github.workspace }}/beam-site steps: - name: Checkout Beam Repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: "v${{ github.event.inputs.RELEASE }}-RC${{ github.event.inputs.RC }}" repository: apache/beam path: beam - name: Checkout Beam Site Repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: apache/beam-site path: beam-site diff --git a/.github/workflows/build_runner_image.yml b/.github/workflows/build_runner_image.yml index f64ada281d72..069b8b7db68c 100644 --- a/.github/workflows/build_runner_image.yml +++ b/.github/workflows/build_runner_image.yml @@ -36,7 +36,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} - name: Authenticate on GCP diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index 1dd586082331..5d67230d5498 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -54,7 +54,7 @@ jobs: py-versions-full: ${{ steps.set-py-versions.outputs.py-versions-full }} py-versions-test: ${{ steps.set-py-versions.outputs.py-versions-test }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: "Check are GCP variables set" run: "./scripts/ci/ci_check_are_gcp_variables_set.sh" id: check_gcp_variables @@ -87,7 +87,7 @@ jobs: rc_num: ${{ steps.get_rc_version.outputs.RC_NUM }} steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install python uses: actions/setup-python@v4 with: @@ -385,7 +385,7 @@ jobs: if: github.repository_owner == 'apache' && github.event_name == 'schedule' steps: - name: Checkout code on master branch - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive diff --git a/.github/workflows/cancel.yml b/.github/workflows/cancel.yml index df9172473f98..f826b22e043b 100644 --- a/.github/workflows/cancel.yml +++ b/.github/workflows/cancel.yml @@ -37,7 +37,7 @@ jobs: runs-on: ubuntu-latest steps: - name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )" - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive diff --git a/.github/workflows/choose_rc_commit.yml b/.github/workflows/choose_rc_commit.yml index d4de9d454a5c..0e51e5284a76 100644 --- a/.github/workflows/choose_rc_commit.yml +++ b/.github/workflows/choose_rc_commit.yml @@ -55,7 +55,7 @@ jobs: DEBUG: "" steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: release-${{ github.event.inputs.RELEASE }} - name: Set git config diff --git a/.github/workflows/code_completion_plugin_tests.yml b/.github/workflows/code_completion_plugin_tests.yml index 9244608543db..38ffd2fbd3f4 100644 --- a/.github/workflows/code_completion_plugin_tests.yml +++ b/.github/workflows/code_completion_plugin_tests.yml @@ -56,13 +56,13 @@ jobs: # Check out beam repository - name: Fetch beam Sources - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: main # Check out intellij community repository for tests - name: Fetch intellij-community Sources - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: JetBrains/intellij-community path: intellij diff --git a/.github/workflows/cut_release_branch.yml b/.github/workflows/cut_release_branch.yml index 4e104d78a445..4201d6018c60 100644 --- a/.github/workflows/cut_release_branch.yml +++ b/.github/workflows/cut_release_branch.yml @@ -68,7 +68,7 @@ jobs: exit 1 fi - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set git config run: | git config user.name $GITHUB_ACTOR @@ -116,7 +116,7 @@ jobs: exit 1 fi - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set git config run: | git config user.name $GITHUB_ACTOR @@ -184,7 +184,7 @@ jobs: tar zvxvf hub-linux-amd64-2.14.2.tgz sudo ./hub-linux-amd64-2.14.2/install echo "eval "$(hub alias -s)"" >> ~/.bashrc - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set git config run: | git config user.name $GITHUB_ACTOR diff --git a/.github/workflows/dask_runner_tests.yml b/.github/workflows/dask_runner_tests.yml index c4d8c9a1cf9e..46d2707ca063 100644 --- a/.github/workflows/dask_runner_tests.yml +++ b/.github/workflows/dask_runner_tests.yml @@ -39,7 +39,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install python uses: actions/setup-python@v4 with: @@ -73,7 +73,7 @@ jobs: ] steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install python uses: actions/setup-python@v4 with: diff --git a/.github/workflows/git_tag_released_version.yml b/.github/workflows/git_tag_released_version.yml index 871149bd26a1..0c6782603856 100644 --- a/.github/workflows/git_tag_released_version.yml +++ b/.github/workflows/git_tag_released_version.yml @@ -37,7 +37,7 @@ jobs: VERSION_PATH: ${{ github.event.inputs.VERSION_TAG }} steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set git config run: | git config user.name $GITHUB_ACTOR diff --git a/.github/workflows/go_tests.yml b/.github/workflows/go_tests.yml index f52065e60ebd..32b494da25be 100644 --- a/.github/workflows/go_tests.yml +++ b/.github/workflows/go_tests.yml @@ -40,7 +40,7 @@ jobs: name: Go Build steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 2 - uses: actions/setup-go@v4 diff --git a/.github/workflows/issue-tagger.yml b/.github/workflows/issue-tagger.yml index 39f92d87f788..dbfe2e996d5e 100644 --- a/.github/workflows/issue-tagger.yml +++ b/.github/workflows/issue-tagger.yml @@ -24,7 +24,7 @@ jobs: permissions: issues: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: damccorm/tag-ur-it@6fa72bbf1a2ea157b533d7e7abeafdb5855dbea5 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/java_tests.yml b/.github/workflows/java_tests.yml index 82e2d3ce3df5..d1180eebdf81 100644 --- a/.github/workflows/java_tests.yml +++ b/.github/workflows/java_tests.yml @@ -52,7 +52,7 @@ jobs: gcp-variables-set: ${{ steps.check_gcp_variables.outputs.gcp-variables-set }} steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "Check are GCP variables set" run: "./scripts/ci/ci_check_are_gcp_variables_set.sh" id: check_gcp_variables @@ -73,7 +73,7 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive @@ -132,7 +132,7 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive @@ -176,7 +176,7 @@ jobs: ) steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive diff --git a/.github/workflows/local_env_tests.yml b/.github/workflows/local_env_tests.yml index a689959dac7e..32e2975c0712 100644 --- a/.github/workflows/local_env_tests.yml +++ b/.github/workflows/local_env_tests.yml @@ -45,7 +45,7 @@ jobs: name: "Ubuntu run local environment shell script" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: go-version: '1.21' @@ -63,7 +63,7 @@ jobs: name: "Mac run local environment shell script" runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: go-version: '1.21' diff --git a/.github/workflows/playground_backend_precommit.yml b/.github/workflows/playground_backend_precommit.yml index dedac3db9299..de9edd4f2a94 100644 --- a/.github/workflows/playground_backend_precommit.yml +++ b/.github/workflows/playground_backend_precommit.yml @@ -37,7 +37,7 @@ jobs: JAVA_VERSION: '11' steps: - name: Check out the repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: diff --git a/.github/workflows/playground_frontend_test.yml b/.github/workflows/playground_frontend_test.yml index 543d166f432d..6f6e02a9697c 100644 --- a/.github/workflows/playground_frontend_test.yml +++ b/.github/workflows/playground_frontend_test.yml @@ -45,7 +45,7 @@ jobs: FLUTTER_VERSION: '3.10.4' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: 'Cache Flutter Dependencies' uses: actions/cache@v3 diff --git a/.github/workflows/pr-bot-new-prs.yml b/.github/workflows/pr-bot-new-prs.yml index 8ba27fbec3dc..ef825e067b7d 100644 --- a/.github/workflows/pr-bot-new-prs.yml +++ b/.github/workflows/pr-bot-new-prs.yml @@ -33,7 +33,7 @@ jobs: if: github.repository == 'apache/beam' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Node uses: actions/setup-node@v3 with: diff --git a/.github/workflows/pr-bot-pr-updates.yml b/.github/workflows/pr-bot-pr-updates.yml index d96a11368cb8..c882c18feeba 100644 --- a/.github/workflows/pr-bot-pr-updates.yml +++ b/.github/workflows/pr-bot-pr-updates.yml @@ -35,7 +35,7 @@ jobs: steps: # Pin to master so users can't do anything malicious on their own branch and run it here. - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: 'master' - name: Setup Node diff --git a/.github/workflows/pr-bot-prs-needing-attention.yml b/.github/workflows/pr-bot-prs-needing-attention.yml index e96d3983746b..9dff7c8565a4 100644 --- a/.github/workflows/pr-bot-prs-needing-attention.yml +++ b/.github/workflows/pr-bot-prs-needing-attention.yml @@ -33,7 +33,7 @@ jobs: if: github.repository == 'apache/beam' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Node uses: actions/setup-node@v3 with: diff --git a/.github/workflows/pr-bot-update-reviewers.yml b/.github/workflows/pr-bot-update-reviewers.yml index f3d343b12fb9..b4c41b66f9d6 100644 --- a/.github/workflows/pr-bot-update-reviewers.yml +++ b/.github/workflows/pr-bot-update-reviewers.yml @@ -33,7 +33,7 @@ jobs: if: github.repository == 'apache/beam' runs-on: [self-hosted, ubuntu-20.04] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Node uses: actions/setup-node@v3 with: diff --git a/.github/workflows/publish_github_release_notes.yml b/.github/workflows/publish_github_release_notes.yml index 246ce690f8b1..473e0deef83d 100644 --- a/.github/workflows/publish_github_release_notes.yml +++ b/.github/workflows/publish_github_release_notes.yml @@ -36,7 +36,7 @@ jobs: properties: ${{ steps.test-properties.outputs.properties }} steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - id: test-properties uses: ./.github/actions/setup-default-test-properties @@ -49,7 +49,7 @@ jobs: name: Publish Github Release Notes steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Publish github release notes run: | POST_PATH="website/www/site/content/en/blog/beam-${{env.RELEASE_VERSION}}.md" diff --git a/.github/workflows/python_dependency_tests.yml b/.github/workflows/python_dependency_tests.yml index aea77330f0ae..0c91e64b0db9 100644 --- a/.github/workflows/python_dependency_tests.yml +++ b/.github/workflows/python_dependency_tests.yml @@ -33,7 +33,7 @@ jobs: ] steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install python uses: actions/setup-python@v4 with: diff --git a/.github/workflows/python_tests.yml b/.github/workflows/python_tests.yml index ba7585c31bf5..406949eda96e 100644 --- a/.github/workflows/python_tests.yml +++ b/.github/workflows/python_tests.yml @@ -49,7 +49,7 @@ jobs: outputs: gcp-variables-set: ${{ steps.check_gcp_variables.outputs.gcp-variables-set }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: "Check are GCP variables set" run: "./scripts/ci/ci_check_are_gcp_variables_set.sh" id: check_gcp_variables @@ -73,7 +73,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install python uses: actions/setup-python@v4 with: @@ -108,7 +108,7 @@ jobs: ] steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install python uses: actions/setup-python@v4 with: @@ -143,7 +143,7 @@ jobs: python: ["3.8", "3.9", "3.10", "3.11"] steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install python uses: actions/setup-python@v4 with: @@ -171,7 +171,7 @@ jobs: python: ["3.8", "3.9", "3.10", "3.11"] steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install python uses: actions/setup-python@v4 with: diff --git a/.github/workflows/reportGenerator.yml b/.github/workflows/reportGenerator.yml index 44055cd56310..8f6bccddcfad 100644 --- a/.github/workflows/reportGenerator.yml +++ b/.github/workflows/reportGenerator.yml @@ -26,7 +26,7 @@ jobs: name: Generate issue report runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Node uses: actions/setup-node@v3 with: diff --git a/.github/workflows/run_perf_alert_tool.yml b/.github/workflows/run_perf_alert_tool.yml index 1cdcd858e61a..6946011f0617 100644 --- a/.github/workflows/run_perf_alert_tool.yml +++ b/.github/workflows/run_perf_alert_tool.yml @@ -35,7 +35,7 @@ jobs: issues: write steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install python uses: actions/setup-python@v4 with: diff --git a/.github/workflows/run_rc_validation.yml b/.github/workflows/run_rc_validation.yml index 720150b57450..4902fee81016 100644 --- a/.github/workflows/run_rc_validation.yml +++ b/.github/workflows/run_rc_validation.yml @@ -78,7 +78,7 @@ jobs: WORKING_BRANCH: "v${{github.event.inputs.RELEASE_VER}}-RC${{github.event.inputs.RC_NUM}}_validations" steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ env.RC_TAG }} - name: Setup GitHub CLI @@ -109,7 +109,7 @@ jobs: py_version: [3.8] steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{env.RC_TAG}} @@ -174,7 +174,7 @@ jobs: py_version: [3.8] steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{env.RC_TAG}} - name: Verify ENV values @@ -280,7 +280,7 @@ jobs: needs: generate_shared_pubsub steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{env.RC_TAG}} - name: Install Python @@ -355,7 +355,7 @@ jobs: needs: generate_shared_pubsub steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{env.RC_TAG}} @@ -403,7 +403,7 @@ jobs: needs: [generate_shared_pubsub] steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{env.RC_TAG}} @@ -456,7 +456,7 @@ jobs: needs: [generate_shared_pubsub] steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{env.RC_TAG}} - name: Install Python @@ -505,7 +505,7 @@ jobs: needs: [generate_shared_pubsub] steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{env.RC_TAG}} - name: Install Python diff --git a/.github/workflows/tour_of_beam_backend.yml b/.github/workflows/tour_of_beam_backend.yml index 665cec4e42cd..5c67c9f54fe3 100644 --- a/.github/workflows/tour_of_beam_backend.yml +++ b/.github/workflows/tour_of_beam_backend.yml @@ -41,7 +41,7 @@ jobs: run: working-directory: ./learning/tour-of-beam/backend steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: # pin to the biggest Go version supported by Cloud Functions runtime diff --git a/.github/workflows/tour_of_beam_backend_integration.yml b/.github/workflows/tour_of_beam_backend_integration.yml index ab644358c977..1eb4b66f5868 100644 --- a/.github/workflows/tour_of_beam_backend_integration.yml +++ b/.github/workflows/tour_of_beam_backend_integration.yml @@ -74,7 +74,7 @@ jobs: run: working-directory: ./learning/tour-of-beam/backend steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: # pin to the biggest Go version supported by Cloud Functions runtime diff --git a/.github/workflows/tour_of_beam_frontend_test.yml b/.github/workflows/tour_of_beam_frontend_test.yml index 8880bc287266..5337bb7dd720 100644 --- a/.github/workflows/tour_of_beam_frontend_test.yml +++ b/.github/workflows/tour_of_beam_frontend_test.yml @@ -47,7 +47,7 @@ jobs: FLUTTER_VERSION: '3.10.4' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: 'Cache Flutter Dependencies' uses: actions/cache@v3 diff --git a/.github/workflows/typescript_tests.yml b/.github/workflows/typescript_tests.yml index 542673cbbca8..825b2808af5b 100644 --- a/.github/workflows/typescript_tests.yml +++ b/.github/workflows/typescript_tests.yml @@ -45,7 +45,7 @@ jobs: os: [[self-hosted, ubuntu-20.04], macos-latest, [self-hosted, windows-server-2019]] steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive @@ -74,7 +74,7 @@ jobs: fail-fast: false steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive @@ -109,7 +109,7 @@ jobs: outputs: gcp-variables-set: ${{ steps.check_gcp_variables.outputs.gcp-variables-set }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: "Check are GCP variables set" run: "./scripts/ci/ci_check_are_gcp_variables_set.sh" id: check_gcp_variables @@ -131,7 +131,7 @@ jobs: fail-fast: false steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: persist-credentials: false submodules: recursive diff --git a/.github/workflows/update_python_dependencies.yml b/.github/workflows/update_python_dependencies.yml index d2b8fccdb586..43bed87a42f3 100644 --- a/.github/workflows/update_python_dependencies.yml +++ b/.github/workflows/update_python_dependencies.yml @@ -38,7 +38,7 @@ jobs: properties: ${{ steps.test-properties.outputs.properties }} steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - id: test-properties uses: ./.github/actions/setup-default-test-properties @@ -48,7 +48,7 @@ jobs: name: Update Python Dependencies steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup environment uses: ./.github/actions/setup-self-hosted-action - name: Update Python Dependencies From c7274e9b151f40ac99574e618a8bf66ee234716d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Sep 2023 11:00:31 -0400 Subject: [PATCH 066/105] Bump github.com/aws/aws-sdk-go-v2/config in /sdks (#28519) Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.18.39 to 1.18.40. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.18.39...config/v1.18.40) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/config dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdks/go.mod | 12 ++++++------ sdks/go.sum | 15 ++++++++++----- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/sdks/go.mod b/sdks/go.mod index e28e54896b87..30ac185b8165 100644 --- a/sdks/go.mod +++ b/sdks/go.mod @@ -31,8 +31,8 @@ require ( cloud.google.com/go/spanner v1.49.0 cloud.google.com/go/storage v1.33.0 github.com/aws/aws-sdk-go-v2 v1.21.0 - github.com/aws/aws-sdk-go-v2/config v1.18.39 - github.com/aws/aws-sdk-go-v2/credentials v1.13.37 + github.com/aws/aws-sdk-go-v2/config v1.18.40 + github.com/aws/aws-sdk-go-v2/credentials v1.13.38 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.83 github.com/aws/aws-sdk-go-v2/service/s3 v1.38.5 github.com/aws/smithy-go v1.14.2 @@ -96,9 +96,9 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.14.0 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.16.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.22.0 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -107,7 +107,7 @@ require ( github.com/containerd/containerd v1.7.3 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/docker/distribution v2.8.2+incompatible // indirect - github.com/docker/docker v24.0.5+incompatible // indirect; but required to resolve issue docker has with go1.20 + github.com/docker/docker v24.0.5+incompatible // but required to resolve issue docker has with go1.20 github.com/docker/go-units v0.5.0 // indirect github.com/envoyproxy/go-control-plane v0.11.1 // indirect github.com/envoyproxy/protoc-gen-validate v1.0.2 // indirect diff --git a/sdks/go.sum b/sdks/go.sum index f60c5c87fb1a..15fa89e26c32 100644 --- a/sdks/go.sum +++ b/sdks/go.sum @@ -85,11 +85,13 @@ github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pf github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 h1:OPLEkmhXf6xFPiz0bLeDArZIDx1NNS4oJyG4nv3Gct0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13/go.mod h1:gpAbvyDGQFozTEmlTFO8XcQKHzubdq0LzRyJpG6MiXM= github.com/aws/aws-sdk-go-v2/config v1.5.0/go.mod h1:RWlPOAW3E3tbtNAqTwvSW54Of/yP3oiZXMI0xfUdjyA= -github.com/aws/aws-sdk-go-v2/config v1.18.39 h1:oPVyh6fuu/u4OiW4qcuQyEtk7U7uuNBmHmJSLg1AJsQ= github.com/aws/aws-sdk-go-v2/config v1.18.39/go.mod h1:+NH/ZigdPckFpgB1TRcRuWCB/Kbbvkxc/iNAKTq5RhE= +github.com/aws/aws-sdk-go-v2/config v1.18.40 h1:dbu1llI/nTIL+r6sYHMeVLl99DM8J8/o1I4EPurnhLg= +github.com/aws/aws-sdk-go-v2/config v1.18.40/go.mod h1:JjrCZQwSPGCoZRQzKHyZNNueaKO+kFaEy2sR6mCzd90= github.com/aws/aws-sdk-go-v2/credentials v1.3.1/go.mod h1:r0n73xwsIVagq8RsxmZbGSRQFj9As3je72C2WzUIToc= -github.com/aws/aws-sdk-go-v2/credentials v1.13.37 h1:BvEdm09+ZEh2XtN+PVHPcYwKY3wIeB6pw7vPRM4M9/U= github.com/aws/aws-sdk-go-v2/credentials v1.13.37/go.mod h1:ACLrdkd4CLZyXOghZ8IYumQbcooAcp2jo/s2xsFH8IM= +github.com/aws/aws-sdk-go-v2/credentials v1.13.38 h1:gDAuCdVlA4lmmgQhvpZlscwicloCqH44vkxLklGkQLA= +github.com/aws/aws-sdk-go-v2/credentials v1.13.38/go.mod h1:sD4G/Ybgp6s89mWIES3Xn97CsRLpxvz9uVSdv0UxY8I= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.3.0/go.mod h1:2LAuqPx1I6jNfaGDucWfA2zqQCYCOMCDHiCOciALyNw= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 h1:uDZJF1hu0EVT/4bogChk8DyjSF6fof6uL/0Y26Ma7Fg= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11/go.mod h1:TEPP4tENqBGO99KwVpV9MlOX4NSrSLP8u3KRy2CDwA8= @@ -120,13 +122,16 @@ github.com/aws/aws-sdk-go-v2/service/s3 v1.11.1/go.mod h1:XLAGFrEjbvMCLvAtWLLP32 github.com/aws/aws-sdk-go-v2/service/s3 v1.38.5 h1:A42xdtStObqy7NGvzZKpnyNXvoOmm+FENobZ0/ssHWk= github.com/aws/aws-sdk-go-v2/service/s3 v1.38.5/go.mod h1:rDGMZA7f4pbmTtPOk5v5UM2lmX6UAbRnMDJeDvnH7AM= github.com/aws/aws-sdk-go-v2/service/sso v1.3.1/go.mod h1:J3A3RGUvuCZjvSuZEcOpHDnzZP/sKbhDWV2T1EOzFIM= -github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 h1:2PylFCfKCEDv6PeSN09pC/VUiRd10wi1VfHG5FrW0/g= github.com/aws/aws-sdk-go-v2/service/sso v1.13.6/go.mod h1:fIAwKQKBFu90pBxx07BFOMJLpRUGu8VOzLJakeY+0K4= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6 h1:pSB560BbVj9ZlJZF4WYj5zsytWHWKxg+NgyGV4B2L58= +github.com/aws/aws-sdk-go-v2/service/sso v1.14.0 h1:AR/hlTsCyk1CwlyKnPFvIMvnONydRjDDRT9OGb0i+/g= +github.com/aws/aws-sdk-go-v2/service/sso v1.14.0/go.mod h1:fIAwKQKBFu90pBxx07BFOMJLpRUGu8VOzLJakeY+0K4= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6/go.mod h1:yygr8ACQRY2PrEcy3xsUI357stq2AxnFM6DIsR9lij4= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.16.0 h1:vbgiXuhtn49+erlPrgIvQ+J32rg1HseaPf8lEpKbkxQ= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.16.0/go.mod h1:yygr8ACQRY2PrEcy3xsUI357stq2AxnFM6DIsR9lij4= github.com/aws/aws-sdk-go-v2/service/sts v1.6.0/go.mod h1:q7o0j7d7HrJk/vr9uUt3BVRASvcU7gYZB9PUgPiByXg= -github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 h1:CQBFElb0LS8RojMJlxRSo/HXipvTZW2S44Lt9Mk2aYQ= github.com/aws/aws-sdk-go-v2/service/sts v1.21.5/go.mod h1:VC7JDqsqiwXukYEDjoHh9U0fOJtNWh04FPQz4ct4GGU= +github.com/aws/aws-sdk-go-v2/service/sts v1.22.0 h1:s4bioTgjSFRwOoyEFzAVCmFmoowBgjTR8gkrF/sQ4wk= +github.com/aws/aws-sdk-go-v2/service/sts v1.22.0/go.mod h1:VC7JDqsqiwXukYEDjoHh9U0fOJtNWh04FPQz4ct4GGU= github.com/aws/smithy-go v1.6.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.14.2 h1:MJU9hqBGbvWZdApzpvoF2WAIJDbtjK2NDJSiJP7HblQ= github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= From 9c4e8bff7c97f513f9df498debdbcc08698413d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20=C3=96jeling?= <51084516+johannaojeling@users.noreply.github.com> Date: Tue, 19 Sep 2023 17:03:11 +0200 Subject: [PATCH 067/105] Support writes to Postgres with pgx driver (#28521) --- sdks/go/pkg/beam/io/databaseio/writer.go | 2 +- sdks/go/pkg/beam/io/databaseio/writer_test.go | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/sdks/go/pkg/beam/io/databaseio/writer.go b/sdks/go/pkg/beam/io/databaseio/writer.go index 4719831581ff..ebd805b080d7 100644 --- a/sdks/go/pkg/beam/io/databaseio/writer.go +++ b/sdks/go/pkg/beam/io/databaseio/writer.go @@ -108,7 +108,7 @@ type valueTemplateGenerator struct { func (v *valueTemplateGenerator) generate(rowCount int, columnColunt int) string { switch v.driver { - case "postgres": + case "postgres", "pgx": // the point is to generate ($1,$2),($3,$4) valueTemplates := make([]string, rowCount) for i := 0; i < rowCount; i++ { diff --git a/sdks/go/pkg/beam/io/databaseio/writer_test.go b/sdks/go/pkg/beam/io/databaseio/writer_test.go index 24f617a9b857..91d7fb9246dd 100644 --- a/sdks/go/pkg/beam/io/databaseio/writer_test.go +++ b/sdks/go/pkg/beam/io/databaseio/writer_test.go @@ -39,6 +39,12 @@ func TestValueTemplateGenerator_generate(t *testing.T) { columnCount: 10, expected: "", }, + { + generator: &valueTemplateGenerator{"pgx"}, + rowCount: 4, + columnCount: 3, + expected: "($1,$2,$3),($4,$5,$6),($7,$8,$9),($10,$11,$12)", + }, { generator: &valueTemplateGenerator{"mysql"}, rowCount: 4, From 346541b1b824e0343a736ce4e320667b8d73ec06 Mon Sep 17 00:00:00 2001 From: gDuperran <133862050+gDuperran@users.noreply.github.com> Date: Tue, 19 Sep 2023 17:49:49 +0200 Subject: [PATCH 068/105] Update build.gradle -- remove duplicate deps (#28377) --- sdks/java/io/google-ads/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/sdks/java/io/google-ads/build.gradle b/sdks/java/io/google-ads/build.gradle index e874c0b3cb1b..f3dbb19e04f4 100644 --- a/sdks/java/io/google-ads/build.gradle +++ b/sdks/java/io/google-ads/build.gradle @@ -27,7 +27,6 @@ dependencies { implementation project(path: ":sdks:java:extensions:google-cloud-platform-core") implementation library.java.jackson_annotations implementation library.java.gax - implementation library.java.google_ads implementation library.java.google_auth_library_credentials implementation library.java.google_auth_library_oauth2_http implementation library.java.protobuf_java From 82228873f413de07f9eebcd430ef739c3940ba26 Mon Sep 17 00:00:00 2001 From: Danny McCormick Date: Tue, 19 Sep 2023 12:29:18 -0400 Subject: [PATCH 069/105] Fix numpy install when building wheels (#28532) * Fix numpy install when building wheels * Syntax fix --- .github/workflows/build_wheels.yml | 4 ++-- sdks/python/build.gradle | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index 5d67230d5498..1028dd79af02 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -269,7 +269,7 @@ jobs: # TODO: https://github.com/apache/beam/issues/23048 CIBW_SKIP: "*-musllinux_*" CIBW_ENVIRONMENT: "SETUPTOOLS_USE_DISTUTILS=stdlib" - CIBW_BEFORE_BUILD: pip install cython==0.29.36 numpy && pip install --upgrade setuptools + CIBW_BEFORE_BUILD: pip install cython==0.29.36 numpy --config-settings=setup-args="-Dallow-noblas=true" && pip install --upgrade setuptools run: cibuildwheel --print-build-identifiers && cibuildwheel --output-dir wheelhouse shell: bash - name: install sha512sum on MacOS @@ -295,7 +295,7 @@ jobs: # TODO: https://github.com/apache/beam/issues/23048 CIBW_SKIP: "*-musllinux_*" CIBW_ENVIRONMENT: "SETUPTOOLS_USE_DISTUTILS=stdlib" - CIBW_BEFORE_BUILD: pip install cython==0.29.36 numpy && pip install --upgrade setuptools + CIBW_BEFORE_BUILD: pip install cython==0.29.36 numpy --config-settings=setup-args="-Dallow-noblas=true" && pip install --upgrade setuptools run: cibuildwheel --print-build-identifiers && cibuildwheel --output-dir wheelhouse shell: bash - name: Add RC checksums diff --git a/sdks/python/build.gradle b/sdks/python/build.gradle index 88d0abae83bd..762bed268d63 100644 --- a/sdks/python/build.gradle +++ b/sdks/python/build.gradle @@ -96,7 +96,7 @@ platform_identifiers_map.each { platform, idsuffix -> exec { environment CIBW_BUILD: "cp${pyversion}-${idsuffix}" environment CIBW_ENVIRONMENT: "SETUPTOOLS_USE_DISTUTILS=stdlib" - environment CIBW_BEFORE_BUILD: "pip install cython==0.29.36 numpy && pip install --upgrade setuptools" + environment CIBW_BEFORE_BUILD: "pip install cython==0.29.36 numpy --config-settings=setup-args='-Dallow-noblas=true' && pip install --upgrade setuptools" // note: sync cibuildwheel version with GitHub Action // .github/workflow/build_wheel.yml:build_wheels "Install cibuildwheel" step executable 'sh' From 3915c85b84207f5f2c1e9dc4dd441769f3c586a5 Mon Sep 17 00:00:00 2001 From: Svetak Sundhar Date: Tue, 19 Sep 2023 17:47:57 +0000 Subject: [PATCH 070/105] [Document]: `HealthcareApiClient` methods. (#28482) * Create HealthcareUtils file with shared resources * revert * Document methods in HealthcareApiClient * fix spotless * caps --- .../gcp/healthcare/HealthcareApiClient.java | 242 +++++++++++++----- 1 file changed, 178 insertions(+), 64 deletions(-) diff --git a/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/healthcare/HealthcareApiClient.java b/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/healthcare/HealthcareApiClient.java index a72b55bd7209..39c30b949425 100644 --- a/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/healthcare/HealthcareApiClient.java +++ b/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/healthcare/HealthcareApiClient.java @@ -36,60 +36,68 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.joda.time.Instant; -/** Defines a client that talks to the Cloud Healthcare API (version v1). */ +/** Defines a client to communicate with the GCP HCLS API (version v1). */ public interface HealthcareApiClient { /** - * Fetches an Hl7v2 message by its name from a Hl7v2 store. + * Gets a Hl7v2 message by its name from a Hl7v2 store. * - * @param msgName the msg name - * @return HL7v2 message - * @throws IOException the io exception - * @throws ParseException the parse exception + * @param msgName The message name to be retrieved. + * @return The HL7v2 message. + * @throws IOException The IO Exception. + * @throws ParseException The Parse Exception. */ Message getHL7v2Message(String msgName) throws IOException, ParseException; /** - * Delete hl 7 v 2 message empty. + * Deletes an HL7v2 message. * - * @param msgName the msg name - * @return the empty - * @throws IOException the io exception + * @param msgName The message name to be deleted. + * @return Empty. + * @throws IOException The IO Exception. */ Empty deleteHL7v2Message(String msgName) throws IOException; /** - * Gets HL7v2 store. + * Gets an HL7v2 store. * - * @param storeName the store name - * @return the HL7v2 store - * @throws IOException the io exception + * @param storeName The store name to be retrieved. + * @return The HL7v2 store. + * @throws IOException The IO Exception. */ Hl7V2Store getHL7v2Store(String storeName) throws IOException; /** - * Gets earliest hl 7 v 2 send time. + * Gets the earliest HL7v2 send time. * - * @param hl7v2Store the hl 7 v 2 store - * @param filter the filter - * @return the earliest hl 7 v 2 send time - * @throws IOException the io exception + * @param hl7v2Store The HL7v2 store. + * @param filter the filter to be matched on. + * @return The earliest HL7v2 send time. + * @throws IOException The IO Exception. */ Instant getEarliestHL7v2SendTime(String hl7v2Store, @Nullable String filter) throws IOException; + /** + * Gets the latest HL7v2 send time. + * + * @param hl7v2Store The HL7v2 store. + * @param filter The filter to be matched on. + * @return The latest HL7v2 send time. + * @throws IOException The IO Exception. + */ Instant getLatestHL7v2SendTime(String hl7v2Store, @Nullable String filter) throws IOException; /** - * Make send time bound hl 7 v 2 list request. + * Time Bound HL7v2 list request. * - * @param hl7v2Store the hl 7 v 2 store - * @param start the start - * @param end the end - * @param otherFilter the other filter - * @param orderBy the order by - * @param pageToken the page token - * @return the list messages response - * @throws IOException the io exception + * @param hl7v2Store The HL7v2 store. + * @param start Start time. + * @param end End time. + * @param otherFilter The filter outside of the sendTime. + * @param orderBy Order by. + * @param pageToken The page token. + * @return HTTP List response. + * @throws IOException The IO Exception. */ ListMessagesResponse makeSendTimeBoundHL7v2ListRequest( String hl7v2Store, @@ -103,12 +111,12 @@ ListMessagesResponse makeSendTimeBoundHL7v2ListRequest( /** * Make hl 7 v 2 list request list messages response. * - * @param hl7v2Store the hl 7 v 2 store - * @param filter the filter - * @param orderBy the order by - * @param pageToken the page token - * @return the list messages response - * @throws IOException the io exception + * @param hl7v2Store The HL7v2 Store. + * @param filter The Filter. + * @param orderBy Order by. + * @param pageToken The Page Token. + * @return HTTP List response. + * @throws IOException The IO Exception. */ ListMessagesResponse makeHL7v2ListRequest( String hl7v2Store, @@ -118,38 +126,89 @@ ListMessagesResponse makeHL7v2ListRequest( throws IOException; /** - * Ingest hl 7 v 2 message ingest message response. + * Ingest an HL7v2 message. * - * @param hl7v2Store the hl 7 v 2 store - * @param msg the msg - * @return the ingest message response - * @throws IOException the io exception + * @param hl7v2Store The HL7v2 store of the message. + * @param msg The message. + * @return Empty. + * @throws IOException The IO Exception. */ IngestMessageResponse ingestHL7v2Message(String hl7v2Store, Message msg) throws IOException; /** - * Create hl 7 v 2 message message. + * Creates an HL7v2 message. * - * @param hl7v2Store the hl 7 v 2 store - * @param msg the msg - * @return the message - * @throws IOException the io exception + * @param hl7v2Store The HL7v2 store to create a message in. + * @param msg The message to create. + * @return Empty. + * @throws IOException The IO Exception. */ Message createHL7v2Message(String hl7v2Store, Message msg) throws IOException; + /** + * Deletes an HL7v2 store. + * + * @param store The HL7v2 store to be deleted. + * @return Empty. + * @throws IOException The IO Exception. + */ + Empty deleteHL7v2Store(String store) throws IOException; + + /** + * Importing a FHIR resource from GCS. + * + * @param fhirStore the FhirStore to import into. + * @param gcsSourcePath the GCS Path of resource. + * @param contentStructure The content structure. + * @return Empty. + * @throws IOException the io exception + */ Operation importFhirResource( String fhirStore, String gcsSourcePath, @Nullable String contentStructure) throws IOException; + /** + * Export a FHIR Resource to GCS. + * + * @param fhirStore the FhirStore of the resource. + * @param gcsDestinationPrefix GCS Destination Prefix to export to. + * @return Empty. + * @throws IOException The IO Exception. + */ Operation exportFhirResourceToGcs(String fhirStore, String gcsDestinationPrefix) throws IOException; + /** + * Export a FHIR Resource to BigQuery. + * + * @param fhirStore the FhirStore of the resource. + * @param bigQueryDatasetUri The BQ Dataset URI to export to. + * @return Empty. + * @throws IOException The IO Exception. + */ Operation exportFhirResourceToBigQuery(String fhirStore, String bigQueryDatasetUri) throws IOException; + /** + * Deidentify a GCP FHIR Store and write the result into a new FHIR Store. + * + * @param sourceFhirStore the FhirStore to be deidentified. + * @param destinationFhirStore the FhirStore that the deidentified data will be written to. + * @param deidConfig the deidCongig specifying form of deidentification. + * @return Empty. + * @throws IOException The IO Exception. + */ Operation deidentifyFhirStore( String sourceFhirStore, String destinationFhirStore, DeidentifyConfig deidConfig) throws IOException; + /** + * Poll operation. + * + * @param operation to be polled. + * @param sleepMs length of time to wait between requests. + * @return HTTP Request (that returns status of operation). + * @throws IOException The IO Exception. + */ Operation pollOperation(Operation operation, Long sleepMs) throws InterruptedException, IOException; @@ -159,7 +218,7 @@ Operation pollOperation(Operation operation, Long sleepMs) * @param fhirStore the fhir store * @param bundle the bundle * @return the http body - * @throws IOException the io exception + * @throws IOException The IO Exception. */ HttpBody executeFhirBundle(String fhirStore, String bundle) throws IOException, HealthcareHttpException; @@ -170,7 +229,7 @@ HttpBody executeFhirBundle(String fhirStore, String bundle) * @param resourceName the resource name, in format * projects/{p}/locations/{l}/datasets/{d}/fhirStores/{f}/fhir/{resourceType}/{id} * @return the http body - * @throws IOException the io exception + * @throws IOException The IO Exception. */ HttpBody readFhirResource(String resourceName) throws IOException; @@ -179,9 +238,9 @@ HttpBody executeFhirBundle(String fhirStore, String bundle) * * @param fhirStore the fhir store * @param resourceType the resource type - * @param parameters the parameters + * @param parameters The parameters (in the form of key-value pairs). * @return the http body - * @throws IOException + * @throws IOException The IO Exception. */ HttpBody searchFhirResource( String fhirStore, @@ -195,9 +254,9 @@ HttpBody searchFhirResource( * * @param resourceName the resource name, in format * projects/{p}/locations/{l}/datasets/{d}/fhirStores/{f}/fhir/{resourceType}/{id} - * @param filters optional request filters + * @param filters optional request filters (in key value pairs). * @return the http body - * @throws IOException + * @throws IOException The IO Exception. */ HttpBody getPatientEverything( String resourceName, @Nullable Map filters, String pageToken) @@ -206,46 +265,101 @@ HttpBody getPatientEverything( /** * Create hl 7 v 2 store hl 7 v 2 store. * - * @param dataset the dataset - * @param name the name - * @return the hl 7 v 2 store - * @throws IOException the io exception + * @param dataset The dataset to create the HL7v2 store in. + * @param name The name of the store to be created. + * @return Empty. + * @throws IOException The IO Exception. */ Hl7V2Store createHL7v2Store(String dataset, String name) throws IOException; + /** + * Create FHIR Store with a PubSub topic listener. + * + * @param dataset The name of Dataset for the FHIR store to be created in. + * @param name The name of the FHIR store. + * @param version The version of the FHIR store (DSTU2, STU3, R4). + * @param pubsubTopic The pubsub topic listening to the FHIR store. + * @throws IOException The IO Exception. + */ FhirStore createFhirStore(String dataset, String name, String version, String pubsubTopic) throws IOException; - + /** + * Create FHIR Store. + * + * @param dataset The name of the Dataset for the FHIR store to be created in. + * @param name The name of the FHIR store. + * @param version The version of the FHIR store (DSTU2, STU3, R4). + * @throws IOException The IO Exception. + */ FhirStore createFhirStore(String dataset, String name, String version) throws IOException; /** * List all FHIR stores in a dataset. * - * @param dataset the dataset, in the format: + * @param dataset The dataset, in the format: * projects/project_id/locations/location_id/datasets/dataset_id - * @return a list of FhirStore - * @throws IOException + * @return A list of all FHIR stores in the dataset. + * @throws IOException The IO Exception. */ List listAllFhirStores(String dataset) throws IOException; /** - * Delete hl 7 v 2 store empty. + * Delete Fhir store. * - * @param store the store - * @return the empty - * @throws IOException the io exception + * @param store The FHIR store to be deleted. + * @return Empty. + * @throws IOException The IO Exception. */ - Empty deleteHL7v2Store(String store) throws IOException; - Empty deleteFhirStore(String store) throws IOException; + /** + * Retrieve DicomStudyMetadata. + * + * @param dicomWebPath The Dicom Web Path to retrieve the metadata from. + * @return The study metadata. + * @throws IOException The IO Exception. + */ String retrieveDicomStudyMetadata(String dicomWebPath) throws IOException; + /** + * Create a DicomStore. + * + * @param dataset The dataset that the Dicom Store should be in, in the format: + * projects/project_id/locations/location_id/datasets/dataset_id. + * @param name The name of the Dicom Store to be created. + * @return Empty. + * @throws IOException The IO Exception. + */ DicomStore createDicomStore(String dataset, String name) throws IOException; + /** + * Create a DicomStore with a PubSub listener. + * + * @param dataset The dataset that the Dicom Store should be in, in the format: + * projects/project_id/locations/location_id/datasets/dataset_id + * @param name The name of the Dicom Store to be created. + * @param pubsubTopic Name of PubSub topic connected with the Dicom store. + * @return Empty. + * @throws IOException The IO Exception. + */ DicomStore createDicomStore(String dataset, String name, String pubsubTopic) throws IOException; + /** + * Delete a Dicom Store. + * + * @param name The name of the Dicom Store to be deleted. + * @return Empty. + * @throws IOException The IO Exception. + */ Empty deleteDicomStore(String name) throws IOException; + /** + * Upload to a Dicom Store. + * + * @param webPath String format of webPath to upload into. + * @param filePath filePath of file to upload. + * @return Empty. + * @throws IOException The IO Exception. + */ Empty uploadToDicomStore(String webPath, String filePath) throws IOException, URISyntaxException; } From e6ecface49a7d757b57a85ab342813a292873ee0 Mon Sep 17 00:00:00 2001 From: Aleksandr Dudko <116064902+aleksandr-dudko@users.noreply.github.com> Date: Tue, 19 Sep 2023 22:26:33 +0400 Subject: [PATCH 071/105] Add GitHub Workflow Replacements for Jenkins job_PostCommit_Java_Nexmark (#28468) --- .github/workflows/README.md | 6 + .../beam_PostCommit_Java_Nexmark_Dataflow.yml | 115 +++++++++++++++++ ...am_PostCommit_Java_Nexmark_Dataflow_V2.yml | 106 ++++++++++++++++ ...stCommit_Java_Nexmark_Dataflow_V2_Java.yml | 116 ++++++++++++++++++ .../beam_PostCommit_Java_Nexmark_Direct.yml | 110 +++++++++++++++++ .../beam_PostCommit_Java_Nexmark_Flink.yml | 109 ++++++++++++++++ .../beam_PostCommit_Java_Nexmark_Spark.yml | 109 ++++++++++++++++ 7 files changed, 671 insertions(+) create mode 100644 .github/workflows/beam_PostCommit_Java_Nexmark_Dataflow.yml create mode 100644 .github/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2.yml create mode 100644 .github/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2_Java.yml create mode 100644 .github/workflows/beam_PostCommit_Java_Nexmark_Direct.yml create mode 100644 .github/workflows/beam_PostCommit_Java_Nexmark_Flink.yml create mode 100644 .github/workflows/beam_PostCommit_Java_Nexmark_Spark.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index d0897602f5e0..a1b8bdd2c273 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -206,6 +206,12 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | [ PostCommit Java Jpms Direct Java17 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml) | N/A |`Run Jpms Direct Java 17 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Direct_Java17](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Direct_Java17.yml) | | [ PostCommit Java Jpms Flink Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml) | N/A |`Run Jpms Flink Java 11 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Flink_Java11](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Flink_Java11.yml) | | [ PostCommit Java Jpms Spark Java11 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml) | N/A |`Run Jpms Spark Java 11 PostCommit`| [![.github/workflows/beam_PostCommit_Java_Jpms_Spark_Java11](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Jpms_Spark_Java11.yml) | +| [ PostCommit Java Nexmark Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Nexmark_Dataflow.yml) | N/A |`Run Dataflow Runner Nexmark Tests`| [![.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Nexmark_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Nexmark_Dataflow.yml) | +| [ PostCommit Java Nexmark Dataflow V2 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2.yml) | N/A |`Run Dataflow Runner V2 Nexmark Tests`| [![.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2.yml) | +| [ PostCommit Java Nexmark Dataflow V2 Java ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2_Java.yml) | ['11','17'] |`Run Dataflow Runner V2 Java (matrix) Nexmark Tests`| [![.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2_Java.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2_Java.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2_Java.yml) | +| [ PostCommit Java Nexmark Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Nexmark_Direct.yml) | N/A |`Run Direct Runner Nexmark Tests`| [![.github/workflows/beam_PostCommit_Java_Nexmark_Direct.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Nexmark_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Nexmark_Direct.yml) | +| [ PostCommit Java Nexmark Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Nexmark_Flink.yml) | N/A |`Run Flink Runner Nexmark Tests`| [![.github/workflows/beam_PostCommit_Java_Nexmark_Flink.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Nexmark_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Nexmark_Flink.yml) | +| [ PostCommit Java Nexmark Spark ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Nexmark_Spark.yml) | N/A |`Run Spark Runner Nexmark Tests`| [![.github/workflows/beam_PostCommit_Java_Nexmark_Spark.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Nexmark_Spark.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Nexmark_Spark.yml) | | [ PostCommit Java PVR Flink Streaming ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Flink_Streaming.yml) | N/A |`Run Java Flink PortableValidatesRunner Streaming`| [![PostCommit Java PVR Flink Streaming](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Flink_Streaming.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Flink_Streaming.yml) | | [ PostCommit Java PVR Samza ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Samza.yml) | N/A |`Run Java Samza PortableValidatesRunner`| [![PostCommit Java PVR Samza](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Samza.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Samza.yml) | | [ PostCommit Java PVR Spark3 Streaming ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Spark3_Streaming.yml) | N/A |`Run Java Spark v3 PortableValidatesRunner Streaming`| [![PostCommit Java PVR Spark3 Streaming](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Spark3_Streaming.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_PVR_Spark3_Streaming.yml) | diff --git a/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow.yml b/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow.yml new file mode 100644 index 000000000000..cf5162307078 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow.yml @@ -0,0 +1,115 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java Nexmark Dataflow + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + GRADLE_COMMAND_ARGUMENTS: | + -Pnexmark.args=--manageResources=false + --monitorJobs=true + --bigQueryTable=nexmark + --bigQueryDataset=nexmark + --project=apache-beam-testing + --resourceNameMode=QUERY_RUNNER_AND_MODE + --exportSummaryToBigQuery=true + --tempLocation=gs://temp-storage-for-perf-tests/nexmark + --influxDatabase=beam_test_metrics + --influxHost=http://10.128.0.96:8086 + --baseInfluxMeasurement=nexmark + --exportSummaryToInfluxDB=true + --influxRetentionPolicy=forever + --region=us-central1 + --suite=STRESS + --numWorkers=4 + --maxNumWorkers=4 + --autoscalingAlgorithm=NONE + --nexmarkParallel=16 + --enforceEncodability=true + --enforceImmutability=true + --runner=DataflowRunner + +jobs: + beam_PostCommit_Java_Nexmark_Dataflow: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 240 + strategy: + fail-fast: false + matrix: + job_name: [beam_PostCommit_Java_Nexmark_Dataflow] + job_phrase: [Run Dataflow Runner Nexmark Tests] + streaming: [false, true] + queryLanguage: [sql, zetasql, none] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Dataflow Runner Nexmark Tests' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: run PostCommit Java Nexmark Dataflow (${{ matrix.streaming }} ${{ matrix.queryLanguage }}) script + if: matrix.queryLanguage != 'none' + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:java:testing:nexmark:run + arguments: | + -Pnexmark.runner=:runners:google-cloud-dataflow-java \ + "${{ env.GRADLE_COMMAND_ARGUMENTS }}--streaming=${{ matrix.streaming }} --queryLanguage=${{ matrix.queryLanguage }}" \ + - name: run PostCommit Java Nexmark Dataflow (${{ matrix.streaming }}) script + if: matrix.queryLanguage == 'none' + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:java:testing:nexmark:run + arguments: | + -Pnexmark.runner=:runners:google-cloud-dataflow-java \ + "${{ env.GRADLE_COMMAND_ARGUMENTS }}--streaming=${{ matrix.streaming }}" \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2.yml b/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2.yml new file mode 100644 index 000000000000..0ee017ecce22 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2.yml @@ -0,0 +1,106 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java Nexmark Dataflow V2 + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + GRADLE_COMMAND_ARGUMENTS: | + -Pnexmark.args=--manageResources=false + --monitorJobs=true + --bigQueryTable=nexmark + --bigQueryDataset=nexmark + --project=apache-beam-testing + --resourceNameMode=QUERY_RUNNER_AND_MODE + --exportSummaryToBigQuery=false + --tempLocation=gs://temp-storage-for-perf-tests/nexmark + --influxDatabase=beam_test_metrics + --influxHost=http://10.128.0.96:8086 + --baseInfluxMeasurement=nexmark + --exportSummaryToInfluxDB=true + --influxRetentionPolicy=forever + --influxTags={"runnerVersion":"V2","javaVersion":"8"} + --region=us-central1 + --suite=STRESS + --numWorkers=4 + --maxNumWorkers=4 + --autoscalingAlgorithm=NONE + --nexmarkParallel=16 + --enforceEncodability=true + --enforceImmutability=true + --runner=DataflowRunner + +jobs: + beam_PostCommit_Java_Nexmark_Dataflow_V2: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 240 + strategy: + fail-fast: false + matrix: + job_name: [beam_PostCommit_Java_Nexmark_Dataflow_V2] + job_phrase: [Run Dataflow Runner V2 Nexmark Tests] + streaming: [false, true] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Dataflow Runner V2 Nexmark Tests' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: run PostCommit Java Nexmark Dataflow V2 (streaming = ${{ matrix.streaming }}) script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:java:testing:nexmark:run + arguments: | + -Pnexmark.runner=:runners:google-cloud-dataflow-java \ + '${{ env.GRADLE_COMMAND_ARGUMENTS }}--streaming=${{ matrix.streaming }}' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2_Java.yml b/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2_Java.yml new file mode 100644 index 000000000000..001e37775048 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2_Java.yml @@ -0,0 +1,116 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java Nexmark Dataflow V2 Java + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + GRADLE_COMMAND_ARGUMENTS: | + -Pnexmark.args=--manageResources=false + --monitorJobs=true + --bigQueryTable=nexmark + --bigQueryDataset=nexmark + --project=apache-beam-testing + --resourceNameMode=QUERY_RUNNER_AND_MODE + --exportSummaryToBigQuery=false + --tempLocation=gs://temp-storage-for-perf-tests/nexmark + --influxDatabase=beam_test_metrics + --influxHost=http://10.128.0.96:8086 + --baseInfluxMeasurement=nexmark + --exportSummaryToInfluxDB=true + --influxRetentionPolicy=forever + --region=us-central1 + --suite=STRESS + --numWorkers=4 + --maxNumWorkers=4 + --autoscalingAlgorithm=NONE + --nexmarkParallel=16 + --enforceEncodability=true + --enforceImmutability=true + --runner=DataflowRunner + +jobs: + beam_PostCommit_Java_Nexmark_Dataflow_V2_Java: + name: ${{matrix.job_name}} (${{matrix.job_phrase_1}} ${{matrix.java_version}} ${{matrix.job_phrase_2}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 240 + strategy: + fail-fast: false + matrix: + job_name: [beam_PostCommit_Java_Nexmark_Dataflow_V2_Java] + job_phrase_1: [Run Dataflow Runner V2 Java] + job_phrase_2: [Nexmark Tests] + streaming: [false, true] + java_version: ['11','17'] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + (contains(github.event.comment.body, 'Run Dataflow Runner V2 Java') && + contains(github.event.comment.body, 'Nexmark Tests')) + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase_1 }} ${{matrix.java_version}} ${{ matrix.job_phrase_2 }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} ${{ matrix.job_phrase_1 }} ${{matrix.java_version}} ${{ matrix.job_phrase_2 }} + - name: Set up Java${{ matrix.java_version }} + uses: actions/setup-java@v3.8.0 + with: + distribution: 'temurin' + java-version: ${{ matrix.java_version }} + - name: run PostCommit Java ${{matrix.java_version}} Nexmark Dataflow V2 (streaming = ${{ matrix.streaming }}) script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:java:testing:nexmark:run + arguments: | + -PcompileAndRunTestsWithJava${{ matrix.java_version }} \ + -Pjava${{ matrix.java_version }}Home=$JAVA_HOME_${{ matrix.java_version }}_X64 \ + -Pnexmark.runner.version=V2 \ + -Pnexmark.runner=:runners:google-cloud-dataflow-java \ + '${{ env.GRADLE_COMMAND_ARGUMENTS }}--influxTags={"runnerVersion":"V2","javaVersion":"${{matrix.java_version}}"}--streaming=${{ matrix.streaming }}' \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Nexmark_Direct.yml b/.github/workflows/beam_PostCommit_Java_Nexmark_Direct.yml new file mode 100644 index 000000000000..00727ba64d48 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_Nexmark_Direct.yml @@ -0,0 +1,110 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java Nexmark Direct + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + GRADLE_COMMAND_ARGUMENTS: | + -Pnexmark.args=--manageResources=false + --monitorJobs=true + --bigQueryTable=nexmark + --bigQueryDataset=nexmark + --project=apache-beam-testing + --resourceNameMode=QUERY_RUNNER_AND_MODE + --exportSummaryToBigQuery=true + --tempLocation=gs://temp-storage-for-perf-tests/nexmark + --influxDatabase=beam_test_metrics + --influxHost=http://10.128.0.96:8086 + --baseInfluxMeasurement=nexmark + --exportSummaryToInfluxDB=true + --influxRetentionPolicy=forever + --suite=SMOKE + --enforceEncodability=true + --enforceImmutability=true + --runner=DirectRunner + +jobs: + beam_PostCommit_Java_Nexmark_Direct: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 240 + strategy: + fail-fast: false + matrix: + job_name: [beam_PostCommit_Java_Nexmark_Direct] + job_phrase: [Run Direct Runner Nexmark Tests] + streaming: [false, true] + queryLanguage: [sql, zetasql, none] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Direct Runner Nexmark Tests' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: run PostCommit Java Nexmark Direct (${{ matrix.streaming }} ${{ matrix.queryLanguage }}) script + if: matrix.queryLanguage != 'none' + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:java:testing:nexmark:run + arguments: | + -Pnexmark.runner=:runners:direct-java \ + "${{ env.GRADLE_COMMAND_ARGUMENTS }} --streaming=${{ matrix.streaming }} --queryLanguage=${{ matrix.queryLanguage }}" \ + - name: run PostCommit Java Nexmark Direct (${{ matrix.streaming }}) script + if: matrix.queryLanguage == 'none' + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:java:testing:nexmark:run + arguments: | + -Pnexmark.runner=:runners:direct-java \ + "${{ env.GRADLE_COMMAND_ARGUMENTS }}--streaming=${{ matrix.streaming }}" \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Nexmark_Flink.yml b/.github/workflows/beam_PostCommit_Java_Nexmark_Flink.yml new file mode 100644 index 000000000000..fce3ee045065 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_Nexmark_Flink.yml @@ -0,0 +1,109 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java Nexmark Flink + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + GRADLE_COMMAND_ARGUMENTS: | + -Pnexmark.args=--manageResources=false + --monitorJobs=true + --bigQueryTable=nexmark + --bigQueryDataset=nexmark + --project=apache-beam-testing + --resourceNameMode=QUERY_RUNNER_AND_MODE + --exportSummaryToBigQuery=true + --tempLocation=gs://temp-storage-for-perf-tests/nexmark + --influxDatabase=beam_test_metrics + --influxHost=http://10.128.0.96:8086 + --baseInfluxMeasurement=nexmark + --exportSummaryToInfluxDB=true + --influxRetentionPolicy=forever + --suite=SMOKE + --streamTimeout=60 + --runner=FlinkRunner + +jobs: + beam_PostCommit_Java_Nexmark_Flink: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 240 + strategy: + fail-fast: false + matrix: + job_name: [beam_PostCommit_Java_Nexmark_Flink] + job_phrase: [Run Flink Runner Nexmark Tests] + streaming: [false, true] + queryLanguage: [sql, zetasql, none] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Flink Runner Nexmark Tests' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: run PostCommit Java Nexmark Flink (${{ matrix.streaming }} ${{ matrix.queryLanguage }}) script + if: matrix.queryLanguage != 'none' + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:java:testing:nexmark:run + arguments: | + -Pnexmark.runner=:runners:flink:1.15 \ + "${{ env.GRADLE_COMMAND_ARGUMENTS }} --streaming=${{ matrix.streaming }} --queryLanguage=${{ matrix.queryLanguage }}" \ + - name: run PostCommit Java Nexmark Flink (${{ matrix.streaming }}) script + if: matrix.queryLanguage == 'none' + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:java:testing:nexmark:run + arguments: | + -Pnexmark.runner=:runners:flink:1.15 \ + "${{ env.GRADLE_COMMAND_ARGUMENTS }}--streaming=${{ matrix.streaming }}" \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Java_Nexmark_Spark.yml b/.github/workflows/beam_PostCommit_Java_Nexmark_Spark.yml new file mode 100644 index 000000000000..a3735a00738f --- /dev/null +++ b/.github/workflows/beam_PostCommit_Java_Nexmark_Spark.yml @@ -0,0 +1,109 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Java Nexmark Spark + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: write + checks: write + contents: read + deployments: read + id-token: none + issues: write + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + GRADLE_COMMAND_ARGUMENTS: | + -Pnexmark.args=--manageResources=false + --monitorJobs=true + --bigQueryTable=nexmark + --bigQueryDataset=nexmark + --project=apache-beam-testing + --resourceNameMode=QUERY_RUNNER_AND_MODE + --exportSummaryToBigQuery=true + --tempLocation=gs://temp-storage-for-perf-tests/nexmark + --influxDatabase=beam_test_metrics + --influxHost=http://10.128.0.96:8086 + --baseInfluxMeasurement=nexmark + --exportSummaryToInfluxDB=true + --influxRetentionPolicy=forever + --suite=SMOKE + --streamTimeout=60 + --streaming=false + +jobs: + beam_PostCommit_Java_Nexmark_Spark: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 240 + strategy: + fail-fast: false + matrix: + job_name: [beam_PostCommit_Java_Nexmark_Spark] + job_phrase: [Run Spark Runner Nexmark Tests] + runner: [SparkRunner, SparkStructuredStreamingRunner --skipQueries=3] + queryLanguage: [sql, zetasql, none] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Spark Runner Nexmark Tests' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: run PostCommit Java Nexmark Spark (runner = ${{ matrix.runner }} queryLanguage = ${{ matrix.queryLanguage }}) script + if: matrix.queryLanguage != 'none' + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:java:testing:nexmark:run + arguments: | + -Pnexmark.runner=:runners:spark:3 \ + "${{ env.GRADLE_COMMAND_ARGUMENTS }} --runner=${{ matrix.runner }} --queryLanguage=${{ matrix.queryLanguage }}" \ + - name: run PostCommit Java Nexmark Spark (${{ matrix.runner }}) script + if: matrix.queryLanguage == 'none' + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:java:testing:nexmark:run + arguments: | + -Pnexmark.runner=:runners:spark:3 \ + "${{ env.GRADLE_COMMAND_ARGUMENTS }}--runner=${{ matrix.runner }}" \ No newline at end of file From 6f91ef83cade027523ea0a4d99c1edc2c41e7b3d Mon Sep 17 00:00:00 2001 From: Andrey Devyatkin Date: Tue, 19 Sep 2023 20:27:30 +0200 Subject: [PATCH 072/105] Migrate "beam_PostCommit_Python_Nexmark_Direct" Jenkins jobs to GitHub Actions (#28483) * added beam_PostCommit_Python_Nexmark_Direct job to GitHub Actions * added comment + fix for arguments * fixes and comment update --- .github/workflows/README.md | 1 + .../beam_PostCommit_Python_Nexmark_Direct.yml | 138 ++++++++++++++++++ ...on_ValidatesContainer_Dataflow_With_RC.yml | 2 +- 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/beam_PostCommit_Python_Nexmark_Direct.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index a1b8bdd2c273..1f0bca083e77 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -240,6 +240,7 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | [ PostCommit Python Examples Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml) | ['3.8','3.11'] |`Run Python Examples_Flink (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_Examples_Flink.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Flink.yml) | | [ PostCommit Python Examples Spark ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Spark.yml) | ['3.8','3.11'] |`Run Python Examples_Spark (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_Examples_Spark.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Spark.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Examples_Spark.yml) | | [ PostCommit Python MongoDBIO IT ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml) | N/A |`Run Python MongoDBIO_IT`| [![.github/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml) | +| [ PostCommit Python Nexmark Direct ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Nexmark_Direct.yml) | N/A |`Run Python Direct Runner Nexmark Tests`| [![.github/workflows/beam_PostCommit_Python_Nexmark_Direct.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Nexmark_Direct.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_Nexmark_Direct.yml) | | [ PostCommit Python ValidatesContainer Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml) | ['3.8','3.9','3.10','3.11'] |`Run Python Dataflow ValidatesContainer (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml) | | [ PostCommit Python ValidatesContainer Dataflow With RC ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml) | ['3.8','3.9','3.10','3.11'] |`Run Python RC Dataflow ValidatesContainer (matrix_element)`| [![.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml) | | [ PostCommit Python ValidatesRunner Dataflow ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml) | ['3.8','3.11'] |`Run Python Dataflow ValidatesRunner (matrix_element)`| [![PostCommit Python ValidatesRunner Dataflow](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml) | diff --git a/.github/workflows/beam_PostCommit_Python_Nexmark_Direct.yml b/.github/workflows/beam_PostCommit_Python_Nexmark_Direct.yml new file mode 100644 index 000000000000..a493d6a1656b --- /dev/null +++ b/.github/workflows/beam_PostCommit_Python_Nexmark_Direct.yml @@ -0,0 +1,138 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Python Nexmark Direct + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + GRADLE_JAVA_COMMAND_ARGUMENTS: | + --manageResources=false + --monitorJobs=true + --bigQueryTable=nexmark + --bigQueryDataset=nexmark + --project=apache-beam-testing + --resourceNameMode=QUERY_RUNNER_AND_MODE + --exportSummaryToBigQuery=true + --tempLocation=gs://temp-storage-for-perf-tests/nexmark + --influxDatabase=beam_test_metrics + --influxHost=http://10.128.0.96:8086 + --baseInfluxMeasurement=nexmark + --exportSummaryToInfluxDB=true + --influxRetentionPolicy=forever + --suite=SMOKE + --enforceEncodability=true + --enforceImmutability=true + --runner=DirectRunner + --numEvents=100000 + GRADLE_PYTHON_COMMAND_ARGUMENTS: | + --monitor_jobs + --big_query_table=nexmark + --big_query_dataset=nexmark + --project=apache-beam-testing + --resource_name_mode=QUERY_RUNNER_AND_MODE + --export_summary_to_big_query + --temp_location=gs://temp-storage-for-perf-tests/nexmark + --influx_database=beam_test_metrics + --influx_host=http://10.128.0.96:8086 + --base_influx_measurement=nexmark + --export_summary_to_influx_db + --influx_retention_policy=forever + --suite=SMOKE + --enforce_encodability + --enforce_immutability + --runner=DirectRunner + --num_events=100000 + +jobs: + beam_PostCommit_Python_Nexmark_Direct: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 240 + strategy: + fail-fast: false + matrix: + job_name: [beam_PostCommit_Python_Nexmark_Direct] + job_phrase: [Run Python Direct Runner Nexmark Tests] + # Query numbers are listed explicitly due to issues with Python Nexmark Query: + # query = 1 - https://github.com/apache/beam/issues/24678 + # query = 4,6,9 - https://github.com/apache/beam/issues/24679 + # query = 12 - https://github.com/apache/beam/issues/24680 + query: [0, 2, 3, 5, 7, 8, 10, 11] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Python Direct Runner Nexmark Tests' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: run Java Testing Nexmark (query ${{ matrix.query }}) + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:java:testing:nexmark:run + arguments: | + -Pnexmark.runner=:runners:direct-java \ + "-Pnexmark.args=${{ env.GRADLE_JAVA_COMMAND_ARGUMENTS }} \ + --query=${{ matrix.query }} \ + --generateEventFilePathPrefix=gs://temp-storage-for-perf-tests/nexmark/eventFiles/beam_PostCommit_Python_Nexmark_Direct/query${{ matrix.query }}-" \ + - name: run Python Testing Benchmarks Nexmark (query ${{ matrix.query }}) + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:python:apache_beam:testing:benchmarks:nexmark:run + arguments: | + -PpythonVersion=3.8 \ + "-Pnexmark.args=${{ env.GRADLE_PYTHON_COMMAND_ARGUMENTS }} \ + --query=${{ matrix.query }} \ + --input=gs://temp-storage-for-perf-tests/nexmark/eventFiles/beam_PostCommit_Python_Nexmark_Direct/query${{ matrix.query }}-\*" \ No newline at end of file diff --git a/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml b/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml index b048cc1563d4..c4354b072c52 100644 --- a/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml +++ b/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml @@ -97,7 +97,7 @@ jobs: with: gradle-command: :sdks:python:test-suites:dataflow:py${{steps.set_py_ver_clean.outputs.py_ver_clean}}:validatesContainer arguments: | - -PtestRCDependencies=true + -PtestRCDependencies=true \ -PpythonVersion=${{ matrix.python_version }} \ - name: Archive code coverage results uses: actions/upload-artifact@v3 From 6c4bdf105567f5553fc738633e1d97af8bb63447 Mon Sep 17 00:00:00 2001 From: Andrey Devyatkin Date: Tue, 19 Sep 2023 20:28:05 +0200 Subject: [PATCH 073/105] added beam_PostCommit_Javadoc job to GitHub Actions (#28522) --- .github/workflows/README.md | 3 +- .github/workflows/beam_PostCommit_Javadoc.yml | 82 +++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/beam_PostCommit_Javadoc.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 1f0bca083e77..0d0277bd478d 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -190,7 +190,6 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | [ PostCommit Go VR Flink](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Flink.yml) | N/A |`Run Go Flink ValidatesRunner`| [![.github/workflows/beam_PostCommit_Go_VR_Flink.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Flink.yml) | | [ PostCommit Go VR Samza](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Samza.yml) | N/A |`Run Go Samza ValidatesRunner`| [![.github/workflows/beam_PostCommit_Go_VR_Samza.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Samza.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Samza.yml) | | [ PostCommit Go VR Spark](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Spark.yml) | N/A |`Run Go Spark ValidatesRunner`| [![.github/workflows/beam_PostCommit_Go_VR_Spark.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Spark.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Go_VR_Spark.yml) | -| [ PostCommit Java ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java.yml) | N/A |`Run Java PostCommit`| [![.github/workflows/beam_PostCommit_Java.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java.yml) | | [ PostCommit Java Avro Versions ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Avro_Versions.yml) | N/A |`Run Java Avro Versions PostCommit`| [![.github/workflows/beam_PostCommit_Java_Avro_Versions.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Avro_Versions.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_Avro_Versions.yml) | | [ PostCommit Java Dataflow V1 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV1.yml) | N/A |`Run PostCommit_Java_Dataflow`| [![.github/workflows/beam_PostCommit_Java_DataflowV1.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV1.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV1.yml) | | [ PostCommit Java Dataflow V2 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV2.yml) | N/A |`Run PostCommit_Java_DataflowV2`| [![.github/workflows/beam_PostCommit_Java_DataflowV2.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV2.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_DataflowV2.yml) | @@ -232,6 +231,8 @@ Please note that jobs with matrix need to have matrix element in the comment. Ex | [ PostCommit Java ValidatesRunner SparkStructuredStreaming ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml) | N/A |`Run Spark StructuredStreaming ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_SparkStructuredStreaming.yml) | | [ PostCommit Java ValidatesRunner Twister2 ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml) | N/A |`Run Twister2 ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_Twister2.yml) | | [ PostCommit Java ValidatesRunner ULR ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml) | N/A |`Run ULR Loopback ValidatesRunner`| [![.github/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java_ValidatesRunner_ULR.yml) | +| [ PostCommit Java ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java.yml) | N/A |`Run Java PostCommit`| [![.github/workflows/beam_PostCommit_Java.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Java.yml) | +| [ PostCommit Javadoc ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Javadoc.yml) | N/A |`Run Javadoc PostCommit`| [![.github/workflows/beam_PostCommit_Javadoc.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Javadoc.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Javadoc.yml) | | [ PostCommit PortableJar Flink ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_PortableJar_Flink.yml) | N/A |`Run PortableJar_Flink PostCommit`| [![.github/workflows/beam_PostCommit_PortableJar_Flink.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_PortableJar_Flink.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_PortableJar_Flink.yml) | | [ PostCommit PortableJar Spark ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_PortableJar_Spark.yml) | N/A |`Run PortableJar_Spark PostCommit`| [![.github/workflows/beam_PostCommit_PortableJar_Spark.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_PortableJar_Spark.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_PortableJar_Spark.yml) | | [ PostCommit Python ](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python.yml) | ['3.8','3.9','3.10','3.11'] |`Run Python PostCommit (matrix_element)`| [![.github/workflows/beam_PostCommit_Python.yml](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python.yml/badge.svg?event=schedule)](https://github.com/apache/beam/actions/workflows/beam_PostCommit_Python.yml) | diff --git a/.github/workflows/beam_PostCommit_Javadoc.yml b/.github/workflows/beam_PostCommit_Javadoc.yml new file mode 100644 index 000000000000..f60f9dafd535 --- /dev/null +++ b/.github/workflows/beam_PostCommit_Javadoc.yml @@ -0,0 +1,82 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: PostCommit Javadoc + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 */6 * * *' + workflow_dispatch: + +# This allows a subsequently queued workflow run to interrupt previous runs +concurrency: + group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' + cancel-in-progress: true + +#Setting explicit permissions for the action to avoid the default permissions which are `write-all` in case of pull_request_target event +permissions: + actions: write + pull-requests: read + checks: read + contents: read + deployments: read + id-token: none + issues: read + discussions: read + packages: read + pages: read + repository-projects: read + security-events: read + statuses: read + +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + +jobs: + beam_PostCommit_Javadoc: + name: ${{matrix.job_name}} (${{matrix.job_phrase}}) + runs-on: [self-hosted, ubuntu-20.04, main] + timeout-minutes: 240 + strategy: + matrix: + job_name: [beam_PostCommit_Javadoc] + job_phrase: [Run Javadoc PostCommit] + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'schedule' || + github.event.comment.body == 'Run Javadoc PostCommit' + steps: + - uses: actions/checkout@v3 + - name: Setup repository + uses: ./.github/actions/setup-action + with: + comment_phrase: ${{ matrix.job_phrase }} + github_token: ${{ secrets.GITHUB_TOKEN }} + github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) + - name: run aggregateJavadoc script + uses: ./.github/actions/gradle-command-self-hosted-action + with: + gradle-command: :sdks:java:javadoc:aggregateJavadoc + - name: Upload Javadoc Results + uses: actions/upload-artifact@v3 + with: + name: Javadoc Results + path: '**/sdks/java/javadoc/build/docs/javadoc/**' \ No newline at end of file From a90879af543eede5bc7e99e383cfd666dab3ea59 Mon Sep 17 00:00:00 2001 From: Yi Hu Date: Tue, 19 Sep 2023 14:35:26 -0400 Subject: [PATCH 074/105] Revert "Remove TableSchema to JSON conversion. (#28274)" (#28533) This reverts commit 7e830593e61ba1fbff16411b5825bfb4aea53ba2. --- .../beam/sdk/io/gcp/bigquery/BigQueryIO.java | 40 ++++++++++++++----- .../io/gcp/bigquery/BigQueryIOReadTest.java | 18 ++++++--- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIO.java b/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIO.java index 3c006d24d037..58d769312444 100644 --- a/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIO.java +++ b/sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIO.java @@ -19,7 +19,6 @@ import static org.apache.beam.sdk.io.gcp.bigquery.BigQueryHelpers.resolveTempLocation; import static org.apache.beam.sdk.io.gcp.bigquery.BigQueryResourceNaming.createTempTableReference; -import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull; import static org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkArgument; import static org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkState; @@ -50,6 +49,7 @@ import com.google.protobuf.DynamicMessage; import com.google.protobuf.Message; import java.io.IOException; +import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.List; @@ -132,10 +132,13 @@ import org.apache.beam.sdk.values.TypeDescriptors; import org.apache.beam.sdk.values.ValueInSingleWindow; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.annotations.VisibleForTesting; +import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Function; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.MoreObjects; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Predicates; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Strings; +import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Supplier; +import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Suppliers; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.ImmutableList; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.Iterables; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.Lists; @@ -646,19 +649,29 @@ public static TypedRead readTableRowsWithSchema() { BigQueryUtils.tableRowFromBeamRow()); } + private static class TableSchemaFunction + implements Serializable, Function<@Nullable String, @Nullable TableSchema> { + @Override + public @Nullable TableSchema apply(@Nullable String input) { + return BigQueryHelpers.fromJsonString(input, TableSchema.class); + } + } + @VisibleForTesting static class GenericDatumTransformer implements DatumReader { private final SerializableFunction parseFn; - private final TableSchema tableSchema; + private final Supplier tableSchema; private GenericDatumReader reader; private org.apache.avro.Schema writerSchema; public GenericDatumTransformer( SerializableFunction parseFn, - TableSchema tableSchema, + String tableSchema, org.apache.avro.Schema writer) { this.parseFn = parseFn; - this.tableSchema = tableSchema; + this.tableSchema = + Suppliers.memoize( + Suppliers.compose(new TableSchemaFunction(), Suppliers.ofInstance(tableSchema))); this.writerSchema = writer; this.reader = new GenericDatumReader<>(this.writerSchema); } @@ -676,7 +689,7 @@ public void setSchema(org.apache.avro.Schema schema) { @Override public T read(T reuse, Decoder in) throws IOException { GenericRecord record = (GenericRecord) this.reader.read(reuse, in); - return parseFn.apply(new SchemaAndRecord(record, this.tableSchema)); + return parseFn.apply(new SchemaAndRecord(record, this.tableSchema.get())); } } @@ -708,9 +721,16 @@ public static TypedRead read(SerializableFunction par .setDatumReaderFactory( (SerializableFunction>) input -> { - TableSchema safeInput = checkStateNotNull(input); - return (AvroSource.DatumReaderFactory) - (writer, reader) -> new GenericDatumTransformer<>(parseFn, safeInput, writer); + try { + String jsonTableSchema = BigQueryIO.JSON_FACTORY.toString(input); + return (AvroSource.DatumReaderFactory) + (writer, reader) -> + new GenericDatumTransformer<>(parseFn, jsonTableSchema, writer); + } catch (IOException e) { + LOG.warn( + String.format("Error while converting table schema %s to JSON!", input), e); + return null; + } }) // TODO: Remove setParseFn once https://github.com/apache/beam/issues/21076 is fixed. .setParseFn(parseFn) @@ -3366,7 +3386,9 @@ private WriteResult expandTyped( @SuppressWarnings({"unchecked", "nullness"}) Descriptors.Descriptor descriptor = (Descriptors.Descriptor) - checkStateNotNull(writeProtoClass.getMethod("getDescriptor")).invoke(null); + org.apache.beam.sdk.util.Preconditions.checkStateNotNull( + writeProtoClass.getMethod("getDescriptor")) + .invoke(null); TableSchema tableSchema = TableRowToStorageApiProto.protoSchemaToTableSchema( TableRowToStorageApiProto.tableSchemaFromDescriptor(descriptor)); diff --git a/sdks/java/io/google-cloud-platform/src/test/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIOReadTest.java b/sdks/java/io/google-cloud-platform/src/test/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIOReadTest.java index e274a8ac68ef..bc75ba8bd9ba 100644 --- a/sdks/java/io/google-cloud-platform/src/test/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIOReadTest.java +++ b/sdks/java/io/google-cloud-platform/src/test/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIOReadTest.java @@ -19,7 +19,6 @@ import static org.apache.beam.sdk.io.gcp.bigquery.BigQueryResourceNaming.createTempTableReference; import static org.apache.beam.sdk.transforms.display.DisplayDataMatchers.hasDisplayItem; -import static org.apache.beam.sdk.util.Preconditions.checkStateNotNull; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.junit.Assert.assertEquals; @@ -144,11 +143,18 @@ public void evaluate() throws Throwable { private SerializableFunction> datumReaderFactoryFn = - input -> - (AvroSource.DatumReaderFactory) - (writer, reader) -> - new BigQueryIO.GenericDatumTransformer<>( - BigQueryIO.TableRowParser.INSTANCE, checkStateNotNull(input), writer); + (SerializableFunction>) + input -> { + try { + String jsonSchema = BigQueryIO.JSON_FACTORY.toString(input); + return (AvroSource.DatumReaderFactory) + (writer, reader) -> + new BigQueryIO.GenericDatumTransformer<>( + BigQueryIO.TableRowParser.INSTANCE, jsonSchema, writer); + } catch (IOException e) { + return null; + } + }; private static class MyData implements Serializable { private String name; From f2a0c76703bb78cb46b5e67f86d4b353cf0d6a3d Mon Sep 17 00:00:00 2001 From: caneff Date: Tue, 19 Sep 2023 14:52:56 -0400 Subject: [PATCH 075/105] Remove inplace argument from set_axis for Pandas 2 (#28536) --- sdks/python/apache_beam/dataframe/frames.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sdks/python/apache_beam/dataframe/frames.py b/sdks/python/apache_beam/dataframe/frames.py index 34fc17553b07..b864e952e19e 100644 --- a/sdks/python/apache_beam/dataframe/frames.py +++ b/sdks/python/apache_beam/dataframe/frames.py @@ -1901,7 +1901,8 @@ def dropna(self, **kwargs): @frame_base.with_docs_from(pd.Series) @frame_base.args_to_kwargs(pd.Series) - @frame_base.populate_defaults(pd.Series) + @frame_base.populate_defaults( + pd.Series, removed_args=['inplace'] if PD_VERSION >= (2, 0) else None) @frame_base.maybe_inplace def set_axis(self, labels, **kwargs): # TODO: assigning the index is generally order-sensitive, but we could @@ -2675,7 +2676,9 @@ def set_index(self, keys, **kwargs): @frame_base.with_docs_from(pd.DataFrame) @frame_base.args_to_kwargs(pd.DataFrame) - @frame_base.populate_defaults(pd.DataFrame) + @frame_base.populate_defaults( + pd.DataFrame, + removed_args=['inplace'] if PD_VERSION >= (2, 0) else None) @frame_base.maybe_inplace def set_axis(self, labels, axis, **kwargs): if axis in ('index', 0): From 0f2f3b12ac6f13dd068e95c22146f7aa636c6df4 Mon Sep 17 00:00:00 2001 From: caneff Date: Tue, 19 Sep 2023 15:03:10 -0400 Subject: [PATCH 076/105] Only value_counts.drop_na for Pandas 2 (#28500) --- sdks/python/apache_beam/dataframe/frames.py | 17 ++++++++++++++--- .../python/apache_beam/dataframe/frames_test.py | 2 ++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/sdks/python/apache_beam/dataframe/frames.py b/sdks/python/apache_beam/dataframe/frames.py index b864e952e19e..7929c879bdd2 100644 --- a/sdks/python/apache_beam/dataframe/frames.py +++ b/sdks/python/apache_beam/dataframe/frames.py @@ -2348,8 +2348,13 @@ def value_counts( result = column.groupby(column, dropna=dropna).size() - # groupby.size() names the index, which we don't need - result.index.name = None + # Pandas 2 introduces new naming for the results. + if PD_VERSION >= (2, 0): + result.index.name = getattr(self, "name", None) + result.name = "proportion" if normalize else "count" + else: + # groupby.size() names the index, which we don't need + result.index.name = None if normalize: return result / column.length() @@ -4007,12 +4012,18 @@ def value_counts(self, subset=None, sort=False, normalize=False, columns = subset or list(self.columns) if dropna: - dropped = self.dropna() + # Must include subset here because otherwise we spuriously drop NAs due + # to columns outside our subset. + dropped = self.dropna(subset=subset) else: dropped = self result = dropped.groupby(columns, dropna=dropna).size() + # Pandas 2 introduces new naming for the results. + if PD_VERSION >= (2,0): + result.name = "proportion" if normalize else "count" + if normalize: return result/dropped.length() else: diff --git a/sdks/python/apache_beam/dataframe/frames_test.py b/sdks/python/apache_beam/dataframe/frames_test.py index 5d904855f87e..257d77e0a6b3 100644 --- a/sdks/python/apache_beam/dataframe/frames_test.py +++ b/sdks/python/apache_beam/dataframe/frames_test.py @@ -694,6 +694,8 @@ def test_value_counts_with_nans(self): self._run_test(lambda df: df.value_counts(), df) self._run_test(lambda df: df.value_counts(normalize=True), df) + # Ensure we don't drop rows due to nan values in unused columns. + self._run_test(lambda df: df.value_counts('num_wings'), df) if PD_VERSION >= (1, 3): # dropna=False is new in pandas 1.3 From 0f727927c0136c63e8127ab5e9d3279fd5748dc4 Mon Sep 17 00:00:00 2001 From: Adam Whitmore <61937566+ajdub508@users.noreply.github.com> Date: Tue, 19 Sep 2023 15:05:46 -0400 Subject: [PATCH 077/105] Return errors for insert_rows_json exceptions (#21080) (#28091) --- CHANGES.md | 1 + .../apache_beam/io/gcp/bigquery_test.py | 853 +++++++++++++----- .../apache_beam/io/gcp/bigquery_tools.py | 22 +- .../io/gcp/bigquery_write_it_test.py | 51 +- 4 files changed, 681 insertions(+), 246 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 47c35fe3491b..cdf93909cb6e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -82,6 +82,7 @@ ## Bugfixes * Fixed exception chaining issue in GCS connector (Python) ([#26769](https://github.com/apache/beam/issues/26769#issuecomment-1700422615)). +* Fixed streaming inserts exception handling, GoogleAPICallErrors are now retried according to retry strategy and routed to failed rows where appropriate rather than causing a pipeline error (Python) ([#21080](https://github.com/apache/beam/issues/21080)). ## Security Fixes diff --git a/sdks/python/apache_beam/io/gcp/bigquery_test.py b/sdks/python/apache_beam/io/gcp/bigquery_test.py index 3a3033dfcaf4..7e9c1e634748 100644 --- a/sdks/python/apache_beam/io/gcp/bigquery_test.py +++ b/sdks/python/apache_beam/io/gcp/bigquery_test.py @@ -77,7 +77,6 @@ from apache_beam.testing.util import equal_to from apache_beam.transforms.display import DisplayData from apache_beam.transforms.display_test import DisplayDataItemMatcher -from apache_beam.utils import retry # Protect against environments where bigquery library is not available. # pylint: disable=wrong-import-order, wrong-import-position @@ -931,77 +930,269 @@ def test_copy_load_job_exception(self, exception_type, error_message): 'GCP dependencies are not installed') class BigQueryStreamingInsertsErrorHandling(unittest.TestCase): - # Using https://cloud.google.com/bigquery/docs/error-messages and - # https://googleapis.dev/python/google-api-core/latest/_modules/google - # /api_core/exceptions.html - # to determine error types and messages to try for retriables. + # Running tests with a variety of exceptions from https://googleapis.dev + # /python/google-api-core/latest/_modules/google/api_core/exceptions.html. + # Choosing some exceptions that produce reasons included in + # bigquery_tools._NON_TRANSIENT_ERRORS and some that are not @parameterized.expand([ + # reason not in _NON_TRANSIENT_ERRORS for row 1 on first attempt + # transient error retried and succeeds on second attempt, 0 rows sent to + # failed rows param( - exception_type=exceptions.Forbidden if exceptions else None, - error_reason='rateLimitExceeded'), + insert_response=[ + exceptions.TooManyRequests if exceptions else None, + None], + error_reason='Too Many Requests', # not in _NON_TRANSIENT_ERRORS + failed_rows=[]), + # reason not in _NON_TRANSIENT_ERRORS for row 1 on both attempts, sent to + # failed rows after hitting max_retries + param( + insert_response=[ + exceptions.InternalServerError if exceptions else None, + exceptions.InternalServerError if exceptions else None], + error_reason='Internal Server Error', # not in _NON_TRANSIENT_ERRORS + failed_rows=['value1', 'value3', 'value5']), + # reason in _NON_TRANSIENT_ERRORS for row 1 on both attempts, sent to + # failed_rows after hitting max_retries + param( + insert_response=[ + exceptions.Forbidden if exceptions else None, + exceptions.Forbidden if exceptions else None], + error_reason='Forbidden', # in _NON_TRANSIENT_ERRORS + failed_rows=['value1', 'value3', 'value5']), + ]) + def test_insert_rows_json_exception_retry_always( + self, insert_response, error_reason, failed_rows): + # In this test, a pipeline will always retry all caught exception types + # since RetryStrategy is not set and defaults to RETRY_ALWAYS + with mock.patch('time.sleep'): + call_counter = 0 + mock_response = mock.Mock() + mock_response.reason = error_reason + + def store_callback(table, **kwargs): + nonlocal call_counter + # raise exception if insert_response element is an exception + if insert_response[call_counter]: + exception_type = insert_response[call_counter] + call_counter += 1 + raise exception_type('some exception', response=mock_response) + # return empty list if not insert_response element, indicating + # successful call to insert_rows_json + else: + call_counter += 1 + return [] + + client = mock.Mock() + client.insert_rows_json.side_effect = store_callback + + # Using the bundle based direct runner to avoid pickling problems + # with mocks. + with beam.Pipeline(runner='BundleBasedDirectRunner') as p: + bq_write_out = ( + p + | beam.Create([{ + 'columnA': 'value1', 'columnB': 'value2' + }, { + 'columnA': 'value3', 'columnB': 'value4' + }, { + 'columnA': 'value5', 'columnB': 'value6' + }]) + # Using _StreamToBigQuery in order to be able to pass max_retries + # in order to limit run time of test with RETRY_ALWAYS + | _StreamToBigQuery( + table_reference='project:dataset.table', + table_side_inputs=[], + schema_side_inputs=[], + schema='anyschema', + batch_size=None, + triggering_frequency=None, + create_disposition='CREATE_NEVER', + write_disposition=None, + kms_key=None, + retry_strategy=RetryStrategy.RETRY_ALWAYS, + additional_bq_parameters=[], + ignore_insert_ids=False, + ignore_unknown_columns=False, + with_auto_sharding=False, + test_client=client, + max_retries=len(insert_response) - 1, + num_streaming_keys=500)) + + failed_values = ( + bq_write_out[beam_bq.BigQueryWriteFn.FAILED_ROWS] + | beam.Map(lambda x: x[1]['columnA'])) + + assert_that(failed_values, equal_to(failed_rows)) + + # Running tests with a variety of exceptions from https://googleapis.dev + # /python/google-api-core/latest/_modules/google/api_core/exceptions.html. + # Choosing some exceptions that produce reasons that are included in + # bigquery_tools._NON_TRANSIENT_ERRORS and some that are not + @parameterized.expand([ + param( + # not in _NON_TRANSIENT_ERRORS + exception_type=exceptions.BadGateway if exceptions else None, + error_reason='Bad Gateway'), + param( + # in _NON_TRANSIENT_ERRORS + exception_type=exceptions.Unauthorized if exceptions else None, + error_reason='Unauthorized'), + ]) + @mock.patch('time.sleep') + @mock.patch('google.cloud.bigquery.Client.insert_rows_json') + def test_insert_rows_json_exception_retry_never( + self, mock_send, unused_mock_sleep, exception_type, error_reason): + # In this test, a pipeline will never retry caught exception types + # since RetryStrategy is set to RETRY_NEVER + mock_response = mock.Mock() + mock_response.reason = error_reason + mock_send.side_effect = [ + exception_type('some exception', response=mock_response) + ] + + with beam.Pipeline(runner='BundleBasedDirectRunner') as p: + bq_write_out = ( + p + | beam.Create([{ + 'columnA': 'value1' + }, { + 'columnA': 'value2' + }]) + | WriteToBigQuery( + table='project:dataset.table', + schema={ + 'fields': [{ + 'name': 'columnA', 'type': 'STRING', 'mode': 'NULLABLE' + }] + }, + create_disposition='CREATE_NEVER', + method='STREAMING_INSERTS', + insert_retry_strategy=RetryStrategy.RETRY_NEVER)) + failed_values = ( + bq_write_out[beam_bq.BigQueryWriteFn.FAILED_ROWS_WITH_ERRORS] + | beam.Map(lambda x: x[1]['columnA'])) + + assert_that(failed_values, equal_to(['value1', 'value2'])) + + self.assertEqual(1, mock_send.call_count) + + # Running tests with a variety of exceptions from https://googleapis.dev + # /python/google-api-core/latest/_modules/google/api_core/exceptions.html. + # Choosing some exceptions that produce reasons that are included in + # bigquery_tools._NON_TRANSIENT_ERRORS and some that are not + @parameterized.expand([ param( exception_type=exceptions.DeadlineExceeded if exceptions else None, - error_reason='somereason'), + error_reason='Deadline Exceeded', # not in _NON_TRANSIENT_ERRORS + failed_values=[], + expected_call_count=2), param( - exception_type=exceptions.ServiceUnavailable if exceptions else None, - error_reason='backendError'), + exception_type=exceptions.Conflict if exceptions else None, + error_reason='Conflict', # not in _NON_TRANSIENT_ERRORS + failed_values=[], + expected_call_count=2), param( - exception_type=exceptions.InternalServerError if exceptions else None, - error_reason='internalError'), + exception_type=exceptions.TooManyRequests if exceptions else None, + error_reason='Too Many Requests', # not in _NON_TRANSIENT_ERRORS + failed_values=[], + expected_call_count=2), param( exception_type=exceptions.InternalServerError if exceptions else None, - error_reason='backendError'), + error_reason='Internal Server Error', # not in _NON_TRANSIENT_ERRORS + failed_values=[], + expected_call_count=2), + param( + exception_type=exceptions.BadGateway if exceptions else None, + error_reason='Bad Gateway', # not in _NON_TRANSIENT_ERRORS + failed_values=[], + expected_call_count=2), + param( + exception_type=exceptions.ServiceUnavailable if exceptions else None, + error_reason='Service Unavailable', # not in _NON_TRANSIENT_ERRORS + failed_values=[], + expected_call_count=2), + param( + exception_type=exceptions.GatewayTimeout if exceptions else None, + error_reason='Gateway Timeout', # not in _NON_TRANSIENT_ERRORS + failed_values=[], + expected_call_count=2), + param( + exception_type=exceptions.BadRequest if exceptions else None, + error_reason='Bad Request', # in _NON_TRANSIENT_ERRORS + failed_values=['value1', 'value2'], + expected_call_count=1), + param( + exception_type=exceptions.Unauthorized if exceptions else None, + error_reason='Unauthorized', # in _NON_TRANSIENT_ERRORS + failed_values=['value1', 'value2'], + expected_call_count=1), + param( + exception_type=exceptions.Forbidden if exceptions else None, + error_reason='Forbidden', # in _NON_TRANSIENT_ERRORS + failed_values=['value1', 'value2'], + expected_call_count=1), + param( + exception_type=exceptions.NotFound if exceptions else None, + error_reason='Not Found', # in _NON_TRANSIENT_ERRORS + failed_values=['value1', 'value2'], + expected_call_count=1), + param( + exception_type=exceptions.MethodNotImplemented + if exceptions else None, + error_reason='Not Implemented', # in _NON_TRANSIENT_ERRORS + failed_values=['value1', 'value2'], + expected_call_count=1), ]) @mock.patch('time.sleep') @mock.patch('google.cloud.bigquery.Client.insert_rows_json') - def test_insert_all_retries_if_structured_retriable( + def test_insert_rows_json_exception_retry_on_transient_error( self, mock_send, unused_mock_sleep, - exception_type=None, - error_reason=None): - # In this test, a BATCH pipeline will retry the known RETRIABLE errors. + exception_type, + error_reason, + failed_values, + expected_call_count): + # In this test, a pipeline will only retry caught exception types + # with reasons that are not in _NON_TRANSIENT_ERRORS since RetryStrategy is + # set to RETRY_ON_TRANSIENT_ERROR + mock_response = mock.Mock() + mock_response.reason = error_reason mock_send.side_effect = [ - exception_type( - 'some retriable exception', errors=[{ - 'reason': error_reason - }]), - exception_type( - 'some retriable exception', errors=[{ - 'reason': error_reason - }]), - exception_type( - 'some retriable exception', errors=[{ - 'reason': error_reason - }]), - exception_type( - 'some retriable exception', errors=[{ - 'reason': error_reason - }]), + exception_type('some exception', response=mock_response), + # Return no exception and no errors on 2nd call, if there is a 2nd call + [] ] - with self.assertRaises(Exception) as exc: - with beam.Pipeline() as p: - _ = ( - p - | beam.Create([{ - 'columnA': 'value1' - }]) - | WriteToBigQuery( - table='project:dataset.table', - schema={ - 'fields': [{ - 'name': 'columnA', 'type': 'STRING', 'mode': 'NULLABLE' - }] - }, - create_disposition='CREATE_NEVER', - method='STREAMING_INSERTS')) - self.assertEqual(4, mock_send.call_count) - self.assertIn('some retriable exception', exc.exception.args[0]) + with beam.Pipeline(runner='BundleBasedDirectRunner') as p: + bq_write_out = ( + p + | beam.Create([{ + 'columnA': 'value1' + }, { + 'columnA': 'value2' + }]) + | WriteToBigQuery( + table='project:dataset.table', + schema={ + 'fields': [{ + 'name': 'columnA', 'type': 'STRING', 'mode': 'NULLABLE' + }] + }, + create_disposition='CREATE_NEVER', + method='STREAMING_INSERTS', + insert_retry_strategy=RetryStrategy.RETRY_ON_TRANSIENT_ERROR)) + failed_values_out = ( + bq_write_out[beam_bq.BigQueryWriteFn.FAILED_ROWS] + | beam.Map(lambda x: x[1]['columnA'])) + + assert_that(failed_values_out, equal_to(failed_values)) + self.assertEqual(expected_call_count, mock_send.call_count) - # Using https://googleapis.dev/python/google-api-core/latest/_modules/google - # /api_core/exceptions.html - # to determine error types and messages to try for retriables. + # Running tests with persistent exceptions with exception types not + # caught in BigQueryWrapper._insert_all_rows but retriable by + # retry.with_exponential_backoff @parameterized.expand([ param( exception_type=requests.exceptions.ConnectionError, @@ -1009,28 +1200,18 @@ def test_insert_all_retries_if_structured_retriable( param( exception_type=requests.exceptions.Timeout, error_message='some timeout error'), - param( - exception_type=ConnectionError, - error_message='some py connection error'), - param( - exception_type=exceptions.BadGateway if exceptions else None, - error_message='some badgateway error'), ]) @mock.patch('time.sleep') @mock.patch('google.cloud.bigquery.Client.insert_rows_json') - def test_insert_all_retries_if_unstructured_retriable( - self, - mock_send, - unused_mock_sleep, - exception_type=None, - error_message=None): - # In this test, a BATCH pipeline will retry the unknown RETRIABLE errors. - mock_send.side_effect = [ - exception_type(error_message), - exception_type(error_message), - exception_type(error_message), - exception_type(error_message), - ] + def test_insert_rows_json_persistent_retriable_exception( + self, mock_send, unused_mock_sleep, exception_type, error_message): + # In this test, each insert_rows_json call will result in an exception + # and be retried with retry.with_exponential_backoff until MAX_RETRIES is + # reached + mock_send.side_effect = exception_type(error_message) + + # Expecting 1 initial call plus maximum number of retries + expected_call_count = 1 + bigquery_tools.MAX_RETRIES with self.assertRaises(Exception) as exc: with beam.Pipeline() as p: @@ -1038,6 +1219,8 @@ def test_insert_all_retries_if_unstructured_retriable( p | beam.Create([{ 'columnA': 'value1' + }, { + 'columnA': 'value2' }]) | WriteToBigQuery( table='project:dataset.table', @@ -1048,138 +1231,390 @@ def test_insert_all_retries_if_unstructured_retriable( }, create_disposition='CREATE_NEVER', method='STREAMING_INSERTS')) - self.assertEqual(4, mock_send.call_count) + + self.assertEqual(expected_call_count, mock_send.call_count) self.assertIn(error_message, exc.exception.args[0]) - # Using https://googleapis.dev/python/google-api-core/latest/_modules/google - # /api_core/exceptions.html - # to determine error types and messages to try for retriables. + # Running tests with intermittent exceptions with exception types not + # caught in BigQueryWrapper._insert_all_rows but retriable by + # retry.with_exponential_backoff @parameterized.expand([ param( - exception_type=retry.PermanentException, - error_args=('nonretriable', )), - param( - exception_type=exceptions.BadRequest if exceptions else None, - error_args=( - 'forbidden morbidden', [{ - 'reason': 'nonretriablereason' - }])), - param( - exception_type=exceptions.BadRequest if exceptions else None, - error_args=('BAD REQUEST!', [{ - 'reason': 'nonretriablereason' - }])), - param( - exception_type=exceptions.MethodNotAllowed if exceptions else None, - error_args=( - 'method not allowed!', [{ - 'reason': 'nonretriablereason' - }])), - param( - exception_type=exceptions.MethodNotAllowed if exceptions else None, - error_args=('method not allowed!', 'args')), - param( - exception_type=exceptions.Unknown if exceptions else None, - error_args=('unknown!', 'args')), + exception_type=requests.exceptions.ConnectionError, + error_message='some connection error'), param( - exception_type=exceptions.Aborted if exceptions else None, - error_args=('abortet!', 'abort')), + exception_type=requests.exceptions.Timeout, + error_message='some timeout error'), ]) @mock.patch('time.sleep') @mock.patch('google.cloud.bigquery.Client.insert_rows_json') - def test_insert_all_unretriable_errors( - self, mock_send, unused_mock_sleep, exception_type=None, error_args=None): - # In this test, a BATCH pipeline will retry the unknown RETRIABLE errors. + def test_insert_rows_json_intermittent_retriable_exception( + self, mock_send, unused_mock_sleep, exception_type, error_message): + # In this test, the first 2 insert_rows_json calls will result in an + # exception and be retried with retry.with_exponential_backoff. The last + # call will not raise an exception and will succeed. mock_send.side_effect = [ - exception_type(*error_args), - exception_type(*error_args), - exception_type(*error_args), - exception_type(*error_args), + exception_type(error_message), exception_type(error_message), [] ] - with self.assertRaises(Exception): - with beam.Pipeline() as p: - _ = ( + with beam.Pipeline() as p: + _ = ( + p + | beam.Create([{ + 'columnA': 'value1' + }, { + 'columnA': 'value2' + }]) + | WriteToBigQuery( + table='project:dataset.table', + schema={ + 'fields': [{ + 'name': 'columnA', 'type': 'STRING', 'mode': 'NULLABLE' + }] + }, + create_disposition='CREATE_NEVER', + method='STREAMING_INSERTS')) + + self.assertEqual(3, mock_send.call_count) + + # Running tests with a variety of error reasons from + # https://cloud.google.com/bigquery/docs/error-messages + # This covers the scenario when + # the google.cloud.bigquery.Client.insert_rows_json call returns an error list + # rather than raising an exception. + # Choosing some error reasons that are included in + # bigquery_tools._NON_TRANSIENT_ERRORS and some that are not + @parameterized.expand([ + # reason in _NON_TRANSIENT_ERRORS for row 1, sent to failed_rows + param( + insert_response=[ + [{ + 'index': 0, 'errors': [{ + 'reason': 'invalid' + }] + }], + [{ + 'index': 0, 'errors': [{ + 'reason': 'invalid' + }] + }], + ], + failed_rows=['value1']), + # reason in _NON_TRANSIENT_ERRORS for row 1 + # reason not in _NON_TRANSIENT_ERRORS for row 2 on 1st run + # row 1 sent to failed_rows + param( + insert_response=[ + [{ + 'index': 0, 'errors': [{ + 'reason': 'invalid' + }] + }, { + 'index': 1, 'errors': [{ + 'reason': 'internalError' + }] + }], + [{ + 'index': 0, 'errors': [{ + 'reason': 'invalid' + }] + }], + ], + failed_rows=['value1']), + # reason not in _NON_TRANSIENT_ERRORS for row 1 on first attempt + # transient error succeeds on second attempt, 0 rows sent to failed rows + param( + insert_response=[ + [{ + 'index': 0, 'errors': [{ + 'reason': 'internalError' + }] + }], + [], + ], + failed_rows=[]), + ]) + def test_insert_rows_json_errors_retry_always( + self, insert_response, failed_rows, unused_sleep_mock=None): + # In this test, a pipeline will always retry all errors + # since RetryStrategy is not set and defaults to RETRY_ALWAYS + with mock.patch('time.sleep'): + call_counter = 0 + + def store_callback(table, **kwargs): + nonlocal call_counter + response = insert_response[call_counter] + call_counter += 1 + return response + + client = mock.Mock() + client.insert_rows_json = mock.Mock(side_effect=store_callback) + + # Using the bundle based direct runner to avoid pickling problems + # with mocks. + with beam.Pipeline(runner='BundleBasedDirectRunner') as p: + bq_write_out = ( p | beam.Create([{ - 'columnA': 'value1' + 'columnA': 'value1', 'columnB': 'value2' + }, { + 'columnA': 'value3', 'columnB': 'value4' + }, { + 'columnA': 'value5', 'columnB': 'value6' }]) - | WriteToBigQuery( - table='project:dataset.table', - schema={ - 'fields': [{ - 'name': 'columnA', 'type': 'STRING', 'mode': 'NULLABLE' - }] - }, + # Using _StreamToBigQuery in order to be able to pass max_retries + # in order to limit run time of test with RETRY_ALWAYS + | _StreamToBigQuery( + table_reference='project:dataset.table', + table_side_inputs=[], + schema_side_inputs=[], + schema='anyschema', + batch_size=None, + triggering_frequency=None, create_disposition='CREATE_NEVER', - method='STREAMING_INSERTS')) - self.assertEqual(1, mock_send.call_count) + write_disposition=None, + kms_key=None, + retry_strategy=RetryStrategy.RETRY_ALWAYS, + additional_bq_parameters=[], + ignore_insert_ids=False, + ignore_unknown_columns=False, + with_auto_sharding=False, + test_client=client, + max_retries=len(insert_response) - 1, + num_streaming_keys=500)) - # Using https://googleapis.dev/python/google-api-core/latest/_modules/google - # /api_core/exceptions.html - # to determine error types and messages to try for retriables. + failed_values = ( + bq_write_out[beam_bq.BigQueryWriteFn.FAILED_ROWS] + | beam.Map(lambda x: x[1]['columnA'])) + + assert_that(failed_values, equal_to(failed_rows)) + + # Running tests with a variety of error reasons from + # https://cloud.google.com/bigquery/docs/error-messages + # This covers the scenario when + # the google.cloud.bigquery.Client.insert_rows_json call returns an error list + # rather than raising an exception. + # Choosing some error reasons that are included in + # bigquery_tools._NON_TRANSIENT_ERRORS and some that are not @parameterized.expand([ + # reason in _NON_TRANSIENT_ERRORS for row 1, sent to failed_rows param( - exception_type=retry.PermanentException, - error_args=('nonretriable', )), - param( - exception_type=exceptions.BadRequest if exceptions else None, - error_args=( - 'forbidden morbidden', [{ - 'reason': 'nonretriablereason' - }])), + insert_response=[ + [{ + 'index': 0, 'errors': [{ + 'reason': 'invalidQuery' + }] + }], + ], + streaming=False), + # reason not in _NON_TRANSIENT_ERRORS for row 1, sent to failed_rows param( - exception_type=exceptions.BadRequest if exceptions else None, - error_args=('BAD REQUEST!', [{ - 'reason': 'nonretriablereason' - }])), + insert_response=[ + [{ + 'index': 0, 'errors': [{ + 'reason': 'internalError' + }] + }], + ], + streaming=False), param( - exception_type=exceptions.MethodNotAllowed if exceptions else None, - error_args=( - 'method not allowed!', [{ - 'reason': 'nonretriablereason' - }])), + insert_response=[ + [{ + 'index': 0, 'errors': [{ + 'reason': 'invalid' + }] + }], + ], + streaming=True), + # reason not in _NON_TRANSIENT_ERRORS for row 1, sent to failed_rows param( - exception_type=exceptions.MethodNotAllowed if exceptions else None, - error_args=('method not allowed!', 'args')), + insert_response=[ + [{ + 'index': 0, 'errors': [{ + 'reason': 'internalError' + }] + }], + ], + streaming=True), + ]) + @mock.patch('time.sleep') + @mock.patch('google.cloud.bigquery.Client.insert_rows_json') + def test_insert_rows_json_errors_retry_never( + self, mock_send, unused_mock_sleep, insert_response, streaming): + # In this test, a pipeline will never retry errors since RetryStrategy is + # set to RETRY_NEVER + mock_send.side_effect = insert_response + opt = StandardOptions() + opt.streaming = streaming + with beam.Pipeline(runner='BundleBasedDirectRunner', options=opt) as p: + bq_write_out = ( + p + | beam.Create([{ + 'columnA': 'value1' + }, { + 'columnA': 'value2' + }]) + | WriteToBigQuery( + table='project:dataset.table', + schema={ + 'fields': [{ + 'name': 'columnA', 'type': 'STRING', 'mode': 'NULLABLE' + }] + }, + create_disposition='CREATE_NEVER', + method='STREAMING_INSERTS', + insert_retry_strategy=RetryStrategy.RETRY_NEVER)) + failed_values = ( + bq_write_out[beam_bq.BigQueryWriteFn.FAILED_ROWS_WITH_ERRORS] + | beam.Map(lambda x: x[1]['columnA'])) + + assert_that(failed_values, equal_to(['value1'])) + + self.assertEqual(1, mock_send.call_count) + + # Running tests with a variety of error reasons from + # https://cloud.google.com/bigquery/docs/error-messages + # This covers the scenario when + # the google.cloud.bigquery.Client.insert_rows_json call returns an error list + # rather than raising an exception. + # Choosing some error reasons that are included in + # bigquery_tools._NON_TRANSIENT_ERRORS and some that are not + @parameterized.expand([ + # reason in _NON_TRANSIENT_ERRORS for row 1, sent to failed_rows param( - exception_type=exceptions.Unknown if exceptions else None, - error_args=('unknown!', 'args')), + insert_response=[ + [{ + 'index': 0, 'errors': [{ + 'reason': 'invalid' + }] + }], + ], + failed_rows=['value1'], + streaming=False), + # reason not in _NON_TRANSIENT_ERRORS for row 1 on 1st attempt + # transient error succeeds on 2nd attempt, 0 rows sent to failed rows param( - exception_type=exceptions.Aborted if exceptions else None, - error_args=('abortet!', 'abort')), + insert_response=[ + [{ + 'index': 0, 'errors': [{ + 'reason': 'internalError' + }] + }], + [], + ], + failed_rows=[], + streaming=False), + # reason in _NON_TRANSIENT_ERRORS for row 1 + # reason not in _NON_TRANSIENT_ERRORS for row 2 on 1st and 2nd attempt + # all rows with errors are retried when any row has a retriable error + # row 1 sent to failed_rows after final attempt param( - exception_type=requests.exceptions.ConnectionError, - error_args=('some connection error', )), + insert_response=[ + [{ + 'index': 0, 'errors': [{ + 'reason': 'invalid' + }] + }, { + 'index': 1, 'errors': [{ + 'reason': 'internalError' + }] + }], + [ + { + 'index': 0, 'errors': [{ + 'reason': 'invalid' + }] + }, + ], + ], + failed_rows=['value1'], + streaming=False), + # reason in _NON_TRANSIENT_ERRORS for row 1, sent to failed_rows param( - exception_type=requests.exceptions.Timeout, - error_args=('some timeout error', )), + insert_response=[ + [{ + 'index': 0, 'errors': [{ + 'reason': 'invalid' + }] + }], + ], + failed_rows=['value1'], + streaming=True), + # reason not in _NON_TRANSIENT_ERRORS for row 1 on 1st attempt + # transient error succeeds on 2nd attempt, 0 rows sent to failed rows param( - exception_type=ConnectionError, - error_args=('some py connection error', )), + insert_response=[ + [{ + 'index': 0, 'errors': [{ + 'reason': 'internalError' + }] + }], + [], + ], + failed_rows=[], + streaming=True), + # reason in _NON_TRANSIENT_ERRORS for row 1 + # reason not in _NON_TRANSIENT_ERRORS for row 2 on 1st and 2nd attempt + # all rows with errors are retried when any row has a retriable error + # row 1 sent to failed_rows after final attempt param( - exception_type=exceptions.BadGateway if exceptions else None, - error_args=('some badgateway error', )), + insert_response=[ + [{ + 'index': 0, 'errors': [{ + 'reason': 'invalid' + }] + }, { + 'index': 1, 'errors': [{ + 'reason': 'internalError' + }] + }], + [ + { + 'index': 0, 'errors': [{ + 'reason': 'invalid' + }] + }, + ], + ], + failed_rows=['value1'], + streaming=True), ]) @mock.patch('time.sleep') @mock.patch('google.cloud.bigquery.Client.insert_rows_json') - def test_insert_all_unretriable_errors_streaming( - self, mock_send, unused_mock_sleep, exception_type=None, error_args=None): - # In this test, a STREAMING pipeline will retry ALL errors, and never throw - # an exception. - mock_send.side_effect = [ - exception_type(*error_args), - exception_type(*error_args), - [] # Errors thrown twice, and then succeeded - ] + def test_insert_rows_json_errors_retry_on_transient_error( + self, + mock_send, + unused_mock_sleep, + insert_response, + failed_rows, + streaming=False): + # In this test, a pipeline will only retry errors with reasons that are not + # in _NON_TRANSIENT_ERRORS since RetryStrategy is set to + # RETRY_ON_TRANSIENT_ERROR + call_counter = 0 + + def store_callback(table, **kwargs): + nonlocal call_counter + response = insert_response[call_counter] + call_counter += 1 + return response + + mock_send.side_effect = store_callback opt = StandardOptions() - opt.streaming = True + opt.streaming = streaming + + # Using the bundle based direct runner to avoid pickling problems + # with mocks. with beam.Pipeline(runner='BundleBasedDirectRunner', options=opt) as p: - _ = ( + bq_write_out = ( p | beam.Create([{ 'columnA': 'value1' + }, { + 'columnA': 'value2' + }, { + 'columnA': 'value3' }]) | WriteToBigQuery( table='project:dataset.table', @@ -1189,8 +1624,14 @@ def test_insert_all_unretriable_errors_streaming( }] }, create_disposition='CREATE_NEVER', - method='STREAMING_INSERTS')) - self.assertEqual(3, mock_send.call_count) + method='STREAMING_INSERTS', + insert_retry_strategy=RetryStrategy.RETRY_ON_TRANSIENT_ERROR)) + + failed_values = ( + bq_write_out[beam_bq.BigQueryWriteFn.FAILED_ROWS] + | beam.Map(lambda x: x[1]['columnA'])) + + assert_that(failed_values, equal_to(failed_rows)) @unittest.skipIf(HttpError is None, 'GCP dependencies are not installed') @@ -1499,76 +1940,6 @@ def store_callback(table, **kwargs): result) self.assertEqual(len(data1['colA_values']), 1) - @parameterized.expand([ - param(retry_strategy=RetryStrategy.RETRY_ALWAYS), - param(retry_strategy=RetryStrategy.RETRY_NEVER), - param(retry_strategy=RetryStrategy.RETRY_ON_TRANSIENT_ERROR), - ]) - def test_permanent_failure_in_some_rows_does_not_duplicate( - self, unused_sleep_mock=None, retry_strategy=None): - with mock.patch('time.sleep'): - - def store_callback(table, **kwargs): - return [ - { - 'index': 0, - 'errors': [{ - 'reason': 'invalid' - }, { - 'reason': 'its bad' - }] - }, - ] - - client = mock.Mock() - client.insert_rows_json = mock.Mock(side_effect=store_callback) - - # The expected rows to be inserted according to the insert strategy - if retry_strategy == RetryStrategy.RETRY_NEVER: - inserted_rows = ['value3', 'value5'] - else: # RETRY_ALWAYS and RETRY_ON_TRANSIENT_ERRORS should insert all rows - inserted_rows = ['value3', 'value5'] - - # Using the bundle based direct runner to avoid pickling problems - # with mocks. - with beam.Pipeline(runner='BundleBasedDirectRunner') as p: - bq_write_out = ( - p - | beam.Create([{ - 'columnA': 'value1', 'columnB': 'value2' - }, { - 'columnA': 'value3', 'columnB': 'value4' - }, { - 'columnA': 'value5', 'columnB': 'value6' - }]) - | _StreamToBigQuery( - table_reference='project:dataset.table', - table_side_inputs=[], - schema_side_inputs=[], - schema='anyschema', - batch_size=None, - triggering_frequency=None, - create_disposition='CREATE_NEVER', - write_disposition=None, - kms_key=None, - retry_strategy=retry_strategy, - additional_bq_parameters=[], - ignore_insert_ids=False, - ignore_unknown_columns=False, - with_auto_sharding=False, - test_client=client, - max_retries=10, - num_streaming_keys=500)) - - failed_values = ( - bq_write_out[beam_bq.BigQueryWriteFn.FAILED_ROWS] - | beam.Map(lambda x: x[1]['columnA'])) - - assert_that( - failed_values, - equal_to( - list({'value1', 'value3', 'value5'}.difference(inserted_rows)))) - @parameterized.expand([ param(with_auto_sharding=False), param(with_auto_sharding=True), diff --git a/sdks/python/apache_beam/io/gcp/bigquery_tools.py b/sdks/python/apache_beam/io/gcp/bigquery_tools.py index 07d711f8fc92..2f9420795288 100644 --- a/sdks/python/apache_beam/io/gcp/bigquery_tools.py +++ b/sdks/python/apache_beam/io/gcp/bigquery_tools.py @@ -732,11 +732,13 @@ def _insert_all_rows( except (ClientError, GoogleAPICallError) as e: # e.code contains the numeric http status code. service_call_metric.call(e.code) - # Re-reise the exception so that we re-try appropriately. - raise + # Package exception with required fields + error = {'message': e.message, 'reason': e.response.reason} + # Add all rows to the errors list along with the error + errors = [{"index": i, "errors": [error]} for i, _ in enumerate(rows)] except HttpError as e: service_call_metric.call(e) - # Re-reise the exception so that we re-try appropriately. + # Re-raise the exception so that we re-try appropriately. raise finally: self._latency_histogram_metric.update( @@ -1491,7 +1493,19 @@ class RetryStrategy(object): RETRY_NEVER = 'RETRY_NEVER' RETRY_ON_TRANSIENT_ERROR = 'RETRY_ON_TRANSIENT_ERROR' - _NON_TRANSIENT_ERRORS = {'invalid', 'invalidQuery', 'notImplemented'} + # Values below may be found in reasons provided either in an + # error returned by a client method or by an http response as + # defined in google.api_core.exceptions + _NON_TRANSIENT_ERRORS = { + 'invalid', + 'invalidQuery', + 'notImplemented', + 'Bad Request', + 'Unauthorized', + 'Forbidden', + 'Not Found', + 'Not Implemented', + } @staticmethod def should_retry(strategy, error_message): diff --git a/sdks/python/apache_beam/io/gcp/bigquery_write_it_test.py b/sdks/python/apache_beam/io/gcp/bigquery_write_it_test.py index c73d3ff7e53e..4b728fe7ec1f 100644 --- a/sdks/python/apache_beam/io/gcp/bigquery_write_it_test.py +++ b/sdks/python/apache_beam/io/gcp/bigquery_write_it_test.py @@ -379,7 +379,7 @@ def test_big_query_write_without_schema(self): def test_big_query_write_insert_errors_reporting(self): """ Test that errors returned by beam.io.WriteToBigQuery - contain both the failed rows amd the reason for it failing. + contain both the failed rows and the reason for it failing. """ table_name = 'python_write_table' table_id = '{}.{}'.format(self.dataset_id, table_name) @@ -454,6 +454,55 @@ def test_big_query_write_insert_errors_reporting(self): | 'ParseErrors' >> beam.Map(lambda err: (err[1], err[2])), equal_to(bq_result_errors)) + @pytest.mark.it_postcommit + def test_big_query_write_insert_non_transient_api_call_error(self): + """ + Test that non-transient GoogleAPICallError errors returned + by beam.io.WriteToBigQuery are not retried and result in + FAILED_ROWS containing both the failed rows and the reason + for failure. + """ + table_name = 'this_table_does_not_exist' + table_id = '{}.{}'.format(self.dataset_id, table_name) + + input_data = [{ + 'number': 1, + 'str': 'some_string', + }] + + table_schema = { + "fields": [{ + "name": "number", "type": "INTEGER", 'mode': 'NULLABLE' + }, { + "name": "str", "type": "STRING", 'mode': 'NULLABLE' + }] + } + + bq_result_errors = [({ + 'number': 1, + 'str': 'some_string', + }, "Not Found")] + + args = self.test_pipeline.get_full_options_as_args() + + with beam.Pipeline(argv=args) as p: + # pylint: disable=expression-not-assigned + errors = ( + p | 'create' >> beam.Create(input_data) + | 'write' >> beam.io.WriteToBigQuery( + table_id, + schema=table_schema, + method='STREAMING_INSERTS', + insert_retry_strategy='RETRY_ON_TRANSIENT_ERROR', + create_disposition=beam.io.BigQueryDisposition.CREATE_NEVER, + write_disposition=beam.io.BigQueryDisposition.WRITE_APPEND)) + + assert_that( + errors[BigQueryWriteFn.FAILED_ROWS_WITH_ERRORS] + | + 'ParseErrors' >> beam.Map(lambda err: (err[1], err[2][0]["reason"])), + equal_to(bq_result_errors)) + @pytest.mark.it_postcommit @parameterized.expand([ param(file_format=FileFormat.AVRO), From 272da41a1cc83d20078f69c57e1c89cc289db735 Mon Sep 17 00:00:00 2001 From: Dip Patel <37777672+Dippatel98@users.noreply.github.com> Date: Tue, 19 Sep 2023 15:21:46 -0500 Subject: [PATCH 078/105] Updating access modifiers for utility methods (#28511) * Updating access modifiers for utility methods * Revert "Updating access modifiers for utility methods" This reverts commit 99eb9260b7ee2f700affc25df68115e8ffa7521d. * Updating access modifiers for utility methods --- .../jdbc/src/main/java/org/apache/beam/sdk/io/jdbc/JdbcIO.java | 2 +- .../src/main/java/org/apache/beam/sdk/io/jdbc/SchemaUtil.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdks/java/io/jdbc/src/main/java/org/apache/beam/sdk/io/jdbc/JdbcIO.java b/sdks/java/io/jdbc/src/main/java/org/apache/beam/sdk/io/jdbc/JdbcIO.java index e49ef05974fe..6e7ad865cc35 100644 --- a/sdks/java/io/jdbc/src/main/java/org/apache/beam/sdk/io/jdbc/JdbcIO.java +++ b/sdks/java/io/jdbc/src/main/java/org/apache/beam/sdk/io/jdbc/JdbcIO.java @@ -786,7 +786,7 @@ public PCollection expand(PBegin input) { // Spotbugs seems to not understand the multi-statement try-with-resources @SuppressFBWarnings("OBL_UNSATISFIED_OBLIGATION") - private static Schema inferBeamSchema(DataSource ds, String query) { + public static Schema inferBeamSchema(DataSource ds, String query) { try (Connection conn = ds.getConnection(); PreparedStatement statement = conn.prepareStatement( diff --git a/sdks/java/io/jdbc/src/main/java/org/apache/beam/sdk/io/jdbc/SchemaUtil.java b/sdks/java/io/jdbc/src/main/java/org/apache/beam/sdk/io/jdbc/SchemaUtil.java index 234b60cd3879..65f21308ea32 100644 --- a/sdks/java/io/jdbc/src/main/java/org/apache/beam/sdk/io/jdbc/SchemaUtil.java +++ b/sdks/java/io/jdbc/src/main/java/org/apache/beam/sdk/io/jdbc/SchemaUtil.java @@ -354,7 +354,7 @@ private static ResultSetFieldExtractor createObjectExtractor() { * A {@link org.apache.beam.sdk.io.jdbc.JdbcIO.RowMapper} implementation that converts JDBC * results into Beam {@link Row} objects. */ - static final class BeamRowMapper implements JdbcIO.RowMapper { + public static final class BeamRowMapper implements JdbcIO.RowMapper { private final Schema schema; private final List fieldExtractors; From 6261a007a9e1f3eff2ba7cc003d96bd5d77231a9 Mon Sep 17 00:00:00 2001 From: Sam sam Date: Tue, 19 Sep 2023 13:26:39 -0700 Subject: [PATCH 079/105] Log user code exceptions in Java (#28514) * Fix transform id not set during exceptions * spotless * spotless --------- Co-authored-by: Sam Rohde --- .../org/apache/beam/fn/harness/FnHarness.java | 1 - .../harness/control/BeamFnControlClient.java | 4 +- .../control/ExecutionStateSampler.java | 23 +++ .../data/PCollectionConsumerRegistry.java | 47 ++++-- .../harness/logging/BeamFnLoggingClient.java | 23 +-- .../fn/harness/logging/BeamFnLoggingMDC.java | 20 +++ .../control/ExecutionStateSamplerTest.java | 25 ++++ .../data/PCollectionConsumerRegistryTest.java | 134 ++++++++++++++++++ .../logging/BeamFnLoggingClientTest.java | 20 +++ 9 files changed, 263 insertions(+), 34 deletions(-) diff --git a/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/FnHarness.java b/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/FnHarness.java index 448c8d42df75..e103da4d6007 100644 --- a/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/FnHarness.java +++ b/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/FnHarness.java @@ -311,7 +311,6 @@ private BeamFnApi.ProcessBundleDescriptor loadDescriptor(String id) { executionStateSampler, processWideCache, dataSampler); - logging.setProcessBundleHandler(processBundleHandler); BeamFnStatusClient beamFnStatusClient = null; if (statusApiServiceDescriptor != null) { diff --git a/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/control/BeamFnControlClient.java b/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/control/BeamFnControlClient.java index 0509a26c76bb..876a838f1662 100644 --- a/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/control/BeamFnControlClient.java +++ b/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/control/BeamFnControlClient.java @@ -118,11 +118,11 @@ public void onNext(BeamFnApi.InstructionRequest request) { sendErrorResponse(e); throw e; } finally { - BeamFnLoggingMDC.setInstructionId(null); + BeamFnLoggingMDC.reset(); } }); } finally { - BeamFnLoggingMDC.setInstructionId(null); + BeamFnLoggingMDC.reset(); } } diff --git a/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/control/ExecutionStateSampler.java b/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/control/ExecutionStateSampler.java index 313bbf5b4fa2..a82ce9276820 100644 --- a/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/control/ExecutionStateSampler.java +++ b/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/control/ExecutionStateSampler.java @@ -32,6 +32,7 @@ import java.util.concurrent.atomic.AtomicReference; import javax.annotation.concurrent.GuardedBy; import org.apache.beam.fn.harness.control.ProcessBundleHandler.BundleProcessor; +import org.apache.beam.fn.harness.logging.BeamFnLoggingMDC; import org.apache.beam.model.pipeline.v1.MetricsApi.MonitoringInfo; import org.apache.beam.runners.core.metrics.MetricsContainerStepMap; import org.apache.beam.runners.core.metrics.MonitoringInfoEncodings; @@ -120,6 +121,14 @@ public interface ExecutionState { *

Must only be invoked by the bundle processing thread. */ void deactivate(); + + /** + * Sets the error state to the currently executing state. Returns true if this was the first + * time the error was set. Returns false otherwise. + * + *

This can only be set once. + */ + boolean error(); } /** Stops the execution of the state sampler. */ @@ -250,6 +259,8 @@ public class ExecutionStateTracker implements BundleProgressReporter { private @Nullable ExecutionStateImpl currentState; // Read by multiple threads, written by the bundle processing thread lazily. private final AtomicReference<@Nullable ExecutionStateImpl> currentStateLazy; + // If an exception occurs, this will be to state at the time of exception. + private boolean inErrorState = false; // Read and written by the ExecutionStateSampler thread private long transitionsAtLastSample; @@ -465,6 +476,15 @@ public void deactivate() { numTransitions += 1; numTransitionsLazy.lazySet(numTransitions); } + + @Override + public boolean error() { + if (!inErrorState) { + inErrorState = true; + return true; + } + return false; + } } /** @@ -473,6 +493,7 @@ public void deactivate() { *

Only invoked by the bundle processing thread. */ public void start(String processBundleId) { + BeamFnLoggingMDC.setStateTracker(this); this.processBundleId.lazySet(processBundleId); this.lastTransitionTime.lazySet(clock.getMillis()); this.trackedThread.lazySet(Thread.currentThread()); @@ -514,6 +535,8 @@ public void reset() { this.numTransitionsLazy.lazySet(0); this.lastTransitionTime.lazySet(0); this.metricsContainerRegistry.reset(); + this.inErrorState = false; + BeamFnLoggingMDC.setStateTracker(null); } } diff --git a/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/data/PCollectionConsumerRegistry.java b/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/data/PCollectionConsumerRegistry.java index a7a8766ffc7b..e27df577779c 100644 --- a/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/data/PCollectionConsumerRegistry.java +++ b/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/data/PCollectionConsumerRegistry.java @@ -53,6 +53,8 @@ import org.apache.beam.sdk.util.WindowedValue; import org.apache.beam.sdk.util.WindowedValue.WindowedValueCoder; import org.apache.beam.sdk.util.common.ElementByteSizeObserver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The {@code PCollectionConsumerRegistry} is used to maintain a collection of consuming @@ -97,6 +99,7 @@ public static ConsumerAndMetadata forConsumer( private final ProcessBundleDescriptor processBundleDescriptor; private final RehydratedComponents rehydratedComponents; private final @Nullable DataSampler dataSampler; + private static final Logger LOG = LoggerFactory.getLogger(PCollectionConsumerRegistry.class); public PCollectionConsumerRegistry( ExecutionStateTracker stateTracker, @@ -242,6 +245,26 @@ public FnDataReceiver> getMultiplexingConsumer(String pCollecti }); } + private static void logAndRethrow( + Exception e, + ExecutionState executionState, + ExecutionStateTracker executionStateTracker, + String ptransformId, + @Nullable OutputSampler outputSampler, + @Nullable ElementSample elementSample) + throws Exception { + ExecutionStateSampler.ExecutionStateTrackerStatus status = executionStateTracker.getStatus(); + String processBundleId = status == null ? null : status.getProcessBundleId(); + if (outputSampler != null) { + outputSampler.exception(elementSample, e, ptransformId, processBundleId); + } + + if (executionState.error()) { + LOG.error("Failed to process element for bundle \"{}\"", processBundleId, e); + } + throw e; + } + /** * A wrapping {@code FnDataReceiver>} which counts the number of elements * consumed by the original {@code FnDataReceiver> consumer} and sets up metrics @@ -324,13 +347,8 @@ public void accept(WindowedValue input) throws Exception { try { this.delegate.accept(input); } catch (Exception e) { - if (outputSampler != null) { - ExecutionStateSampler.ExecutionStateTrackerStatus status = - executionStateTracker.getStatus(); - String processBundleId = status == null ? null : status.getProcessBundleId(); - outputSampler.exception(elementSample, e, ptransformId, processBundleId); - } - throw e; + logAndRethrow( + e, executionState, executionStateTracker, ptransformId, outputSampler, elementSample); } finally { executionState.deactivate(); } @@ -419,14 +437,13 @@ public void accept(WindowedValue input) throws Exception { try { consumerAndMetadata.getConsumer().accept(input); } catch (Exception e) { - if (outputSampler != null) { - ExecutionStateSampler.ExecutionStateTrackerStatus status = - consumerAndMetadata.getExecutionStateTracker().getStatus(); - String processBundleId = status == null ? null : status.getProcessBundleId(); - outputSampler.exception( - elementSample, e, consumerAndMetadata.getPTransformId(), processBundleId); - } - throw e; + logAndRethrow( + e, + state, + consumerAndMetadata.getExecutionStateTracker(), + consumerAndMetadata.getPTransformId(), + outputSampler, + elementSample); } finally { state.deactivate(); } diff --git a/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/logging/BeamFnLoggingClient.java b/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/logging/BeamFnLoggingClient.java index 2d3e168eab50..8fa074b04768 100644 --- a/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/logging/BeamFnLoggingClient.java +++ b/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/logging/BeamFnLoggingClient.java @@ -40,8 +40,7 @@ import java.util.logging.LogRecord; import java.util.logging.Logger; import java.util.logging.SimpleFormatter; -import org.apache.beam.fn.harness.control.ProcessBundleHandler; -import org.apache.beam.fn.harness.control.ProcessBundleHandler.BundleProcessor; +import org.apache.beam.fn.harness.control.ExecutionStateSampler; import org.apache.beam.model.fnexecution.v1.BeamFnApi; import org.apache.beam.model.fnexecution.v1.BeamFnApi.LogEntry; import org.apache.beam.model.fnexecution.v1.BeamFnLoggingGrpc; @@ -105,8 +104,6 @@ public class BeamFnLoggingClient implements AutoCloseable { * so if they are garbage collected, our hierarchical configuration will be lost. */ private final Collection configuredLoggers = new ArrayList<>(); - private @Nullable ProcessBundleHandler processBundleHandler; - private final BlockingQueue bufferedLogEntries = new ArrayBlockingQueue<>(MAX_BUFFERED_LOG_ENTRY_COUNT); @@ -347,10 +344,6 @@ public void close() throws Exception { } } - public void setProcessBundleHandler(ProcessBundleHandler processBundleHandler) { - this.processBundleHandler = processBundleHandler; - } - // Reset the logging configuration to what it is at startup. @RequiresNonNull("configuredLoggers") @RequiresNonNull("logRecordHandler") @@ -440,14 +433,12 @@ public void publish(LogRecord record) { if (loggerName != null) { builder.setLogLocation(loggerName); } - if (instructionId != null && processBundleHandler != null) { - BundleProcessor bundleProcessor = - processBundleHandler.getBundleProcessorCache().find(instructionId); - if (bundleProcessor != null) { - String transformId = bundleProcessor.getStateTracker().getCurrentThreadsPTransformId(); - if (transformId != null) { - builder.setTransformId(transformId); - } + + ExecutionStateSampler.ExecutionStateTracker stateTracker = BeamFnLoggingMDC.getStateTracker(); + if (stateTracker != null) { + String transformId = stateTracker.getCurrentThreadsPTransformId(); + if (transformId != null) { + builder.setTransformId(transformId); } } diff --git a/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/logging/BeamFnLoggingMDC.java b/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/logging/BeamFnLoggingMDC.java index bcfcd4b34ea5..68b03a484904 100644 --- a/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/logging/BeamFnLoggingMDC.java +++ b/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/logging/BeamFnLoggingMDC.java @@ -17,12 +17,16 @@ */ package org.apache.beam.fn.harness.logging; +import org.apache.beam.fn.harness.control.ExecutionStateSampler.ExecutionStateTracker; import org.checkerframework.checker.nullness.qual.Nullable; /** Mapped diagnostic context to be consumed and set on LogEntry protos in BeamFnLoggingClient. */ public class BeamFnLoggingMDC { private static final ThreadLocal<@Nullable String> instructionId = new ThreadLocal<>(); + private static final ThreadLocal<@Nullable ExecutionStateTracker> stateTracker = + new ThreadLocal<>(); + /** Sets the Instruction ID of the current thread, which will be inherited by child threads. */ public static void setInstructionId(@Nullable String newInstructionId) { instructionId.set(newInstructionId); @@ -32,4 +36,20 @@ public static void setInstructionId(@Nullable String newInstructionId) { public static @Nullable String getInstructionId() { return instructionId.get(); } + + /** Sets the State Tracker of the current thread, which will be inherited by child threads. */ + public static void setStateTracker(@Nullable ExecutionStateTracker newStateTracker) { + stateTracker.set(newStateTracker); + } + + /** Gets the State Tracker of the current thread. */ + public static @Nullable ExecutionStateTracker getStateTracker() { + return stateTracker.get(); + } + + /** Resets to a default state. */ + public static void reset() { + instructionId.set(null); + stateTracker.set(null); + } } diff --git a/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/control/ExecutionStateSamplerTest.java b/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/control/ExecutionStateSamplerTest.java index 9d79de0fa153..47866adc892b 100644 --- a/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/control/ExecutionStateSamplerTest.java +++ b/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/control/ExecutionStateSamplerTest.java @@ -22,6 +22,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; @@ -648,4 +649,28 @@ public Long answer(InvocationOnMock invocation) throws Throwable { sampler.stop(); expectedLogs.verifyWarn("Operation ongoing in bundle bundleId for PTransform"); } + + @Test + public void testErrorState() throws Exception { + MillisProvider clock = mock(MillisProvider.class); + ExecutionStateSampler sampler = + new ExecutionStateSampler( + PipelineOptionsFactory.fromArgs("--experiments=state_sampling_period_millis=10") + .create(), + clock); + ExecutionStateTracker tracker = sampler.create(); + ExecutionState state1 = + tracker.create("shortId1", "ptransformId1", "ptransformIdName1", "process"); + ExecutionState state2 = + tracker.create("shortId2", "ptransformId2", "ptransformIdName2", "process"); + + state1.activate(); + state2.activate(); + assertTrue(state2.error()); + assertFalse(state2.error()); + state2.deactivate(); + assertFalse(state2.error()); + tracker.reset(); + assertTrue(state1.error()); + } } diff --git a/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/data/PCollectionConsumerRegistryTest.java b/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/data/PCollectionConsumerRegistryTest.java index b3e2788e378d..f75b84e76ad5 100644 --- a/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/data/PCollectionConsumerRegistryTest.java +++ b/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/data/PCollectionConsumerRegistryTest.java @@ -33,17 +33,26 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import org.apache.beam.fn.harness.HandlesSplits; import org.apache.beam.fn.harness.control.BundleProgressReporter; import org.apache.beam.fn.harness.control.ExecutionStateSampler; import org.apache.beam.fn.harness.control.ExecutionStateSampler.ExecutionStateTracker; import org.apache.beam.fn.harness.debug.DataSampler; +import org.apache.beam.fn.harness.logging.BeamFnLoggingClient; +import org.apache.beam.fn.harness.logging.BeamFnLoggingMDC; import org.apache.beam.model.fnexecution.v1.BeamFnApi; import org.apache.beam.model.fnexecution.v1.BeamFnApi.ProcessBundleDescriptor; +import org.apache.beam.model.fnexecution.v1.BeamFnLoggingGrpc; +import org.apache.beam.model.pipeline.v1.Endpoints; import org.apache.beam.model.pipeline.v1.MetricsApi.MonitoringInfo; import org.apache.beam.model.pipeline.v1.RunnerApi.PCollection; import org.apache.beam.runners.core.construction.SdkComponents; @@ -56,6 +65,7 @@ import org.apache.beam.sdk.coders.IterableCoder; import org.apache.beam.sdk.coders.StringUtf8Coder; import org.apache.beam.sdk.fn.data.FnDataReceiver; +import org.apache.beam.sdk.fn.test.TestStreams; import org.apache.beam.sdk.metrics.Counter; import org.apache.beam.sdk.metrics.Metrics; import org.apache.beam.sdk.metrics.MetricsEnvironment; @@ -65,6 +75,12 @@ import org.apache.beam.sdk.util.common.ElementByteSizeObservableIterable; import org.apache.beam.sdk.util.common.ElementByteSizeObservableIterator; import org.apache.beam.vendor.grpc.v1p54p0.com.google.protobuf.ByteString; +import org.apache.beam.vendor.grpc.v1p54p0.io.grpc.ManagedChannel; +import org.apache.beam.vendor.grpc.v1p54p0.io.grpc.Server; +import org.apache.beam.vendor.grpc.v1p54p0.io.grpc.inprocess.InProcessChannelBuilder; +import org.apache.beam.vendor.grpc.v1p54p0.io.grpc.inprocess.InProcessServerBuilder; +import org.apache.beam.vendor.grpc.v1p54p0.io.grpc.stub.CallStreamObserver; +import org.apache.beam.vendor.grpc.v1p54p0.io.grpc.stub.StreamObserver; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.Iterables; import org.junit.After; import org.junit.Before; @@ -567,6 +583,124 @@ public void dataSampling() throws Exception { assertTrue(elementList.getElementsList().containsAll(expectedSamples)); } + @Test + public void logsExceptionWithTransformId() throws Exception { + final String pTransformId = "pTransformId"; + final String message = "testException"; + final String instructionId = "instruction"; + final Exception thrownException = new Exception(message); + + // The following is a bunch of boiler-plate to set up a local FnApiLoggingService to catch any + // logs for later test + // expectations. + AtomicBoolean clientClosedStream = new AtomicBoolean(); + Collection values = new ConcurrentLinkedQueue<>(); + AtomicReference> outboundServerObserver = + new AtomicReference<>(); + CallStreamObserver inboundServerObserver = + TestStreams.withOnNext( + (BeamFnApi.LogEntry.List logEntries) -> + values.addAll(logEntries.getLogEntriesList())) + .withOnCompleted( + () -> { + // Remember that the client told us that this stream completed + clientClosedStream.set(true); + outboundServerObserver.get().onCompleted(); + }) + .build(); + + Endpoints.ApiServiceDescriptor apiServiceDescriptor = + Endpoints.ApiServiceDescriptor.newBuilder() + .setUrl(this.getClass().getName() + "-" + UUID.randomUUID().toString()) + .build(); + Server server = + InProcessServerBuilder.forName(apiServiceDescriptor.getUrl()) + .addService( + new BeamFnLoggingGrpc.BeamFnLoggingImplBase() { + @Override + public StreamObserver logging( + StreamObserver outboundObserver) { + outboundServerObserver.set(outboundObserver); + return inboundServerObserver; + } + }) + .build(); + server.start(); + ManagedChannel channel = InProcessChannelBuilder.forName(apiServiceDescriptor.getUrl()).build(); + // End logging boiler-plate... + + // This section is to set up the StateSampler with the expected metadata. + ExecutionStateSampler sampler = + new ExecutionStateSampler(PipelineOptionsFactory.create(), System::currentTimeMillis); + ExecutionStateSampler.ExecutionStateTracker stateTracker = sampler.create(); + stateTracker.start("process-bundle"); + ExecutionStateSampler.ExecutionState state = + stateTracker.create("shortId", pTransformId, pTransformId, "process"); + state.activate(); + + // Track the instruction and state in the logging system. In a real run, this is set when a + // ProcessBundlehandler + // starts processing. + BeamFnLoggingMDC.setInstructionId(instructionId); + BeamFnLoggingMDC.setStateTracker(stateTracker); + + // Start the test within the logging context. This reroutes logging through to the boiler-plate + // that was set up + // earlier. + try (BeamFnLoggingClient ignored = + BeamFnLoggingClient.createAndStart( + PipelineOptionsFactory.create(), + apiServiceDescriptor, + (Endpoints.ApiServiceDescriptor descriptor) -> channel)) { + + // Set up the component under test, the FnDataReceiver, to emit an exception when it starts. + ShortIdMap shortIds = new ShortIdMap(); + BundleProgressReporter.InMemory reporterAndRegistrar = new BundleProgressReporter.InMemory(); + PCollectionConsumerRegistry consumers = + new PCollectionConsumerRegistry( + stateTracker, shortIds, reporterAndRegistrar, TEST_DESCRIPTOR); + FnDataReceiver> consumer = mock(FnDataReceiver.class); + + consumers.register(P_COLLECTION_A, pTransformId, pTransformId + "Name", consumer); + + FnDataReceiver> wrapperConsumer = + (FnDataReceiver>) + (FnDataReceiver) consumers.getMultiplexingConsumer(P_COLLECTION_A); + + doThrow(thrownException).when(consumer).accept(any()); + expectedException.expectMessage(message); + expectedException.expect(Exception.class); + + // Run the test. + wrapperConsumer.accept(valueInGlobalWindow("elem")); + + } finally { + // The actual log entry has a lot of metadata that can't easily be controlled. So set the + // entries that are needed + // for this test and cull everything else. + final BeamFnApi.LogEntry expectedEntry = + BeamFnApi.LogEntry.newBuilder() + .setInstructionId(instructionId) + .setTransformId(pTransformId) + .setMessage("Failed to process element for bundle \"process-bundle\"") + .build(); + + List entries = new ArrayList<>(values); + assertEquals(1, entries.size()); + BeamFnApi.LogEntry actualEntry = entries.get(0); + BeamFnApi.LogEntry actualEntryCulled = + BeamFnApi.LogEntry.newBuilder() + .setInstructionId(actualEntry.getInstructionId()) + .setTransformId(actualEntry.getTransformId()) + .setMessage(actualEntry.getMessage()) + .build(); + + assertEquals(expectedEntry, actualEntryCulled); + + server.shutdownNow(); + } + } + private static class TestElementByteSizeObservableIterable extends ElementByteSizeObservableIterable> { private List elements; diff --git a/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/logging/BeamFnLoggingClientTest.java b/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/logging/BeamFnLoggingClientTest.java index 47b59bc68b07..8c7a40f8db90 100644 --- a/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/logging/BeamFnLoggingClientTest.java +++ b/sdks/java/harness/src/test/java/org/apache/beam/fn/harness/logging/BeamFnLoggingClientTest.java @@ -38,6 +38,7 @@ import java.util.logging.LogRecord; import java.util.logging.Logger; import java.util.logging.SimpleFormatter; +import org.apache.beam.fn.harness.control.ExecutionStateSampler; import org.apache.beam.model.fnexecution.v1.BeamFnApi; import org.apache.beam.model.fnexecution.v1.BeamFnLoggingGrpc; import org.apache.beam.model.pipeline.v1.Endpoints; @@ -95,6 +96,7 @@ public class BeamFnLoggingClientTest { .setInstructionId("instruction-1") .setSeverity(BeamFnApi.LogEntry.Severity.Enum.DEBUG) .setMessage("Message") + .setTransformId("ptransformId") .setThread("12345") .setTimestamp(Timestamp.newBuilder().setSeconds(1234567).setNanos(890000000).build()) .setLogLocation("LoggerName") @@ -104,6 +106,7 @@ public class BeamFnLoggingClientTest { .setInstructionId("instruction-1") .setSeverity(BeamFnApi.LogEntry.Severity.Enum.DEBUG) .setMessage("testMdcValue:Message") + .setTransformId("ptransformId") .setCustomData( Struct.newBuilder() .putFields( @@ -117,6 +120,7 @@ public class BeamFnLoggingClientTest { .setInstructionId("instruction-1") .setSeverity(BeamFnApi.LogEntry.Severity.Enum.WARN) .setMessage("MessageWithException") + .setTransformId("errorPtransformId") .setTrace(getStackTraceAsString(TEST_RECORD_WITH_EXCEPTION.getThrown())) .setThread("12345") .setTimestamp(Timestamp.newBuilder().setSeconds(1234567).setNanos(890000000).build()) @@ -126,7 +130,16 @@ public class BeamFnLoggingClientTest { @Test public void testLogging() throws Exception { + ExecutionStateSampler sampler = + new ExecutionStateSampler(PipelineOptionsFactory.create(), null); + ExecutionStateSampler.ExecutionStateTracker stateTracker = sampler.create(); + ExecutionStateSampler.ExecutionState state = + stateTracker.create("shortId", "ptransformId", "ptransformIdName", "process"); + state.activate(); + BeamFnLoggingMDC.setInstructionId("instruction-1"); + BeamFnLoggingMDC.setStateTracker(stateTracker); + AtomicBoolean clientClosedStream = new AtomicBoolean(); Collection values = new ConcurrentLinkedQueue<>(); AtomicReference> outboundServerObserver = @@ -188,7 +201,14 @@ public StreamObserver logging( rootLogger.log(FILTERED_RECORD); // Should not be filtered because the default log level override for ConfiguredLogger is DEBUG configuredLogger.log(TEST_RECORD); + + // Simulate an exception. This sets an internal error state where the PTransform should come + // from. + ExecutionStateSampler.ExecutionState errorState = + stateTracker.create("shortId", "errorPtransformId", "errorPtransformIdName", "process"); + errorState.activate(); configuredLogger.log(TEST_RECORD_WITH_EXCEPTION); + errorState.deactivate(); // Ensure that configuring a custom formatter on the logging handler will be honored. for (Handler handler : rootLogger.getHandlers()) { From 612bfc4b486dc1df08f6e82fe41d3abab842f060 Mon Sep 17 00:00:00 2001 From: Sam sam Date: Tue, 19 Sep 2023 14:40:52 -0700 Subject: [PATCH 080/105] Log user code exceptions in Python (#28515) * Log user code exceptions in Python * address linter * trigger tests --------- Co-authored-by: Sam Rohde --- sdks/python/apache_beam/runners/common.py | 4 + .../runners/worker/log_handler_test.py | 151 ++++++++++++++++++ 2 files changed, 155 insertions(+) diff --git a/sdks/python/apache_beam/runners/common.py b/sdks/python/apache_beam/runners/common.py index c3de9c190434..99cd26cc4098 100644 --- a/sdks/python/apache_beam/runners/common.py +++ b/sdks/python/apache_beam/runners/common.py @@ -24,6 +24,7 @@ # pytype: skip-file +import logging import sys import threading import traceback @@ -81,6 +82,8 @@ ENCODED_IMPULSE_VALUE = IMPULSE_VALUE_CODER_IMPL.encode_nested( GlobalWindows.windowed_value(b'')) +_LOGGER = logging.getLogger(__name__) + class NameContext(object): """Holds the name information for a step.""" @@ -1538,6 +1541,7 @@ def _reraise_augmented(self, exn, windowed_value=None): new_exn = new_exn.with_traceback(tb) self._maybe_sample_exception(exc_info, windowed_value) + _LOGGER.exception(new_exn) raise new_exn diff --git a/sdks/python/apache_beam/runners/worker/log_handler_test.py b/sdks/python/apache_beam/runners/worker/log_handler_test.py index 4adae90edceb..9eb9299cac39 100644 --- a/sdks/python/apache_beam/runners/worker/log_handler_test.py +++ b/sdks/python/apache_beam/runners/worker/log_handler_test.py @@ -23,17 +23,97 @@ import grpc +import apache_beam as beam +from apache_beam.coders.coders import FastPrimitivesCoder +from apache_beam.portability import common_urns from apache_beam.portability.api import beam_fn_api_pb2 from apache_beam.portability.api import beam_fn_api_pb2_grpc from apache_beam.portability.api import endpoints_pb2 +from apache_beam.runners import common from apache_beam.runners.common import NameContext +from apache_beam.runners.worker import bundle_processor from apache_beam.runners.worker import log_handler +from apache_beam.runners.worker import operations from apache_beam.runners.worker import statesampler +from apache_beam.runners.worker.bundle_processor import BeamTransformFactory +from apache_beam.runners.worker.bundle_processor import BundleProcessor +from apache_beam.transforms.window import GlobalWindow from apache_beam.utils import thread_pool_executor +from apache_beam.utils.windowed_value import WindowedValue _LOGGER = logging.getLogger(__name__) +@BeamTransformFactory.register_urn('beam:internal:testexn:v1', bytes) +def create_exception_dofn( + factory, transform_id, transform_proto, payload, consumers): + """Returns a test DoFn that raises the given exception.""" + class RaiseException(beam.DoFn): + def __init__(self, msg): + self.msg = msg.decode() + + def process(self, _): + raise RuntimeError(self.msg) + + return bundle_processor._create_simple_pardo_operation( + factory, + transform_id, + transform_proto, + consumers, + RaiseException(payload)) + + +class TestOperation(operations.Operation): + """Test operation that forwards its payload to consumers.""" + class Spec: + def __init__(self, transform_proto): + self.output_coders = [ + FastPrimitivesCoder() for _ in transform_proto.outputs + ] + + def __init__( + self, + transform_proto, + name_context, + counter_factory, + state_sampler, + consumers, + payload, + ): + super().__init__( + name_context, + self.Spec(transform_proto), + counter_factory, + state_sampler) + self.payload = payload + + for _, consumer_ops in consumers.items(): + for consumer in consumer_ops: + self.add_receiver(consumer, 0) + + def start(self): + super().start() + + # Not using windowing logic, so just using simple defaults here. + if self.payload: + self.process( + WindowedValue(self.payload, timestamp=0, windows=[GlobalWindow()])) + + def process(self, windowed_value): + self.output(windowed_value) + + +@BeamTransformFactory.register_urn('beam:internal:testop:v1', bytes) +def create_test_op(factory, transform_id, transform_proto, payload, consumers): + return TestOperation( + transform_proto, + common.NameContext(transform_proto.unique_name, transform_id), + factory.counter_factory, + factory.state_sampler, + consumers, + payload) + + class BeamFnLoggingServicer(beam_fn_api_pb2_grpc.BeamFnLoggingServicer): def __init__(self): self.log_records_received = [] @@ -153,6 +233,77 @@ def test_context(self): finally: statesampler.set_current_tracker(None) + def test_extracts_transform_id_during_exceptions(self): + """Tests that transform ids are captured during user code exceptions.""" + descriptor = beam_fn_api_pb2.ProcessBundleDescriptor() + + # Boiler plate for the DoFn. + WINDOWING_ID = 'window' + WINDOW_CODER_ID = 'cw' + window = descriptor.windowing_strategies[WINDOWING_ID] + window.window_fn.urn = common_urns.global_windows.urn + window.window_coder_id = WINDOW_CODER_ID + window.trigger.default.SetInParent() + window_coder = descriptor.coders[WINDOW_CODER_ID] + window_coder.spec.urn = common_urns.StandardCoders.Enum.GLOBAL_WINDOW.urn + + # Input collection to the exception raising DoFn. + INPUT_PCOLLECTION_ID = 'pc-in' + INPUT_CODER_ID = 'c-in' + descriptor.pcollections[ + INPUT_PCOLLECTION_ID].unique_name = INPUT_PCOLLECTION_ID + descriptor.pcollections[INPUT_PCOLLECTION_ID].coder_id = INPUT_CODER_ID + descriptor.pcollections[ + INPUT_PCOLLECTION_ID].windowing_strategy_id = WINDOWING_ID + descriptor.coders[ + INPUT_CODER_ID].spec.urn = common_urns.StandardCoders.Enum.BYTES.urn + + # Output collection to the exception raising DoFn. + OUTPUT_PCOLLECTION_ID = 'pc-out' + OUTPUT_CODER_ID = 'c-out' + descriptor.pcollections[ + OUTPUT_PCOLLECTION_ID].unique_name = OUTPUT_PCOLLECTION_ID + descriptor.pcollections[OUTPUT_PCOLLECTION_ID].coder_id = OUTPUT_CODER_ID + descriptor.pcollections[ + OUTPUT_PCOLLECTION_ID].windowing_strategy_id = WINDOWING_ID + descriptor.coders[ + OUTPUT_CODER_ID].spec.urn = common_urns.StandardCoders.Enum.BYTES.urn + + # Add a simple transform to inject an element into the fake pipeline. + TEST_OP_TRANSFORM_ID = 'test_op' + test_transform = descriptor.transforms[TEST_OP_TRANSFORM_ID] + test_transform.outputs['None'] = INPUT_PCOLLECTION_ID + test_transform.spec.urn = 'beam:internal:testop:v1' + test_transform.spec.payload = b'hello, world!' + + # Add the DoFn to create an exception. + TEST_EXCEPTION_TRANSFORM_ID = 'test_transform' + test_transform = descriptor.transforms[TEST_EXCEPTION_TRANSFORM_ID] + test_transform.inputs['0'] = INPUT_PCOLLECTION_ID + test_transform.outputs['None'] = OUTPUT_PCOLLECTION_ID + test_transform.spec.urn = 'beam:internal:testexn:v1' + test_transform.spec.payload = b'expected exception' + + # Create and process a fake bundle. The instruction id doesn't matter + # here. + processor = BundleProcessor(descriptor, None, None) + + with self.assertRaisesRegex(RuntimeError, 'expected exception'): + processor.process_bundle('instruction_id') + + self.fn_log_handler.close() + logs = [ + log for logs in self.test_logging_service.log_records_received + for log in logs.log_entries + ] + + actual_log = logs[0] + + self.assertEqual( + actual_log.severity, beam_fn_api_pb2.LogEntry.Severity.ERROR) + self.assertTrue('expected exception' in actual_log.message) + self.assertEqual(actual_log.transform_id, 'test_transform') + # Test cases. data = { From 27ada99648b07e6e32047df26193326d16e5cffc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Sep 2023 15:00:26 -0700 Subject: [PATCH 081/105] Bump github.com/aws/aws-sdk-go-v2/feature/s3/manager in /sdks (#28517) Bumps [github.com/aws/aws-sdk-go-v2/feature/s3/manager](https://github.com/aws/aws-sdk-go-v2) from 1.11.83 to 1.11.84. - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/feature/s3/manager/v1.11.83...feature/s3/manager/v1.11.84) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/feature/s3/manager dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdks/go.mod | 2 +- sdks/go.sum | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/sdks/go.mod b/sdks/go.mod index 30ac185b8165..169f882b1eaf 100644 --- a/sdks/go.mod +++ b/sdks/go.mod @@ -33,7 +33,7 @@ require ( github.com/aws/aws-sdk-go-v2 v1.21.0 github.com/aws/aws-sdk-go-v2/config v1.18.40 github.com/aws/aws-sdk-go-v2/credentials v1.13.38 - github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.83 + github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.84 github.com/aws/aws-sdk-go-v2/service/s3 v1.38.5 github.com/aws/smithy-go v1.14.2 github.com/docker/go-connections v0.4.0 diff --git a/sdks/go.sum b/sdks/go.sum index 15fa89e26c32..13d194fa0194 100644 --- a/sdks/go.sum +++ b/sdks/go.sum @@ -85,19 +85,17 @@ github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pf github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 h1:OPLEkmhXf6xFPiz0bLeDArZIDx1NNS4oJyG4nv3Gct0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13/go.mod h1:gpAbvyDGQFozTEmlTFO8XcQKHzubdq0LzRyJpG6MiXM= github.com/aws/aws-sdk-go-v2/config v1.5.0/go.mod h1:RWlPOAW3E3tbtNAqTwvSW54Of/yP3oiZXMI0xfUdjyA= -github.com/aws/aws-sdk-go-v2/config v1.18.39/go.mod h1:+NH/ZigdPckFpgB1TRcRuWCB/Kbbvkxc/iNAKTq5RhE= github.com/aws/aws-sdk-go-v2/config v1.18.40 h1:dbu1llI/nTIL+r6sYHMeVLl99DM8J8/o1I4EPurnhLg= github.com/aws/aws-sdk-go-v2/config v1.18.40/go.mod h1:JjrCZQwSPGCoZRQzKHyZNNueaKO+kFaEy2sR6mCzd90= github.com/aws/aws-sdk-go-v2/credentials v1.3.1/go.mod h1:r0n73xwsIVagq8RsxmZbGSRQFj9As3je72C2WzUIToc= -github.com/aws/aws-sdk-go-v2/credentials v1.13.37/go.mod h1:ACLrdkd4CLZyXOghZ8IYumQbcooAcp2jo/s2xsFH8IM= github.com/aws/aws-sdk-go-v2/credentials v1.13.38 h1:gDAuCdVlA4lmmgQhvpZlscwicloCqH44vkxLklGkQLA= github.com/aws/aws-sdk-go-v2/credentials v1.13.38/go.mod h1:sD4G/Ybgp6s89mWIES3Xn97CsRLpxvz9uVSdv0UxY8I= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.3.0/go.mod h1:2LAuqPx1I6jNfaGDucWfA2zqQCYCOMCDHiCOciALyNw= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 h1:uDZJF1hu0EVT/4bogChk8DyjSF6fof6uL/0Y26Ma7Fg= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11/go.mod h1:TEPP4tENqBGO99KwVpV9MlOX4NSrSLP8u3KRy2CDwA8= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.3.2/go.mod h1:qaqQiHSrOUVOfKe6fhgQ6UzhxjwqVW8aHNegd6Ws4w4= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.83 h1:wcluDLIQ0uYaxv0fCWQRimbXkPdTgWHUD21j1CzXEwc= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.83/go.mod h1:nGCBuon134gW67yAtxHKV73x+tAcY/xG4ZPNPDB1h/I= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.84 h1:LENrVcqnWTyI8fbIUCvxAMe+fXbREIaXzcR8WPwco1U= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.84/go.mod h1:LHxCiYAStsgps4srke7HujyADd504MSkNXjLpOtICTc= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 h1:22dGT7PneFMx4+b3pz7lMTRyN8ZKH7M2cW4GP9yUS2g= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 h1:SijA0mgjV8E+8G45ltVHs0fvKpTj8xmZJ3VwhGKtUSI= @@ -122,14 +120,11 @@ github.com/aws/aws-sdk-go-v2/service/s3 v1.11.1/go.mod h1:XLAGFrEjbvMCLvAtWLLP32 github.com/aws/aws-sdk-go-v2/service/s3 v1.38.5 h1:A42xdtStObqy7NGvzZKpnyNXvoOmm+FENobZ0/ssHWk= github.com/aws/aws-sdk-go-v2/service/s3 v1.38.5/go.mod h1:rDGMZA7f4pbmTtPOk5v5UM2lmX6UAbRnMDJeDvnH7AM= github.com/aws/aws-sdk-go-v2/service/sso v1.3.1/go.mod h1:J3A3RGUvuCZjvSuZEcOpHDnzZP/sKbhDWV2T1EOzFIM= -github.com/aws/aws-sdk-go-v2/service/sso v1.13.6/go.mod h1:fIAwKQKBFu90pBxx07BFOMJLpRUGu8VOzLJakeY+0K4= github.com/aws/aws-sdk-go-v2/service/sso v1.14.0 h1:AR/hlTsCyk1CwlyKnPFvIMvnONydRjDDRT9OGb0i+/g= github.com/aws/aws-sdk-go-v2/service/sso v1.14.0/go.mod h1:fIAwKQKBFu90pBxx07BFOMJLpRUGu8VOzLJakeY+0K4= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6/go.mod h1:yygr8ACQRY2PrEcy3xsUI357stq2AxnFM6DIsR9lij4= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.16.0 h1:vbgiXuhtn49+erlPrgIvQ+J32rg1HseaPf8lEpKbkxQ= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.16.0/go.mod h1:yygr8ACQRY2PrEcy3xsUI357stq2AxnFM6DIsR9lij4= github.com/aws/aws-sdk-go-v2/service/sts v1.6.0/go.mod h1:q7o0j7d7HrJk/vr9uUt3BVRASvcU7gYZB9PUgPiByXg= -github.com/aws/aws-sdk-go-v2/service/sts v1.21.5/go.mod h1:VC7JDqsqiwXukYEDjoHh9U0fOJtNWh04FPQz4ct4GGU= github.com/aws/aws-sdk-go-v2/service/sts v1.22.0 h1:s4bioTgjSFRwOoyEFzAVCmFmoowBgjTR8gkrF/sQ4wk= github.com/aws/aws-sdk-go-v2/service/sts v1.22.0/go.mod h1:VC7JDqsqiwXukYEDjoHh9U0fOJtNWh04FPQz4ct4GGU= github.com/aws/smithy-go v1.6.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= From a1ec0552a340aa99a6d0aadc77f49caf8acab110 Mon Sep 17 00:00:00 2001 From: Jack McCluskey <34928439+jrmccluskey@users.noreply.github.com> Date: Tue, 19 Sep 2023 18:18:12 -0400 Subject: [PATCH 082/105] Add extra marks to VertexAI IT tests (#28534) --- .../apache_beam/ml/inference/vertex_ai_inference_it_test.py | 1 + sdks/python/test-suites/dataflow/common.gradle | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sdks/python/apache_beam/ml/inference/vertex_ai_inference_it_test.py b/sdks/python/apache_beam/ml/inference/vertex_ai_inference_it_test.py index 4ef42fb10a70..8ba315ba00ca 100644 --- a/sdks/python/apache_beam/ml/inference/vertex_ai_inference_it_test.py +++ b/sdks/python/apache_beam/ml/inference/vertex_ai_inference_it_test.py @@ -26,6 +26,7 @@ from apache_beam.io.filesystems import FileSystems from apache_beam.testing.test_pipeline import TestPipeline +# pylint: disable=ungrouped-imports try: from apache_beam.examples.inference import vertex_ai_image_classification from apache_beam.examples.inference import vertex_ai_llm_text_classification diff --git a/sdks/python/test-suites/dataflow/common.gradle b/sdks/python/test-suites/dataflow/common.gradle index eee6e9d21887..8b8a56808996 100644 --- a/sdks/python/test-suites/dataflow/common.gradle +++ b/sdks/python/test-suites/dataflow/common.gradle @@ -418,7 +418,8 @@ task vertexAIInferenceTest { "test_opts": testOpts, "suite": "VertexAITests-df-py${pythonVersionSuffix}", "collect": "uses_vertex_ai and it_postcommit" , - "runner": "TestDataflowRunner" + "runner": "TestDataflowRunner", + "requirements_file": "$requirementsFile" ] def cmdArgs = mapToArgString(argMap) exec { From 845dfc51e070b16ee2d8831ba268547ff7bffd0e Mon Sep 17 00:00:00 2001 From: David Cavazos Date: Tue, 19 Sep 2023 16:43:02 -0700 Subject: [PATCH 083/105] Create py.typed for use with mypy (#27942) Co-authored-by: Jack McCluskey <34928439+jrmccluskey@users.noreply.github.com> --- CHANGES.md | 1 + sdks/python/apache_beam/py.typed | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 sdks/python/apache_beam/py.typed diff --git a/CHANGES.md b/CHANGES.md index cdf93909cb6e..bbe9d539531b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -68,6 +68,7 @@ * In Python, [RunInference](https://beam.apache.org/documentation/sdks/python-machine-learning/#why-use-the-runinference-api) now supports loading many models in the same transform using a [KeyedModelHandler](https://beam.apache.org/documentation/sdks/python-machine-learning/#use-a-keyed-modelhandler) ([#27628](https://github.com/apache/beam/issues/27628)). * In Python, the [VertexAIModelHandlerJSON](https://beam.apache.org/releases/pydoc/current/apache_beam.ml.inference.vertex_ai_inference.html#apache_beam.ml.inference.vertex_ai_inference.VertexAIModelHandlerJSON) now supports passing in inference_args. These will be passed through to the Vertex endpoint as parameters. +* Added support to run `mypy` on user pipelines ([#27906](https://github.com/apache/beam/issues/27906)) ## Breaking Changes diff --git a/sdks/python/apache_beam/py.typed b/sdks/python/apache_beam/py.typed new file mode 100644 index 000000000000..1aea47b9e7dd --- /dev/null +++ b/sdks/python/apache_beam/py.typed @@ -0,0 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Marker file for PEP 561. +# The apache-beam package uses inline types. From b39089870e72c5b901fbd9dbd75f72a55927fff4 Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Tue, 19 Sep 2023 17:04:25 -0700 Subject: [PATCH 084/105] Remove unused ToRow, GroupByKey, mark WithSchema as experimental. (#28504) --- sdks/python/apache_beam/yaml/yaml_provider.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/sdks/python/apache_beam/yaml/yaml_provider.py b/sdks/python/apache_beam/yaml/yaml_provider.py index 382dcfd97dca..d01852a69c39 100644 --- a/sdks/python/apache_beam/yaml/yaml_provider.py +++ b/sdks/python/apache_beam/yaml/yaml_provider.py @@ -549,15 +549,9 @@ def _parse_window_spec(spec): 'PyFilter': lambda keep: beam.Filter( python_callable.PythonCallableWithSource(keep)), 'PyTransform': fully_qualified_named_transform, - 'PyToRow': lambda fields: beam.Select( - **{ - name: python_callable.PythonCallableWithSource(fn) - for (name, fn) in fields.items() - }), - 'WithSchema': with_schema, + 'WithSchemaExperimental': with_schema, 'Flatten': Flatten, 'WindowInto': WindowInto, - 'GroupByKey': beam.GroupByKey, }, no_input_transforms=('Create', )) From 4184f5ea521941fcba6b90769b2d7f35b7262471 Mon Sep 17 00:00:00 2001 From: Ritesh Ghorse Date: Tue, 19 Sep 2023 22:40:15 -0400 Subject: [PATCH 085/105] Update HuggingFace api doc and add text2audio pipeline task (#28474) * update api doc and add text2audio pipeline task * update doc * update doc * fix indent * correct example snippet --- .../ml/inference/huggingface_inference.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/sdks/python/apache_beam/ml/inference/huggingface_inference.py b/sdks/python/apache_beam/ml/inference/huggingface_inference.py index aee613363781..3ec063808ae3 100644 --- a/sdks/python/apache_beam/ml/inference/huggingface_inference.py +++ b/sdks/python/apache_beam/ml/inference/huggingface_inference.py @@ -98,6 +98,7 @@ class PipelineTask(str, Enum): TextClassification = 'text-classification' TextGeneration = 'text-generation' Text2TextGeneration = 'text2text-generation' + TextToAudio = 'text-to-audio' TokenClassification = 'token-classification' Translation = 'translation' VideoClassification = 'video-classification' @@ -570,7 +571,7 @@ class HuggingFacePipelineModelHandler(ModelHandler[str, def __init__( self, task: Union[str, PipelineTask] = "", - model=None, + model: str = "", *, inference_fn: PipelineInferenceFn = _default_pipeline_inference_fn, load_pipeline_args: Optional[Dict[str, Any]] = None, @@ -593,9 +594,18 @@ def __init__( Args: task (str or enum.Enum): task supported by HuggingFace Pipelines. Accepts a string task or an enum.Enum from PipelineTask. - model : path to pretrained model on Hugging Face Models Hub to use custom - model for the chosen task. If the model already defines the task then - no need to specify the task parameter. + model (str): path to the pretrained *model-id* on Hugging Face Models Hub + to use custom model for the chosen task. If the `model` already defines + the task then no need to specify the `task` parameter. + Use the *model-id* string instead of an actual model here. + Model-specific kwargs for `from_pretrained(..., **model_kwargs)` can be + specified with `model_kwargs` using `load_pipeline_args`. + + Example Usage:: + model_handler = HuggingFacePipelineModelHandler( + task="text-generation", model="meta-llama/Llama-2-7b-hf", + load_pipeline_args={'model_kwargs':{'quantization_map':config}}) + inference_fn: the inference function to use during RunInference. Default is _default_pipeline_inference_fn. load_pipeline_args (Dict[str, Any]): keyword arguments to provide load From 79d0a8d11095522a036a6b3007f5fed4f6f46b3b Mon Sep 17 00:00:00 2001 From: Robert Burke Date: Tue, 19 Sep 2023 21:36:23 -0700 Subject: [PATCH 086/105] tooltips, footer, humanize (#28538) Co-authored-by: lostluck <13907733+lostluck@users.noreply.github.com> --- .../prism/internal/web/assets/style.css | 58 ++++++++++++++++++- .../beam/runners/prism/internal/web/debugz.go | 25 +++++--- .../runners/prism/internal/web/debugz.html | 10 ++-- .../runners/prism/internal/web/index.html | 52 +++++++++-------- 4 files changed, 108 insertions(+), 37 deletions(-) diff --git a/sdks/go/pkg/beam/runners/prism/internal/web/assets/style.css b/sdks/go/pkg/beam/runners/prism/internal/web/assets/style.css index 74f4a6958d29..d252dc020e63 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/web/assets/style.css +++ b/sdks/go/pkg/beam/runners/prism/internal/web/assets/style.css @@ -101,10 +101,19 @@ footer { color: var(--beam-white); } +#page-container { + position: relative; + min-height: 100vh; +} + +#content-wrap { + padding-bottom: 2.5rem; /* Footer height */ +} + .container { width: 100%; margin: 0 auto; - padding: 80px 20px 40px; + padding: 40px 20px 0px; } .child { @@ -132,6 +141,53 @@ footer { padding: 12px 15px; } +/* Tooltip container */ +.tooltip { + display: inline-block; + border-bottom: 1px dotted var(--beam-black); +} + +/* Tooltip text */ +.tooltip .tooltiptext { + visibility: hidden; + width: max-content; + max-width: 400px; + background-color: var(--dark-grey); + color: var(--beam-white); + text-align: left; + padding: 5px 10px; + border-radius: 6px; + + /* Position the tooltip text */ + position: absolute; + z-index: 1; + bottom: 125%; + left: 50%; + margin-left: -60px; + + /* Fade in tooltip */ + opacity: 0; + transition: opacity 0.3s; +} + +/* Tooltip arrow */ +.tooltip .tooltiptext::after { + content: ""; + position: absolute; + top: 100%; + left: 18%; + margin-left: -5px; + border-width: 5px; + border-style: solid; + border-color: var(--dark-grey) transparent transparent transparent; +} + +/* Show the tooltip text when you mouse over the tooltip container */ +.tooltip:hover .tooltiptext { + visibility: visible; + opacity: 1; +} + @media screen and (max-width: 550px) { header { flex-direction: column; diff --git a/sdks/go/pkg/beam/runners/prism/internal/web/debugz.go b/sdks/go/pkg/beam/runners/prism/internal/web/debugz.go index b34547e92752..015a9103134a 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/web/debugz.go +++ b/sdks/go/pkg/beam/runners/prism/internal/web/debugz.go @@ -21,6 +21,9 @@ import ( "runtime/metrics" "runtime/pprof" "strings" + "time" + + "github.com/dustin/go-humanize" ) type debugzData struct { @@ -54,16 +57,24 @@ func dumpMetrics() debugzData { name, value := sample.Name, sample.Value m := goRuntimeMetric{ - Name: name, + Name: strings.TrimSpace(name), Description: descs[i].Description, } // Handle each sample. switch value.Kind() { case metrics.KindUint64: - m.Value = fmt.Sprintf("%d", value.Uint64()) + if strings.HasSuffix(name, "bytes") { + m.Value = humanize.Bytes(value.Uint64()) + } else { + m.Value = humanize.FormatInteger("", int(value.Uint64())) + } case metrics.KindFloat64: - m.Value = fmt.Sprintf("%f", value.Float64()) + if strings.HasSuffix(name, "seconds") { + m.Value = time.Duration(float64(time.Second) * value.Float64()).String() + } else { + m.Value = humanize.FormatFloat("", value.Float64()) + } case metrics.KindFloat64Histogram: m.Value = fmt.Sprintf("%f", medianBucket(value.Float64Histogram())) // The histogram may be quite large, so let's just pull out @@ -88,16 +99,16 @@ func dumpMetrics() debugzData { data.Metrics = append(data.Metrics, goRuntimeMetric{ Name: "BUILD INFO", - Value: "n/a", - Description: b.String(), + Value: b.String(), + Description: "result from runtime/debug.ReadBuildInfo()", }) b.Reset() goroutineDump(&b) data.Metrics = append(data.Metrics, goRuntimeMetric{ Name: "GOROUTINES", - Value: "n/a", - Description: b.String(), + Value: b.String(), + Description: "consolidated active goroutines", }) b.Reset() diff --git a/sdks/go/pkg/beam/runners/prism/internal/web/debugz.html b/sdks/go/pkg/beam/runners/prism/internal/web/debugz.html index ebf37f129ae3..175f44da7447 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/web/debugz.html +++ b/sdks/go/pkg/beam/runners/prism/internal/web/debugz.html @@ -30,14 +30,16 @@ - - + {{ range .Metrics }} - + - {{ else }} diff --git a/sdks/go/pkg/beam/runners/prism/internal/web/index.html b/sdks/go/pkg/beam/runners/prism/internal/web/index.html index fe9bb056e51c..1aa0ed719d87 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/web/index.html +++ b/sdks/go/pkg/beam/runners/prism/internal/web/index.html @@ -22,31 +22,33 @@ -
-
- -
-
- {{ if .Error}}{{.Error}}{{end}} -
IDNameStateValue
{{ .Name }} +
{{ .Name }} + {{ .Description }} +
+
{{ .Value }}{{ .Description }}
- - - - - - {{ range .Jobs }} - - - - - - {{ else }} - - - - {{ end }} -
IDNameState
{{ .JobId }}{{ .JobName }}{{ .State }}
No jobs have been run.
- +

+
+
+ +
+
+ {{ if .Error}}{{.Error}}{{end}} + + + + + + + {{ range .Jobs }} + + + + + + {{ else }} + + + + {{ end }} +
IDNameState
{{ .JobId }}{{ .JobName }}{{ .State }}
No jobs have been run.
+
+
From 635f9ef5a0e2f1821df3f442bc348d32557acd4b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Sep 2023 09:26:47 -0400 Subject: [PATCH 087/105] Bump github.com/testcontainers/testcontainers-go in /sdks (#28550) Bumps [github.com/testcontainers/testcontainers-go](https://github.com/testcontainers/testcontainers-go) from 0.23.0 to 0.24.0. - [Release notes](https://github.com/testcontainers/testcontainers-go/releases) - [Commits](https://github.com/testcontainers/testcontainers-go/compare/v0.23.0...v0.24.0) --- updated-dependencies: - dependency-name: github.com/testcontainers/testcontainers-go dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdks/go.mod | 19 +++++++++++++++---- sdks/go.sum | 41 ++++++++++++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/sdks/go.mod b/sdks/go.mod index 169f882b1eaf..005a711a3252 100644 --- a/sdks/go.mod +++ b/sdks/go.mod @@ -47,7 +47,7 @@ require ( github.com/linkedin/goavro/v2 v2.12.0 github.com/proullon/ramsql v0.1.2 github.com/spf13/cobra v1.7.0 - github.com/testcontainers/testcontainers-go v0.23.0 + github.com/testcontainers/testcontainers-go v0.24.0 github.com/tetratelabs/wazero v1.5.0 github.com/xitongsys/parquet-go v1.6.2 github.com/xitongsys/parquet-go-source v0.0.0-20220315005136-aec0fe3e777c @@ -71,7 +71,18 @@ require ( golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 ) -require dario.cat/mergo v1.0.0 // indirect +require ( + dario.cat/mergo v1.0.0 // indirect + github.com/Microsoft/hcsshim v0.11.0 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/shirou/gopsutil/v3 v3.23.7 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/tklauser/go-sysconf v0.3.11 // indirect + github.com/tklauser/numcpus v0.6.0 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect +) require ( cloud.google.com/go v0.110.7 // indirect @@ -104,10 +115,10 @@ require ( github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe // indirect github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 // indirect - github.com/containerd/containerd v1.7.3 // indirect + github.com/containerd/containerd v1.7.6 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/docker/distribution v2.8.2+incompatible // indirect - github.com/docker/docker v24.0.5+incompatible // but required to resolve issue docker has with go1.20 + github.com/docker/docker v24.0.6+incompatible // but required to resolve issue docker has with go1.20 github.com/docker/go-units v0.5.0 // indirect github.com/envoyproxy/go-control-plane v0.11.1 // indirect github.com/envoyproxy/protoc-gen-validate v1.0.2 // indirect diff --git a/sdks/go.sum b/sdks/go.sum index 13d194fa0194..d498a8bc7ec2 100644 --- a/sdks/go.sum +++ b/sdks/go.sum @@ -48,7 +48,7 @@ cloud.google.com/go/storage v1.33.0/go.mod h1:Hhh/dogNRGca7IWv1RC2YqEn0c0G77ctA/ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= @@ -64,7 +64,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.10.0-rc.8 h1:YSZVvlIIDD1UxQpJp0h+dnpLUw+TrY0cx8obKsp3bek= +github.com/Microsoft/hcsshim v0.11.0 h1:7EFNIY4igHEXUdj1zXgAyU3fLc7QfOKHbkldRVTBdiM= +github.com/Microsoft/hcsshim v0.11.0/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCztGKDurW1Z8b8VGMM= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/apache/arrow/go/arrow v0.0.0-20200730104253-651201b0f516 h1:byKBBF2CKWBjjA4J1ZL2JXttJULvWSl50LegTyRZ728= @@ -151,8 +152,8 @@ github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+g github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/colinmarc/hdfs/v2 v2.1.1/go.mod h1:M3x+k8UKKmxtFu++uAZ0OtDU8jR3jnaZIAc6yK4Ue0c= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/containerd v1.7.3 h1:cKwYKkP1eTj54bP3wCdXXBymmKRQMrWjkLSWZZJDa8o= -github.com/containerd/containerd v1.7.3/go.mod h1:32FOM4/O0RkNg7AjQj3hDzN9cUGtu+HMvaKUNiqCZB8= +github.com/containerd/containerd v1.7.6 h1:oNAVsnhPoy4BTPQivLgTzI9Oleml9l/+eYIDYXRCYo8= +github.com/containerd/containerd v1.7.6/go.mod h1:SY6lrkkuJT40BVNO37tlYTSnKJnP5AXBc0fhx0q+TJ4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= @@ -160,15 +161,14 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY= -github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE= +github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -197,6 +197,8 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gorp/gorp v2.2.0+incompatible h1:xAUh4QgEeqPPhK3vxZN+bzrim1z5Av6q837gtjUlshc= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= @@ -332,6 +334,8 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/linkedin/goavro/v2 v2.12.0 h1:rIQQSj8jdAUlKQh6DttK8wCRv4t4QO09g1C4aBWXslg= github.com/linkedin/goavro/v2 v2.12.0/go.mod h1:KXx+erlq+RPlGSPmLF7xGo6SAbh8sCQ53x064+ioxhk= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= @@ -375,6 +379,8 @@ github.com/pkg/xattr v0.4.9 h1:5883YPCtkSd8LFbs13nXplj9g9tlrwoJRjgpgMu1/fE= github.com/pkg/xattr v0.4.9/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/proullon/ramsql v0.1.2 h1:PTtsy2iml/CW3Lsopyr86dlIs7JyYEmfLrfYvQVXD2U= github.com/proullon/ramsql v0.1.2/go.mod h1:CFGqeQHQpdRfWqYmWD3yXqPTEaHkF4zgXy1C6qDWc9E= @@ -390,6 +396,12 @@ github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5P github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63 h1:J6qvD6rbmOil46orKqJaRPG+zTpoGlBTUdyv8ki63L0= github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63/go.mod h1:n+VKSARF5y/tS9XFSP7vWDfS+GUC5vs/YT7M5XDTUEM= +github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4= +github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= @@ -414,11 +426,16 @@ github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/testcontainers/testcontainers-go v0.23.0 h1:ERYTSikX01QczBLPZpqsETTBO7lInqEP349phDOVJVs= -github.com/testcontainers/testcontainers-go v0.23.0/go.mod h1:3gzuZfb7T9qfcH2pHpV4RLlWrPjeWNQah6XlYQ32c4I= +github.com/testcontainers/testcontainers-go v0.24.0 h1:eqkq6nNIPVrqpXNyn/s5jDBqPGuWtND2hOMEBrUULIw= +github.com/testcontainers/testcontainers-go v0.24.0/go.mod h1:MGBiAkCm86yXQoCiipmQCqZLVdk1uFqtMqaU1Or0MRk= github.com/tetratelabs/wazero v1.5.0 h1:Yz3fZHivfDiZFUXnWMPUoiW7s8tC1sjdBtlJn08qYa0= github.com/tetratelabs/wazero v1.5.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A= +github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= +github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= +github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= +github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= @@ -440,6 +457,8 @@ github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7Jul github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= @@ -551,6 +570,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -565,6 +585,7 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -576,7 +597,9 @@ golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= From a70a5845230a5d3ebdbe4ed1a23de92b363b8a97 Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Wed, 20 Sep 2023 06:39:51 -0700 Subject: [PATCH 088/105] Add schema-aware text file reading and writing. (#28486) --- sdks/python/apache_beam/yaml/standard_io.yaml | 2 ++ sdks/python/apache_beam/yaml/yaml_io.py | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/sdks/python/apache_beam/yaml/standard_io.yaml b/sdks/python/apache_beam/yaml/standard_io.yaml index e60f0026fd25..1738110539ce 100644 --- a/sdks/python/apache_beam/yaml/standard_io.yaml +++ b/sdks/python/apache_beam/yaml/standard_io.yaml @@ -51,6 +51,8 @@ 'ReadFromBigQuery': 'apache_beam.yaml.yaml_io.read_from_bigquery' # Disable until https://github.com/apache/beam/issues/28162 is resolved. # 'WriteToBigQuery': 'apache_beam.yaml.yaml_io.write_to_bigquery' + 'ReadFromText': 'apache_beam.yaml.yaml_io.read_from_text' + 'WriteToText': 'apache_beam.yaml.yaml_io.write_to_text' # Declared as a renaming transform to avoid exposing all # (implementation-specific) pandas arguments and aligning with possible Java diff --git a/sdks/python/apache_beam/yaml/yaml_io.py b/sdks/python/apache_beam/yaml/yaml_io.py index 2a9d1be62c6d..297c07e9abb5 100644 --- a/sdks/python/apache_beam/yaml/yaml_io.py +++ b/sdks/python/apache_beam/yaml/yaml_io.py @@ -28,12 +28,38 @@ import yaml import apache_beam as beam +import apache_beam.io as beam_io from apache_beam.io import ReadFromBigQuery from apache_beam.io import WriteToBigQuery from apache_beam.io.gcp.bigquery import BigQueryDisposition +from apache_beam.typehints.schemas import named_fields_from_element_type from apache_beam.yaml import yaml_provider +def read_from_text(path: str): + # TODO(yaml): Consider passing the filename and offset, possibly even + # by default. + return beam_io.ReadFromText(path) | beam.Map(lambda s: beam.Row(line=s)) + + +@beam.ptransform_fn +def write_to_text(pcoll, path: str): + try: + field_names = [ + name for name, _ in named_fields_from_element_type(pcoll.element_type) + ] + except Exception as exn: + raise ValueError( + "WriteToText requires an input schema with exactly one field.") from exn + if len(field_names) != 1: + raise ValueError( + "WriteToText requires an input schema with exactly one field, got %s" % + field_names) + sole_field_name, = field_names + return pcoll | beam.Map( + lambda x: str(getattr(x, sole_field_name))) | beam.io.WriteToText(path) + + def read_from_bigquery( query=None, table=None, row_restriction=None, fields=None): if query is None: From 275b177fa410f0f54b39e008033fb42a83e22f14 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Sep 2023 10:23:43 -0400 Subject: [PATCH 089/105] Bump google.golang.org/api from 0.141.0 to 0.142.0 in /sdks (#28549) Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.141.0 to 0.142.0. - [Release notes](https://github.com/googleapis/google-api-go-client/releases) - [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md) - [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.141.0...v0.142.0) --- updated-dependencies: - dependency-name: google.golang.org/api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdks/go.mod | 4 ++-- sdks/go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sdks/go.mod b/sdks/go.mod index 005a711a3252..53596c5d207d 100644 --- a/sdks/go.mod +++ b/sdks/go.mod @@ -57,7 +57,7 @@ require ( golang.org/x/sync v0.3.0 golang.org/x/sys v0.12.0 golang.org/x/text v0.13.0 - google.golang.org/api v0.141.0 + google.golang.org/api v0.142.0 google.golang.org/genproto v0.0.0-20230821184602-ccc8af3d0e93 google.golang.org/grpc v1.58.1 google.golang.org/protobuf v1.31.0 @@ -171,5 +171,5 @@ require ( golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230911183012-2d3300fd4832 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230913181813-007df8e322eb // indirect ) diff --git a/sdks/go.sum b/sdks/go.sum index d498a8bc7ec2..502fdf1e8892 100644 --- a/sdks/go.sum +++ b/sdks/go.sum @@ -670,8 +670,8 @@ google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.141.0 h1:Df6vfMgDoIM6ss0m7H4MPwFwY87WNXHfBIda/Bmfl4E= -google.golang.org/api v0.141.0/go.mod h1:iZqLkdPlXKyG0b90eu6KxVSE4D/ccRF2e/doKD2CnQQ= +google.golang.org/api v0.142.0 h1:mf+7EJ94fi5ZcnpPy+m0Yv2dkz8bKm+UL0snTCuwXlY= +google.golang.org/api v0.142.0/go.mod h1:zJAN5o6HRqR7O+9qJUFOWrZkYE66RH+efPBdTLA4xBA= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -701,8 +701,8 @@ google.golang.org/genproto v0.0.0-20230821184602-ccc8af3d0e93 h1:zv6ieVm8jNcN33A google.golang.org/genproto v0.0.0-20230821184602-ccc8af3d0e93/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 h1:nIgk/EEq3/YlnmVVXVnm14rC2oxgs1o0ong4sD/rd44= google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5/go.mod h1:5DZzOUPCLYL3mNkQ0ms0F3EuUNZ7py1Bqeq6sxzI7/Q= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230911183012-2d3300fd4832 h1:o4LtQxebKIJ4vkzyhtD2rfUNZ20Zf0ik5YVP5E7G7VE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230911183012-2d3300fd4832/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230913181813-007df8e322eb h1:Isk1sSH7bovx8Rti2wZK0UZF6oraBDK74uoyLEEVFN0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230913181813-007df8e322eb/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= From 93c3224be6d93c5960d50c287a27d696d5e9d66a Mon Sep 17 00:00:00 2001 From: Andrey Devyatkin Date: Wed, 20 Sep 2023 16:49:43 +0200 Subject: [PATCH 090/105] Enable remote gradle cache and build scan for GitHub Actions workflows (#28539) * publish gradle build scans publish gradle build scans publish gradle build scans * added missing gradle configs publish gradle build scans --- .github/actions/setup-action/action.yml | 2 + .../action.yml | 53 +++++++++---------- .../beam_PostCommit_Go_Dataflow_ARM.yml | 16 ++---- .../beam_PostCommit_Java_Avro_Versions.yml | 5 ++ ..._PostCommit_Java_Examples_Dataflow_ARM.yml | 15 +----- .../beam_PostCommit_Java_Examples_Flink.yml | 16 ++---- ...m_PostCommit_Java_IO_Performance_Tests.yml | 11 ++-- ..._PostCommit_Java_ValidatesRunner_Flink.yml | 16 ++---- ...am_PostCommit_Python_Examples_Dataflow.yml | 14 ++--- ...beam_PostCommit_Python_Examples_Direct.yml | 14 ++--- .../beam_PostCommit_Python_Examples_Flink.yml | 14 ++--- .../beam_PostCommit_Python_Examples_Spark.yml | 14 ++--- .../beam_PostCommit_Python_MongoDBIO_IT.yml | 14 ++--- ...mit_Python_ValidatesContainer_Dataflow.yml | 14 ++--- ...on_ValidatesContainer_Dataflow_With_RC.yml | 16 ++---- ...Commit_Python_ValidatesRunner_Dataflow.yml | 14 ++--- ...ostCommit_Python_ValidatesRunner_Flink.yml | 14 ++--- ...ostCommit_Python_ValidatesRunner_Samza.yml | 14 ++--- ...ostCommit_Python_ValidatesRunner_Spark.yml | 14 ++--- .../beam_PreCommit_CommunityMetrics.yml | 11 ++-- .github/workflows/beam_PreCommit_Go.yml | 16 ++---- .../workflows/beam_PreCommit_GoPortable.yml | 8 +-- .github/workflows/beam_PreCommit_GoPrism.yml | 8 +-- .../workflows/beam_PreCommit_ItFramework.yml | 12 ++--- .github/workflows/beam_PreCommit_Java.yml | 5 ++ ...beam_PreCommit_Java_Debezium_IO_Direct.yml | 5 ++ ...PreCommit_Java_ElasticSearch_IO_Direct.yml | 5 ++ .../beam_PreCommit_Java_Examples_Dataflow.yml | 12 ++--- ...reCommit_Java_Examples_Dataflow_Java17.yml | 5 ++ ...t_Java_File-schema-transform_IO_Direct.yml | 5 ++ .../beam_PreCommit_Java_Flink_Versions.yml | 16 ++---- .../beam_PreCommit_Java_PVR_Flink_Batch.yml | 5 ++ .github/workflows/beam_PreCommit_Python.yml | 14 ++--- .../workflows/beam_PreCommit_PythonDocker.yml | 19 ++----- .../workflows/beam_PreCommit_PythonDocs.yml | 16 ++---- .../beam_PreCommit_PythonFormatter.yml | 16 ++---- .../workflows/beam_PreCommit_PythonLint.yml | 21 ++------ .../beam_PreCommit_Python_Coverage.yml | 16 ++---- .../beam_PreCommit_Python_Dataframes.yml | 14 ++--- .../beam_PreCommit_Python_Examples.yml | 14 ++--- .../beam_PreCommit_Python_Integration.yml | 14 ++--- .../beam_PreCommit_Python_PVR_Flink.yml | 5 ++ .../beam_PreCommit_Python_Runners.yml | 14 ++--- .../beam_PreCommit_Python_Transforms.yml | 14 ++--- .github/workflows/beam_PreCommit_RAT.yml | 11 ++-- .github/workflows/beam_PreCommit_Spotless.yml | 5 ++ .../workflows/beam_PreCommit_Typescript.yml | 12 ++--- .github/workflows/beam_PreCommit_Website.yml | 11 ++-- .../beam_PreCommit_Website_Stage_GCS.yml | 12 ++--- .../workflows/beam_PreCommit_Whitespace.yml | 16 ++---- ...Python_ValidatesContainer_Dataflow_ARM.yml | 8 +-- .../beam_Release_NightlySnapshot.yml | 11 ++-- .../beam_Release_Python_NightlySnapshot.yml | 16 ++---- .github/workflows/java_tests.yml | 27 +++++----- .../playground_backend_precommit.yml | 15 ++---- .../tour_of_beam_backend_integration.yml | 9 ++-- .../workflows/update_python_dependencies.yml | 8 ++- 57 files changed, 240 insertions(+), 501 deletions(-) rename .github/actions/{setup-self-hosted-action => setup-environment-action}/action.yml (50%) diff --git a/.github/actions/setup-action/action.yml b/.github/actions/setup-action/action.yml index cb24a065f98c..da69dd9a97dd 100644 --- a/.github/actions/setup-action/action.yml +++ b/.github/actions/setup-action/action.yml @@ -70,3 +70,5 @@ runs: shell: bash run: | echo KUBELET_GCLOUD_CONFIG_PATH=/var/lib/kubelet/pods/$POD_UID/volumes/kubernetes.io~empty-dir/gcloud >> $GITHUB_ENV + - name: Setup environment + uses: ./.github/actions/setup-environment-action diff --git a/.github/actions/setup-self-hosted-action/action.yml b/.github/actions/setup-environment-action/action.yml similarity index 50% rename from .github/actions/setup-self-hosted-action/action.yml rename to .github/actions/setup-environment-action/action.yml index ba3bf8d0d5d8..3452a16c132c 100644 --- a/.github/actions/setup-self-hosted-action/action.yml +++ b/.github/actions/setup-environment-action/action.yml @@ -15,47 +15,42 @@ # specific language governing permissions and limitations # under the License. -name: 'Setup environment for self-hosted runners' -description: 'Setup action to run jobs in a self-hosted runner' +name: 'Setup environment action' +description: 'Setup environment to run jobs' inputs: - requires-py-38: + python-version: required: false - description: 'Set as false if does not require py38 setup' - default: 'true' - requires-py-39: + description: 'Install Python version' + default: '' + java-version: required: false - description: 'Set as false if does not require py39 setup' - default: 'true' - requires-java-8: + description: 'Install Java version' + default: '' + go-version: required: false - description: 'Set as false if does not require java-8 setup' - default: 'true' - requires-go: - required: false - description: 'Set as false if does not require go setup' - default: 'true' + description: 'Install Go version' + default: '' runs: using: "composite" steps: - - name: Install python 3.8 - if: ${{ inputs.requires-py-38 == 'true' }} - uses: actions/setup-python@v4 - with: - python-version: "3.8" - - name: Install python 3.9 - if: ${{ inputs.requires-py-39 == 'true' }} + - name: Install Python + if: ${{ inputs.python-version != '' }} uses: actions/setup-python@v4 with: - python-version: "3.9" - - name: Set Java Version - if: ${{ inputs.requires-java-8 == 'true' }} + python-version: ${{ inputs.python-version }} + - name: Install Java + if: ${{ inputs.java-version != '' }} uses: actions/setup-java@v3 with: distribution: 'temurin' - java-version: 8 - - name: Set Go Version - if: ${{ inputs.requires-go == 'true' }} + java-version: ${{ inputs.java-version }} + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + with: + cache-read-only: false + - name: Install Go + if: ${{ inputs.go-version != '' }} uses: actions/setup-go@v3 with: - go-version: '1.21' # never set patch, to get latest patch releases. + go-version: ${{ inputs.go-version }} # never set patch, to get latest patch releases. diff --git a/.github/workflows/beam_PostCommit_Go_Dataflow_ARM.yml b/.github/workflows/beam_PostCommit_Go_Dataflow_ARM.yml index 64b935888cf0..8e5651e29279 100644 --- a/.github/workflows/beam_PostCommit_Go_Dataflow_ARM.yml +++ b/.github/workflows/beam_PostCommit_Go_Dataflow_ARM.yml @@ -73,19 +73,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Install Java - uses: actions/setup-java@v3.8.0 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - distribution: 'zulu' - java-version: '8' - - name: Install Go - uses: actions/setup-go@v4 - with: - go-version: '1.21' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + java-version: 8 + go-version: 1.21 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - name: Authenticate on GCP diff --git a/.github/workflows/beam_PostCommit_Java_Avro_Versions.yml b/.github/workflows/beam_PostCommit_Java_Avro_Versions.yml index 1d551dcebf22..cf3064a6a7de 100644 --- a/.github/workflows/beam_PostCommit_Java_Avro_Versions.yml +++ b/.github/workflows/beam_PostCommit_Java_Avro_Versions.yml @@ -45,6 +45,11 @@ permissions: security-events: read statuses: read +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + jobs: beam_PostCommit_Java_Avro_Versions: name: ${{matrix.job_name}} (${{matrix.job_phrase}}) diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml index ec771bd4cefd..5a42b9f95237 100644 --- a/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml +++ b/.github/workflows/beam_PostCommit_Java_Examples_Dataflow_ARM.yml @@ -85,21 +85,10 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} ${{matrix.java_version}} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{matrix.java_version}}) - - name: Setup self-hosted - uses: ./.github/actions/setup-self-hosted-action + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - requires-py-38: false - requires-py-39: false - requires-go: false - - name: Set up Java${{ matrix.java_version }} - uses: actions/setup-java@v3.8.0 - with: - distribution: 'temurin' java-version: ${{ matrix.java_version }} - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Authenticate on GCP diff --git a/.github/workflows/beam_PostCommit_Java_Examples_Flink.yml b/.github/workflows/beam_PostCommit_Java_Examples_Flink.yml index 71e761d32ae7..3aec68316f81 100644 --- a/.github/workflows/beam_PostCommit_Java_Examples_Flink.yml +++ b/.github/workflows/beam_PostCommit_Java_Examples_Flink.yml @@ -71,19 +71,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Install Java - uses: actions/setup-java@v3.8.0 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - distribution: 'zulu' - java-version: '8' - - name: Install Python - uses: actions/setup-python@v4 - with: - python-version: '3.8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + java-version: 8 + python-version: 3.8 - name: run examplesIntegrationTest script uses: ./.github/actions/gradle-command-self-hosted-action with: diff --git a/.github/workflows/beam_PostCommit_Java_IO_Performance_Tests.yml b/.github/workflows/beam_PostCommit_Java_IO_Performance_Tests.yml index eb1f43c51ed8..471782621fa7 100644 --- a/.github/workflows/beam_PostCommit_Java_IO_Performance_Tests.yml +++ b/.github/workflows/beam_PostCommit_Java_IO_Performance_Tests.yml @@ -80,15 +80,10 @@ jobs: with: ref: v2.50.0 #TODO(https://github.com/apache/beam/issues/28330) automate updating this repository: apache/beam - - name: Install Java - uses: actions/setup-java@v3.8.0 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + java-version: 8 - name: Authenticate on GCP uses: google-github-actions/setup-gcloud@v0 with: diff --git a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml index dbee3bc30782..20aac8a30608 100644 --- a/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml +++ b/.github/workflows/beam_PostCommit_Java_ValidatesRunner_Flink.yml @@ -69,19 +69,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Install Java - uses: actions/setup-java@v3.8.0 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - distribution: 'zulu' - java-version: '8' - - name: Install Python - uses: actions/setup-python@v4 - with: - python-version: '3.8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + java-version: 8 + python-version: 3.8 - name: run validatesRunner script uses: ./.github/actions/gradle-command-self-hosted-action with: diff --git a/.github/workflows/beam_PostCommit_Python_Examples_Dataflow.yml b/.github/workflows/beam_PostCommit_Python_Examples_Dataflow.yml index c7122523b79b..14cdecb356bd 100644 --- a/.github/workflows/beam_PostCommit_Python_Examples_Dataflow.yml +++ b/.github/workflows/beam_PostCommit_Python_Examples_Dataflow.yml @@ -69,19 +69,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: + java-version: 8 python-version: 3.11 - - name: Install Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - name: Run examplesPostCommit script uses: ./.github/actions/gradle-command-self-hosted-action with: diff --git a/.github/workflows/beam_PostCommit_Python_Examples_Direct.yml b/.github/workflows/beam_PostCommit_Python_Examples_Direct.yml index 719d764781f0..7c792d7a3c27 100644 --- a/.github/workflows/beam_PostCommit_Python_Examples_Direct.yml +++ b/.github/workflows/beam_PostCommit_Python_Examples_Direct.yml @@ -71,19 +71,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: + java-version: 8 python-version: ${{ matrix.python_version }} - - name: Install Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - name: Set PY_VER_CLEAN id: set_py_ver_clean run: | diff --git a/.github/workflows/beam_PostCommit_Python_Examples_Flink.yml b/.github/workflows/beam_PostCommit_Python_Examples_Flink.yml index be523408d72b..ccf03918f29d 100644 --- a/.github/workflows/beam_PostCommit_Python_Examples_Flink.yml +++ b/.github/workflows/beam_PostCommit_Python_Examples_Flink.yml @@ -71,19 +71,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: + java-version: 8 python-version: ${{ matrix.python_version }} - - name: Install Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - name: Set PY_VER_CLEAN id: set_py_ver_clean run: | diff --git a/.github/workflows/beam_PostCommit_Python_Examples_Spark.yml b/.github/workflows/beam_PostCommit_Python_Examples_Spark.yml index 01579d595007..073ed0aeda64 100644 --- a/.github/workflows/beam_PostCommit_Python_Examples_Spark.yml +++ b/.github/workflows/beam_PostCommit_Python_Examples_Spark.yml @@ -71,19 +71,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: + java-version: 8 python-version: ${{ matrix.python_version }} - - name: Install Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - name: Set PY_VER_CLEAN id: set_py_ver_clean run: | diff --git a/.github/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml b/.github/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml index 7778be56f58e..be8f0e10dc18 100644 --- a/.github/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml +++ b/.github/workflows/beam_PostCommit_Python_MongoDBIO_IT.yml @@ -69,19 +69,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: + java-version: 8 python-version: 3.11 - - name: Install Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - name: Run mongodbioIT script uses: ./.github/actions/gradle-command-self-hosted-action with: diff --git a/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml b/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml index 9ea80ea0576a..ca5753010d1c 100644 --- a/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml +++ b/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow.yml @@ -71,19 +71,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: + java-version: 8 python-version: ${{ matrix.python_version }} - - name: Install Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - name: Set PY_VER_CLEAN id: set_py_ver_clean run: | diff --git a/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml b/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml index c4354b072c52..ded9ff0a4bd5 100644 --- a/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml +++ b/.github/workflows/beam_PostCommit_Python_ValidatesContainer_Dataflow_With_RC.yml @@ -71,19 +71,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: + java-version: 8 python-version: ${{ matrix.python_version }} - - name: Install Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - name: Set PY_VER_CLEAN id: set_py_ver_clean run: | @@ -97,7 +89,7 @@ jobs: with: gradle-command: :sdks:python:test-suites:dataflow:py${{steps.set_py_ver_clean.outputs.py_ver_clean}}:validatesContainer arguments: | - -PtestRCDependencies=true \ + -PtestRCDependencies=true -PpythonVersion=${{ matrix.python_version }} \ - name: Archive code coverage results uses: actions/upload-artifact@v3 diff --git a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml index f91b17f456e4..4119ddd56020 100644 --- a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml +++ b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Dataflow.yml @@ -71,19 +71,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: + java-version: 8 python-version: ${{ matrix.python_version }} - - name: Install Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - name: Set PY_VER_CLEAN id: set_py_ver_clean run: | diff --git a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml index 8e8205f4a80c..608bba248b3b 100644 --- a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml +++ b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Flink.yml @@ -71,19 +71,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: + java-version: 8 python-version: ${{ matrix.python_version }} - - name: Install Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - name: Set PY_VER_CLEAN id: set_py_ver_clean run: | diff --git a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml index 8a2b42d59e5b..bd0bbe1d6ff1 100644 --- a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml +++ b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Samza.yml @@ -71,19 +71,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: + java-version: 8 python-version: ${{ matrix.python_version }} - - name: Install Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - name: Set PY_VER_CLEAN id: set_py_ver_clean run: | diff --git a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml index ba6336052fa4..6fda3a210aaf 100644 --- a/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml +++ b/.github/workflows/beam_PostCommit_Python_ValidatesRunner_Spark.yml @@ -71,19 +71,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: + java-version: 8 python-version: ${{ matrix.python_version }} - - name: Install Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - name: Set PY_VER_CLEAN id: set_py_ver_clean run: | diff --git a/.github/workflows/beam_PreCommit_CommunityMetrics.yml b/.github/workflows/beam_PreCommit_CommunityMetrics.yml index 7606cc7227e2..7a93873168e0 100644 --- a/.github/workflows/beam_PreCommit_CommunityMetrics.yml +++ b/.github/workflows/beam_PreCommit_CommunityMetrics.yml @@ -78,15 +78,10 @@ jobs: comment_phrase: ${{matrix.job_phrase}} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Install Java - uses: actions/setup-java@v3.8.0 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + java-version: 8 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Remove default github maven configuration diff --git a/.github/workflows/beam_PreCommit_Go.yml b/.github/workflows/beam_PreCommit_Go.yml index 4626552d748a..227f3c7648ab 100644 --- a/.github/workflows/beam_PreCommit_Go.yml +++ b/.github/workflows/beam_PreCommit_Go.yml @@ -78,19 +78,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Install Java - uses: actions/setup-java@v3.8.0 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - distribution: 'zulu' - java-version: '8' - - name: Install Go - uses: actions/setup-go@v4 - with: - go-version: '1.21' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + java-version: 8 + go-version: 1.21 - name: run goPreCommit script uses: ./.github/actions/gradle-command-self-hosted-action with: diff --git a/.github/workflows/beam_PreCommit_GoPortable.yml b/.github/workflows/beam_PreCommit_GoPortable.yml index 5d18d7e1bb19..8156df15133c 100644 --- a/.github/workflows/beam_PreCommit_GoPortable.yml +++ b/.github/workflows/beam_PreCommit_GoPortable.yml @@ -78,11 +78,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Setup self-hosted - uses: ./.github/actions/setup-self-hosted-action + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - requires-py-39: false - requires-go: false + python-version: 3.8 + java-version: 8 - name: Run goPortablePreCommit script uses: ./.github/actions/gradle-command-self-hosted-action with: diff --git a/.github/workflows/beam_PreCommit_GoPrism.yml b/.github/workflows/beam_PreCommit_GoPrism.yml index 073a5d7c4a62..1a669c157007 100644 --- a/.github/workflows/beam_PreCommit_GoPrism.yml +++ b/.github/workflows/beam_PreCommit_GoPrism.yml @@ -78,11 +78,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Setup self-hosted - uses: ./.github/actions/setup-self-hosted-action + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - requires-py-39: false - requires-go: false + python-version: 3.8 + java-version: 8 - name: Run goPrismPreCommit script uses: ./.github/actions/gradle-command-self-hosted-action with: diff --git a/.github/workflows/beam_PreCommit_ItFramework.yml b/.github/workflows/beam_PreCommit_ItFramework.yml index 3d976e4d8f95..e8ec1287be51 100644 --- a/.github/workflows/beam_PreCommit_ItFramework.yml +++ b/.github/workflows/beam_PreCommit_ItFramework.yml @@ -81,16 +81,10 @@ jobs: comment_phrase: ${{matrix.job_phrase}} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Setup self-hosted - uses: ./.github/actions/setup-self-hosted-action + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - requires-py-38: false - requires-py-39: false - requires-go: false - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + java-version: 8 - name: run ItFrameworkPrecommit script run: ./gradlew -p it build - name: Archive JUnit Test Results diff --git a/.github/workflows/beam_PreCommit_Java.yml b/.github/workflows/beam_PreCommit_Java.yml index 22910f6f99fd..ab5c17c55f4c 100644 --- a/.github/workflows/beam_PreCommit_Java.yml +++ b/.github/workflows/beam_PreCommit_Java.yml @@ -141,6 +141,11 @@ permissions: security-events: read statuses: read +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + jobs: beam_PreCommit_Java: name: ${{matrix.job_name}} (${{matrix.job_phrase}}) diff --git a/.github/workflows/beam_PreCommit_Java_Debezium_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_Debezium_IO_Direct.yml index 67c746daccc6..db348a7684af 100644 --- a/.github/workflows/beam_PreCommit_Java_Debezium_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_Debezium_IO_Direct.yml @@ -53,6 +53,11 @@ concurrency: group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' cancel-in-progress: true +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + jobs: beam_PreCommit_Java_Debezium_IO_Direct: name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) diff --git a/.github/workflows/beam_PreCommit_Java_ElasticSearch_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_ElasticSearch_IO_Direct.yml index 93baa9518613..27a3e175e7e1 100644 --- a/.github/workflows/beam_PreCommit_Java_ElasticSearch_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_ElasticSearch_IO_Direct.yml @@ -55,6 +55,11 @@ concurrency: group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' cancel-in-progress: true +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + jobs: beam_PreCommit_Java_ElasticSearch_IO_Direct: name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) diff --git a/.github/workflows/beam_PreCommit_Java_Examples_Dataflow.yml b/.github/workflows/beam_PreCommit_Java_Examples_Dataflow.yml index ff1dc5172c29..1dfd9ea1eb4d 100644 --- a/.github/workflows/beam_PreCommit_Java_Examples_Dataflow.yml +++ b/.github/workflows/beam_PreCommit_Java_Examples_Dataflow.yml @@ -92,16 +92,10 @@ jobs: comment_phrase: ${{matrix.job_phrase}} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Setup self-hosted - uses: ./.github/actions/setup-self-hosted-action + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - requires-py-38: false - requires-py-39: false - requires-go: false - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + java-version: 8 - name: Authenticate on GCP uses: google-github-actions/setup-gcloud@v0 with: diff --git a/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java17.yml b/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java17.yml index d31d1a2c2c16..bc5c457eb761 100644 --- a/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java17.yml +++ b/.github/workflows/beam_PreCommit_Java_Examples_Dataflow_Java17.yml @@ -63,6 +63,11 @@ concurrency: group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' cancel-in-progress: true +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + jobs: beam_PreCommit_Java_Examples_Dataflow_Java17: name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) diff --git a/.github/workflows/beam_PreCommit_Java_File-schema-transform_IO_Direct.yml b/.github/workflows/beam_PreCommit_Java_File-schema-transform_IO_Direct.yml index d8b4b1a4983c..d256bca9ebaa 100644 --- a/.github/workflows/beam_PreCommit_Java_File-schema-transform_IO_Direct.yml +++ b/.github/workflows/beam_PreCommit_Java_File-schema-transform_IO_Direct.yml @@ -53,6 +53,11 @@ concurrency: group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' cancel-in-progress: true +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + jobs: beam_PreCommit_Java_File-schema-transform_IO_Direct: name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) diff --git a/.github/workflows/beam_PreCommit_Java_Flink_Versions.yml b/.github/workflows/beam_PreCommit_Java_Flink_Versions.yml index 855e06c827d1..e4a04839cef4 100644 --- a/.github/workflows/beam_PreCommit_Java_Flink_Versions.yml +++ b/.github/workflows/beam_PreCommit_Java_Flink_Versions.yml @@ -79,19 +79,11 @@ jobs: with: comment_phrase: 'Run Java_Flink_Versions PreCommit' github_token: ${{ secrets.GITHUB_TOKEN }} - - name: Install Java - uses: actions/setup-java@v3.8.0 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - distribution: 'zulu' - java-version: '8' - - name: Install Python - uses: actions/setup-python@v4 - with: - python-version: '3.8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + java-version: 8 + python-version: 3.8 - name: run Java Flink Versions PreCommit script uses: ./.github/actions/gradle-command-self-hosted-action with: diff --git a/.github/workflows/beam_PreCommit_Java_PVR_Flink_Batch.yml b/.github/workflows/beam_PreCommit_Java_PVR_Flink_Batch.yml index 10652edb9420..9679b1825cf5 100644 --- a/.github/workflows/beam_PreCommit_Java_PVR_Flink_Batch.yml +++ b/.github/workflows/beam_PreCommit_Java_PVR_Flink_Batch.yml @@ -59,6 +59,11 @@ permissions: security-events: read statuses: read +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + jobs: beam_PreCommit_Java_PVR_Flink_Batch: name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) diff --git a/.github/workflows/beam_PreCommit_Python.yml b/.github/workflows/beam_PreCommit_Python.yml index 743da499e81a..80c69afce6e2 100644 --- a/.github/workflows/beam_PreCommit_Python.yml +++ b/.github/workflows/beam_PreCommit_Python.yml @@ -79,19 +79,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: + java-version: 8 python-version: ${{ matrix.python_version }} - - name: Install Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - name: Set PY_VER_CLEAN id: set_py_ver_clean run: | diff --git a/.github/workflows/beam_PreCommit_PythonDocker.yml b/.github/workflows/beam_PreCommit_PythonDocker.yml index 19ecf593a67c..669f316549c5 100644 --- a/.github/workflows/beam_PreCommit_PythonDocker.yml +++ b/.github/workflows/beam_PreCommit_PythonDocker.yml @@ -78,23 +78,12 @@ jobs: comment_phrase: ${{matrix.job_phrase}} ${{ matrix.python_version }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) - - name: Install Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '8' - - name: Install Go - uses: actions/setup-go@v4 - with: - go-version: '1.16' - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: + java-version: 8 python-version: ${{ matrix.python_version }} - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + go-version: 1.16 - name: Setup Buildx uses: docker/setup-buildx-action@v2 with: diff --git a/.github/workflows/beam_PreCommit_PythonDocs.yml b/.github/workflows/beam_PreCommit_PythonDocs.yml index e3ffd5a70cd7..a67e8afa3a4e 100644 --- a/.github/workflows/beam_PreCommit_PythonDocs.yml +++ b/.github/workflows/beam_PreCommit_PythonDocs.yml @@ -78,19 +78,11 @@ jobs: comment_phrase: ${{matrix.job_phrase}} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Install Java - uses: actions/setup-java@v3.8.0 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - distribution: 'zulu' - java-version: '8' - - name: Install Python - uses: actions/setup-python@v4 - with: - python-version: '3.8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + java-version: 8 + python-version: 3.8 - name: run pythonDocsPreCommit script uses: ./.github/actions/gradle-command-self-hosted-action with: diff --git a/.github/workflows/beam_PreCommit_PythonFormatter.yml b/.github/workflows/beam_PreCommit_PythonFormatter.yml index 08fa9dabc662..1a3335b370e9 100644 --- a/.github/workflows/beam_PreCommit_PythonFormatter.yml +++ b/.github/workflows/beam_PreCommit_PythonFormatter.yml @@ -77,19 +77,11 @@ jobs: comment_phrase: ${{matrix.job_phrase}} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Install Java - uses: actions/setup-java@v3.8.0 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - distribution: 'zulu' - java-version: '8' - - name: Install Python - uses: actions/setup-python@v4 - with: - python-version: '3.8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + java-version: 8 + python-version: 3.8 - name: run pythonFormatterPreCommit script uses: ./.github/actions/gradle-command-self-hosted-action with: diff --git a/.github/workflows/beam_PreCommit_PythonLint.yml b/.github/workflows/beam_PreCommit_PythonLint.yml index f26f423101d9..9d290f1ba86d 100644 --- a/.github/workflows/beam_PreCommit_PythonLint.yml +++ b/.github/workflows/beam_PreCommit_PythonLint.yml @@ -77,23 +77,12 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Install Java - uses: actions/setup-java@v3.8.0 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - distribution: 'zulu' - java-version: '8' - - name: Install Go - uses: actions/setup-go@v4 - with: - go-version: '1.16' - - name: Install Python - uses: actions/setup-python@v4 - with: - python-version: '3.8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + java-version: 8 + python-version: 3.8 + go-version: 1.16 - name: run pythonLintPreCommit script uses: ./.github/actions/gradle-command-self-hosted-action with: diff --git a/.github/workflows/beam_PreCommit_Python_Coverage.yml b/.github/workflows/beam_PreCommit_Python_Coverage.yml index 9bb0b83c7a63..65002f9da894 100644 --- a/.github/workflows/beam_PreCommit_Python_Coverage.yml +++ b/.github/workflows/beam_PreCommit_Python_Coverage.yml @@ -77,19 +77,11 @@ jobs: comment_phrase: ${{matrix.job_phrase}} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - python-version: '3.8' - - name: Install Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + java-version: 8 + python-version: 3.8 - name: Run preCommitPyCoverage uses: ./.github/actions/gradle-command-self-hosted-action with: diff --git a/.github/workflows/beam_PreCommit_Python_Dataframes.yml b/.github/workflows/beam_PreCommit_Python_Dataframes.yml index ab7d07cc72ad..a7a0ec1836ce 100644 --- a/.github/workflows/beam_PreCommit_Python_Dataframes.yml +++ b/.github/workflows/beam_PreCommit_Python_Dataframes.yml @@ -79,19 +79,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase}} ${{ matrix.python_version}} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name}} (${{ matrix.job_phrase}} ${{ matrix.python_version}}) - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: + java-version: 8 python-version: ${{ matrix.python_version }} - - name: Install Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - name: Set PY_VER_CLEAN id: set_py_ver_clean run: | diff --git a/.github/workflows/beam_PreCommit_Python_Examples.yml b/.github/workflows/beam_PreCommit_Python_Examples.yml index f82aef86de52..1b03b4c0a35a 100644 --- a/.github/workflows/beam_PreCommit_Python_Examples.yml +++ b/.github/workflows/beam_PreCommit_Python_Examples.yml @@ -79,19 +79,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: + java-version: 8 python-version: ${{ matrix.python_version }} - - name: Install Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - name: Set PY_VER_CLEAN id: set_py_ver_clean run: | diff --git a/.github/workflows/beam_PreCommit_Python_Integration.yml b/.github/workflows/beam_PreCommit_Python_Integration.yml index 2ef26516d15c..5a1b7bc720f3 100644 --- a/.github/workflows/beam_PreCommit_Python_Integration.yml +++ b/.github/workflows/beam_PreCommit_Python_Integration.yml @@ -79,19 +79,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: + java-version: 8 python-version: ${{ matrix.python_version }} - - name: Install Java - uses: actions/setup-java@v3.8.0 - with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - name: Set PY_VER_CLEAN id: set_py_ver_clean run: | diff --git a/.github/workflows/beam_PreCommit_Python_PVR_Flink.yml b/.github/workflows/beam_PreCommit_Python_PVR_Flink.yml index 131ec8c634a8..a0011354749a 100644 --- a/.github/workflows/beam_PreCommit_Python_PVR_Flink.yml +++ b/.github/workflows/beam_PreCommit_Python_PVR_Flink.yml @@ -71,6 +71,11 @@ concurrency: group: '${{ github.workflow }} @ ${{ github.event.issue.number || github.event.pull_request.head.label || github.sha || github.head_ref || github.ref }}-${{ github.event.schedule || github.event.comment.body || github.event.sender.login}}' cancel-in-progress: true +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + jobs: beam_PreCommit_Python_PVR_Flink: name: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) diff --git a/.github/workflows/beam_PreCommit_Python_Runners.yml b/.github/workflows/beam_PreCommit_Python_Runners.yml index c9f462d385a9..775af7f39d24 100644 --- a/.github/workflows/beam_PreCommit_Python_Runners.yml +++ b/.github/workflows/beam_PreCommit_Python_Runners.yml @@ -79,19 +79,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: + java-version: 8 python-version: ${{ matrix.python_version }} - - name: Install Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - name: Set PY_VER_CLEAN id: set_py_ver_clean run: | diff --git a/.github/workflows/beam_PreCommit_Python_Transforms.yml b/.github/workflows/beam_PreCommit_Python_Transforms.yml index 0627d8d5393a..291dcde8665a 100644 --- a/.github/workflows/beam_PreCommit_Python_Transforms.yml +++ b/.github/workflows/beam_PreCommit_Python_Transforms.yml @@ -79,19 +79,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: + java-version: 8 python-version: ${{ matrix.python_version }} - - name: Install Java - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - name: Set PY_VER_CLEAN id: set_py_ver_clean run: | diff --git a/.github/workflows/beam_PreCommit_RAT.yml b/.github/workflows/beam_PreCommit_RAT.yml index 5c87644858b7..390413fb7aca 100644 --- a/.github/workflows/beam_PreCommit_RAT.yml +++ b/.github/workflows/beam_PreCommit_RAT.yml @@ -76,15 +76,10 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Install Java - uses: actions/setup-java@v3.8.0 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + java-version: 8 - name: run RAT script uses: ./.github/actions/gradle-command-self-hosted-action with: diff --git a/.github/workflows/beam_PreCommit_Spotless.yml b/.github/workflows/beam_PreCommit_Spotless.yml index ba748cd7b136..552a92e104a1 100644 --- a/.github/workflows/beam_PreCommit_Spotless.yml +++ b/.github/workflows/beam_PreCommit_Spotless.yml @@ -62,6 +62,11 @@ permissions: security-events: read statuses: read +env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_TOKEN }} + GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GE_CACHE_USERNAME }} + GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GE_CACHE_PASSWORD }} + jobs: beam_PreCommit_Spotless: name: ${{matrix.job_name}} (${{matrix.job_phrase}}) diff --git a/.github/workflows/beam_PreCommit_Typescript.yml b/.github/workflows/beam_PreCommit_Typescript.yml index e83e04a1bae4..21c760f2e525 100644 --- a/.github/workflows/beam_PreCommit_Typescript.yml +++ b/.github/workflows/beam_PreCommit_Typescript.yml @@ -79,15 +79,11 @@ jobs: comment_phrase: ${{matrix.job_phrase}} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Setup self-hosted - uses: ./.github/actions/setup-self-hosted-action + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - requires-py-39: false - requires-go: false - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + python-version: 3.8 + java-version: 8 - name: run typescriptPreCommit script uses: ./.github/actions/gradle-command-self-hosted-action with: diff --git a/.github/workflows/beam_PreCommit_Website.yml b/.github/workflows/beam_PreCommit_Website.yml index 81fc578bc345..87218dc28033 100644 --- a/.github/workflows/beam_PreCommit_Website.yml +++ b/.github/workflows/beam_PreCommit_Website.yml @@ -78,15 +78,10 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Install Java - uses: actions/setup-java@v3.8.0 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + java-version: 8 - name: run websitePreCommit script uses: ./.github/actions/gradle-command-self-hosted-action with: diff --git a/.github/workflows/beam_PreCommit_Website_Stage_GCS.yml b/.github/workflows/beam_PreCommit_Website_Stage_GCS.yml index cf9423f81670..f910f9a88da3 100644 --- a/.github/workflows/beam_PreCommit_Website_Stage_GCS.yml +++ b/.github/workflows/beam_PreCommit_Website_Stage_GCS.yml @@ -82,15 +82,11 @@ jobs: github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - name: Echo PR number run: echo "ghprbPullId=${{ github.event.pull_request.number || github.event.issue.number }}" >> $GITHUB_ENV - - name: Setup self-hosted - uses: ./.github/actions/setup-self-hosted-action + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - requires-py-39: false - requires-go: false - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + python-version: 3.8 + java-version: 8 - name: Authenticate on GCP uses: google-github-actions/setup-gcloud@v0 with: diff --git a/.github/workflows/beam_PreCommit_Whitespace.yml b/.github/workflows/beam_PreCommit_Whitespace.yml index b8ef21c8b077..03a976cfe444 100644 --- a/.github/workflows/beam_PreCommit_Whitespace.yml +++ b/.github/workflows/beam_PreCommit_Whitespace.yml @@ -77,19 +77,11 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }}) - - name: Install Java - uses: actions/setup-java@v3.8.0 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - distribution: 'zulu' - java-version: '8' - - name: Install Python - uses: actions/setup-python@v4 - with: - python-version: '3.8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + java-version: 8 + python-version: 3.8 - name: run whitespacePreCommit script uses: ./.github/actions/gradle-command-self-hosted-action with: diff --git a/.github/workflows/beam_Python_ValidatesContainer_Dataflow_ARM.yml b/.github/workflows/beam_Python_ValidatesContainer_Dataflow_ARM.yml index b56d81f17ca8..d4c9178e6c91 100644 --- a/.github/workflows/beam_Python_ValidatesContainer_Dataflow_ARM.yml +++ b/.github/workflows/beam_Python_ValidatesContainer_Dataflow_ARM.yml @@ -76,14 +76,10 @@ jobs: comment_phrase: ${{ matrix.job_phrase }} ${{ matrix.python_version }} github_token: ${{ secrets.GITHUB_TOKEN }} github_job: ${{ matrix.job_name }} (${{ matrix.job_phrase }} ${{ matrix.python_version }}) - - name: Install Python - uses: actions/setup-python@v4 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: python-version: ${{ matrix.python_version }} - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Authenticate on GCP diff --git a/.github/workflows/beam_Release_NightlySnapshot.yml b/.github/workflows/beam_Release_NightlySnapshot.yml index a80105d2a7cb..a4be830cd3c5 100644 --- a/.github/workflows/beam_Release_NightlySnapshot.yml +++ b/.github/workflows/beam_Release_NightlySnapshot.yml @@ -61,15 +61,10 @@ jobs: github_job: ${{matrix.job_name}} github_token: ${{ secrets.GITHUB_TOKEN }} comment_phrase: "Release Nightly Snapshot" - - name: Install Java - uses: actions/setup-java@v3.8.0 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + java-version: 8 - name: Auth on snapshot repository run: | mkdir -p ${HOME}/.m2 diff --git a/.github/workflows/beam_Release_Python_NightlySnapshot.yml b/.github/workflows/beam_Release_Python_NightlySnapshot.yml index 58f879d83148..62019c536969 100644 --- a/.github/workflows/beam_Release_Python_NightlySnapshot.yml +++ b/.github/workflows/beam_Release_Python_NightlySnapshot.yml @@ -60,19 +60,11 @@ jobs: github_job: ${{matrix.job_name}} github_token: ${{ secrets.GITHUB_TOKEN }} comment_phrase: ${{matrix.job_phrase}} - - name: Install Java - uses: actions/setup-java@v3.8.0 + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - distribution: 'zulu' - java-version: '8' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: '3.8' + java-version: 8 + python-version: 3.8 - name: Authenticate on GCP uses: google-github-actions/setup-gcloud@v0 with: diff --git a/.github/workflows/java_tests.yml b/.github/workflows/java_tests.yml index d1180eebdf81..ceff29b50d4f 100644 --- a/.github/workflows/java_tests.yml +++ b/.github/workflows/java_tests.yml @@ -77,11 +77,11 @@ jobs: with: persist-credentials: false submodules: recursive - - name: Setup self-hosted - uses: ./.github/actions/setup-self-hosted-action + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - requires-py-38: false - requires-py-39: false + java-version: 8 + go-version: 1.21 - name: Remove default github maven configuration # This step is a workaround to avoid a decryption issue of Beam's # net.linguica.gradle.maven.settings plugin and github's provided maven @@ -136,12 +136,11 @@ jobs: with: persist-credentials: false submodules: recursive - - name: Setup self-hosted - uses: ./.github/actions/setup-self-hosted-action + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - requires-py-38: false - requires-py-39: false - + java-version: 8 + go-version: 1.21 - name: Remove default github maven configuration # This step is a workaround to avoid a decryption issue of Beam's # net.linguica.gradle.maven.settings plugin and github's provided maven @@ -180,11 +179,11 @@ jobs: with: persist-credentials: false submodules: recursive - - name: Setup self-hosted - uses: ./.github/actions/setup-self-hosted-action - with: - requires-py-38: false - requires-py-39: false + - name: Setup environment + uses: ./.github/actions/setup-environment-action + with: + java-version: 8 + go-version: 1.21 - name: Authenticate on GCP uses: google-github-actions/setup-gcloud@v0 with: diff --git a/.github/workflows/playground_backend_precommit.yml b/.github/workflows/playground_backend_precommit.yml index de9edd4f2a94..114ca4aac1cb 100644 --- a/.github/workflows/playground_backend_precommit.yml +++ b/.github/workflows/playground_backend_precommit.yml @@ -38,19 +38,12 @@ jobs: steps: - name: Check out the repo uses: actions/checkout@v4 - - - uses: actions/setup-python@v4 - with: - python-version: '${{ env.PYTHON_VERSION }}' - - uses: actions/setup-java@v3.8.0 + + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: - distribution: 'zulu' java-version: '${{ env.JAVA_VERSION }}' - - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false + python-version: '${{ env.PYTHON_VERSION }}' - name: Add GOPATH/bin to PATH run: echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV diff --git a/.github/workflows/tour_of_beam_backend_integration.yml b/.github/workflows/tour_of_beam_backend_integration.yml index 1eb4b66f5868..8f56d3f2e2fa 100644 --- a/.github/workflows/tour_of_beam_backend_integration.yml +++ b/.github/workflows/tour_of_beam_backend_integration.yml @@ -75,16 +75,13 @@ jobs: working-directory: ./learning/tour-of-beam/backend steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + + - name: Setup environment + uses: ./.github/actions/setup-environment-action with: # pin to the biggest Go version supported by Cloud Functions runtime go-version: '1.16' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - cache-read-only: false - - name: Build Playground router image run: ./gradlew -i playground:backend:containers:router:docker working-directory: ${{ env.GITHUB_WORKSPACE }} diff --git a/.github/workflows/update_python_dependencies.yml b/.github/workflows/update_python_dependencies.yml index 43bed87a42f3..b4b839c3204c 100644 --- a/.github/workflows/update_python_dependencies.yml +++ b/.github/workflows/update_python_dependencies.yml @@ -50,7 +50,13 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - name: Setup environment - uses: ./.github/actions/setup-self-hosted-action + uses: ./.github/actions/setup-environment-action + with: + python-version: | + 3.8 + 3.9 + java-version: 8 + go-version: 1.21 - name: Update Python Dependencies uses: ./.github/actions/gradle-command-self-hosted-action with: From a7335cb0b3c65174b5eb04b072fa48f28d1ff7cf Mon Sep 17 00:00:00 2001 From: caneff Date: Wed, 20 Sep 2023 11:26:38 -0400 Subject: [PATCH 091/105] Remove deprecated week and weekofyear for Pandas 2 (#28492) --- sdks/python/apache_beam/dataframe/frames.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sdks/python/apache_beam/dataframe/frames.py b/sdks/python/apache_beam/dataframe/frames.py index 7929c879bdd2..80af501cfacb 100644 --- a/sdks/python/apache_beam/dataframe/frames.py +++ b/sdks/python/apache_beam/dataframe/frames.py @@ -5377,11 +5377,12 @@ def func(df, *args, **kwargs): 'second', 'time', 'timetz', - 'week', 'weekday', - 'weekofyear', 'year', ] +# Pandas 2 removed these. +if PD_VERSION < (2, 0): + ELEMENTWISE_DATETIME_PROPERTIES += ['week', 'weekofyear'] for method in ELEMENTWISE_DATETIME_PROPERTIES: setattr(_DeferredDatetimeMethods, From 932744801e67c7d03560114a78881be62aadfa19 Mon Sep 17 00:00:00 2001 From: caneff Date: Wed, 20 Sep 2023 11:28:22 -0400 Subject: [PATCH 092/105] Change pd.core.strings.StringMethods for Pandas 2 compatability. (#28455) --- sdks/python/apache_beam/dataframe/frames.py | 34 +++++++++---------- .../apache_beam/dataframe/frames_test.py | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/sdks/python/apache_beam/dataframe/frames.py b/sdks/python/apache_beam/dataframe/frames.py index 80af501cfacb..a74ccbba041a 100644 --- a/sdks/python/apache_beam/dataframe/frames.py +++ b/sdks/python/apache_beam/dataframe/frames.py @@ -4931,9 +4931,9 @@ def __setitem__(self, index, value): class _DeferredStringMethods(frame_base.DeferredBase): - @frame_base.with_docs_from(pd.core.strings.StringMethods) - @frame_base.args_to_kwargs(pd.core.strings.StringMethods) - @frame_base.populate_defaults(pd.core.strings.StringMethods) + @frame_base.with_docs_from(pd.Series.str) + @frame_base.args_to_kwargs(pd.Series.str) + @frame_base.populate_defaults(pd.Series.str) def cat(self, others, join, **kwargs): """If defined, ``others`` must be a :class:`DeferredSeries` or a ``list`` of ``DeferredSeries``.""" @@ -4973,8 +4973,8 @@ def func(*args): requires_partition_by=requires, preserves_partition_by=partitionings.Arbitrary())) - @frame_base.with_docs_from(pd.core.strings.StringMethods) - @frame_base.args_to_kwargs(pd.core.strings.StringMethods) + @frame_base.with_docs_from(pd.Series.str) + @frame_base.args_to_kwargs(pd.Series.str) def repeat(self, repeats): """``repeats`` must be an ``int`` or a :class:`DeferredSeries`. Lists are not supported because they make this operation order-sensitive.""" @@ -5011,8 +5011,8 @@ def repeat(self, repeats): raise TypeError("str.repeat(repeats=) value must be an int or a " f"DeferredSeries (encountered {type(repeats)}).") - @frame_base.with_docs_from(pd.core.strings.StringMethods) - @frame_base.args_to_kwargs(pd.core.strings.StringMethods) + @frame_base.with_docs_from(pd.Series.str) + @frame_base.args_to_kwargs(pd.Series.str) def get_dummies(self, **kwargs): """ Series must be categorical dtype. Please cast to ``CategoricalDtype`` @@ -5094,9 +5094,9 @@ def func(s): requires_partition_by=partitionings.Arbitrary(), preserves_partition_by=partitionings.Arbitrary())) - @frame_base.with_docs_from(pd.core.strings.StringMethods) - @frame_base.args_to_kwargs(pd.core.strings.StringMethods) - @frame_base.populate_defaults(pd.core.strings.StringMethods) + @frame_base.with_docs_from(pd.Series.str) + @frame_base.args_to_kwargs(pd.Series.str) + @frame_base.populate_defaults(pd.Series.str) def split(self, **kwargs): """ Like other non-deferred methods, dtype must be CategoricalDtype. @@ -5105,9 +5105,9 @@ def split(self, **kwargs): """ return self._split_helper(rsplit=False, **kwargs) - @frame_base.with_docs_from(pd.core.strings.StringMethods) - @frame_base.args_to_kwargs(pd.core.strings.StringMethods) - @frame_base.populate_defaults(pd.core.strings.StringMethods) + @frame_base.with_docs_from(pd.Series.str) + @frame_base.args_to_kwargs(pd.Series.str) + @frame_base.populate_defaults(pd.Series.str) def rsplit(self, **kwargs): """ Like other non-deferred methods, dtype must be CategoricalDtype. @@ -5185,17 +5185,17 @@ def func(df, *args, **kwargs): return func for method in ELEMENTWISE_STRING_METHODS: - if not hasattr(pd.core.strings.StringMethods, method): + if not hasattr(pd.Series.str, method): # older versions (1.0.x) don't support some of these methods continue setattr(_DeferredStringMethods, method, frame_base._elementwise_method(make_str_func(method), name=method, - base=pd.core.strings.StringMethods)) + base=pd.Series.str)) for method in NON_ELEMENTWISE_STRING_METHODS: - if not hasattr(pd.core.strings.StringMethods, method): + if not hasattr(pd.Series.str, method): # older versions (1.0.x) don't support some of these methods continue setattr(_DeferredStringMethods, @@ -5203,7 +5203,7 @@ def func(df, *args, **kwargs): frame_base._proxy_method( make_str_func(method), name=method, - base=pd.core.strings.StringMethods, + base=pd.Series.str, requires_partition_by=partitionings.Arbitrary(), preserves_partition_by=partitionings.Singleton())) diff --git a/sdks/python/apache_beam/dataframe/frames_test.py b/sdks/python/apache_beam/dataframe/frames_test.py index 257d77e0a6b3..4998683461b9 100644 --- a/sdks/python/apache_beam/dataframe/frames_test.py +++ b/sdks/python/apache_beam/dataframe/frames_test.py @@ -2986,7 +2986,7 @@ class DocstringTest(unittest.TestCase): (frames.DeferredDataFrame, pd.DataFrame), (frames.DeferredSeries, pd.Series), #(frames._DeferredIndex, pd.Index), - (frames._DeferredStringMethods, pd.core.strings.StringMethods), + (frames._DeferredStringMethods, pd.Series.str), ( frames._DeferredCategoricalMethods, pd.core.arrays.categorical.CategoricalAccessor), From 840c85883fcf58834c4c0ff5060e13c5d3500184 Mon Sep 17 00:00:00 2001 From: Robert Burke Date: Wed, 20 Sep 2023 08:30:29 -0700 Subject: [PATCH 093/105] [#28187][prism] Basic cross language support. (#28545) * Move wk.Stop() to context cancel. * [prism] Basic Xlang support. --------- Co-authored-by: lostluck <13907733+lostluck@users.noreply.github.com> --- .../pkg/beam/core/runtime/exec/fullvalue.go | 6 +++ .../runners/prism/internal/environments.go | 2 + .../beam/runners/prism/internal/execute.go | 52 +++++++++++-------- .../prism/internal/jobservices/management.go | 20 ++++--- .../pkg/beam/runners/prism/internal/stage.go | 16 +++--- .../beam/runners/prism/internal/urns/urns.go | 2 + .../runners/prism/internal/worker/bundle.go | 2 +- .../runners/prism/internal/worker/worker.go | 24 ++++----- .../prism/internal/worker/worker_test.go | 14 +---- sdks/go/test/integration/integration.go | 4 +- 10 files changed, 76 insertions(+), 66 deletions(-) diff --git a/sdks/go/pkg/beam/core/runtime/exec/fullvalue.go b/sdks/go/pkg/beam/core/runtime/exec/fullvalue.go index aaa049510f52..0a9343199a1c 100644 --- a/sdks/go/pkg/beam/core/runtime/exec/fullvalue.go +++ b/sdks/go/pkg/beam/core/runtime/exec/fullvalue.go @@ -251,6 +251,9 @@ func (s *decodeStream) Read() (*FullValue, error) { } err := s.d.DecodeTo(s.r, &s.ret) if err != nil { + if err == io.EOF { + return nil, io.EOF + } return nil, errors.Wrap(err, "decodeStream value decode failed") } s.next++ @@ -342,6 +345,9 @@ func (s *decodeMultiChunkStream) Read() (*FullValue, error) { if s.chunk == 0 && s.next == 0 { chunk, err := coder.DecodeVarInt(s.r.reader) if err != nil { + if err == io.EOF { + return nil, io.EOF + } return nil, errors.Wrap(err, "decodeMultiChunkStream chunk size decoding failed") } s.chunk = chunk diff --git a/sdks/go/pkg/beam/runners/prism/internal/environments.go b/sdks/go/pkg/beam/runners/prism/internal/environments.go index d4fb6ad5b3e1..7d54cb366ffe 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/environments.go +++ b/sdks/go/pkg/beam/runners/prism/internal/environments.go @@ -99,6 +99,7 @@ func externalEnvironment(ctx context.Context, ep *pipepb.ExternalPayload, wk *wo pool.StopWorker(context.Background(), &fnpb.StopWorkerRequest{ WorkerId: wk.ID, }) + wk.Stop() } func dockerEnvironment(ctx context.Context, logger *slog.Logger, dp *pipepb.DockerPayload, wk *worker.W, artifactEndpoint string) error { @@ -170,6 +171,7 @@ func dockerEnvironment(ctx context.Context, logger *slog.Logger, dp *pipepb.Dock // Start goroutine to wait on container state. go func() { defer cli.Close() + defer wk.Stop() statusCh, errCh := cli.ContainerWait(ctx, containerID, container.WaitConditionNotRunning) select { diff --git a/sdks/go/pkg/beam/runners/prism/internal/execute.go b/sdks/go/pkg/beam/runners/prism/internal/execute.go index cf04381b9cbe..c1ac6ea4488c 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/execute.go +++ b/sdks/go/pkg/beam/runners/prism/internal/execute.go @@ -20,6 +20,7 @@ import ( "fmt" "io" "sort" + "sync/atomic" "time" "github.com/apache/beam/sdks/v2/go/pkg/beam/core/graph/mtime" @@ -46,15 +47,14 @@ func RunPipeline(j *jobservices.Job) { // here, we only want and need the go one, operating // in loopback mode. envs := j.Pipeline.GetComponents().GetEnvironments() - if len(envs) != 1 { - j.Failed(fmt.Errorf("unable to execute multi-environment pipelines;\npipeline has environments: %+v", envs)) - return - } - env, _ := getOnlyPair(envs) - wk, err := makeWorker(env, j) - if err != nil { - j.Failed(err) - return + wks := map[string]*worker.W{} + for envID := range envs { + wk, err := makeWorker(envID, j) + if err != nil { + j.Failed(err) + return + } + wks[envID] = wk } // When this function exits, we cancel the context to clear // any related job resources. @@ -65,15 +65,12 @@ func RunPipeline(j *jobservices.Job) { j.SendMsg("running " + j.String()) j.Running() - if err := executePipeline(j.RootCtx, wk, j); err != nil { + if err := executePipeline(j.RootCtx, wks, j); err != nil { j.Failed(err) return } j.SendMsg("pipeline completed " + j.String()) - // Stop the worker. - wk.Stop() - j.SendMsg("terminating " + j.String()) j.Done() } @@ -95,7 +92,7 @@ func makeWorker(env string, j *jobservices.Job) (*worker.W, error) { // Check for connection succeeding after we've created the environment successfully. timeout := 1 * time.Minute time.AfterFunc(timeout, func() { - if wk.Connected() { + if wk.Connected() || wk.Stopped() { return } err := fmt.Errorf("prism %v didn't get control connection to %v after %v", wk, wk.Endpoint(), timeout) @@ -115,7 +112,7 @@ type processor struct { transformExecuters map[string]transformExecuter } -func executePipeline(ctx context.Context, wk *worker.W, j *jobservices.Job) error { +func executePipeline(ctx context.Context, wks map[string]*worker.W, j *jobservices.Job) error { pipeline := j.Pipeline comps := proto.Clone(pipeline.GetComponents()).(*pipepb.Components) @@ -158,7 +155,12 @@ func executePipeline(ctx context.Context, wk *worker.W, j *jobservices.Job) erro // TODO move this loop and code into the preprocessor instead. stages := map[string]*stage{} var impulses []string - for _, stage := range topo { + + // Inialize the "dataservice cache" to support side inputs. + // TODO(https://github.com/apache/beam/issues/28543), remove this concept. + ds := &worker.DataService{} + + for i, stage := range topo { tid := stage.transforms[0] t := ts[tid] urn := t.GetSpec().GetUrn() @@ -169,11 +171,11 @@ func executePipeline(ctx context.Context, wk *worker.W, j *jobservices.Job) erro if stage.exe != nil { stage.envID = stage.exe.ExecuteWith(t) } - stage.ID = wk.NextStage() + stage.ID = fmt.Sprintf("stage-%03d", i) + wk := wks[stage.envID] switch stage.envID { case "": // Runner Transforms - var onlyOut string for _, out := range t.GetOutputs() { onlyOut = out @@ -232,10 +234,8 @@ func executePipeline(ctx context.Context, wk *worker.W, j *jobservices.Job) erro em.AddStage(stage.ID, inputs, nil, []string{getOnlyValue(t.GetOutputs())}) } stages[stage.ID] = stage - wk.Descriptors[stage.ID] = stage.desc case wk.Env: - // Great! this is for this environment. // Broken abstraction. - if err := buildDescriptor(stage, comps, wk); err != nil { + if err := buildDescriptor(stage, comps, wk, ds); err != nil { return fmt.Errorf("prism error building stage %v: \n%w", stage.ID, err) } stages[stage.ID] = stage @@ -259,7 +259,12 @@ func executePipeline(ctx context.Context, wk *worker.W, j *jobservices.Job) erro maxParallelism := make(chan struct{}, 8) // Execute stages here bundleFailed := make(chan error) - bundles := em.Bundles(ctx, wk.NextInst) + + var instID uint64 + bundles := em.Bundles(ctx, func() string { + return fmt.Sprintf("inst%03d", atomic.AddUint64(&instID, 1)) + }) + for { select { case <-ctx.Done(): @@ -273,7 +278,8 @@ func executePipeline(ctx context.Context, wk *worker.W, j *jobservices.Job) erro go func(rb engine.RunBundle) { defer func() { <-maxParallelism }() s := stages[rb.StageID] - if err := s.Execute(ctx, j, wk, comps, em, rb); err != nil { + wk := wks[s.envID] + if err := s.Execute(ctx, j, wk, ds, comps, em, rb); err != nil { // Ensure we clean up on bundle failure em.FailBundle(rb) bundleFailed <- err diff --git a/sdks/go/pkg/beam/runners/prism/internal/jobservices/management.go b/sdks/go/pkg/beam/runners/prism/internal/jobservices/management.go index 213e33a78379..0fd7381e17f4 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/jobservices/management.go +++ b/sdks/go/pkg/beam/runners/prism/internal/jobservices/management.go @@ -93,14 +93,18 @@ func (s *Server) Prepare(ctx context.Context, req *jobpb.PrepareJobRequest) (*jo return nil, err } var errs []error - check := func(feature string, got, want any) { - if got != want { - err := unimplementedError{ - feature: feature, - value: got, + check := func(feature string, got any, wants ...any) { + for _, want := range wants { + if got == want { + return } - errs = append(errs, err) } + + err := unimplementedError{ + feature: feature, + value: got, + } + errs = append(errs, err) } // Inspect Transforms for unsupported features. @@ -114,6 +118,8 @@ func (s *Server) Prepare(ctx context.Context, req *jobpb.PrepareJobRequest) (*jo urns.TransformGBK, urns.TransformFlatten, urns.TransformCombinePerKey, + urns.TransformCombineGlobally, // Used by Java SDK + urns.TransformCombineGroupedValues, // Used by Java SDK urns.TransformAssignWindows: // Very few expected transforms types for submitted pipelines. // Most URNs are for the runner to communicate back to the SDK for execution. @@ -154,7 +160,7 @@ func (s *Server) Prepare(ctx context.Context, req *jobpb.PrepareJobRequest) (*jo check("WindowingStrategy.MergeStatus", ws.GetMergeStatus(), pipepb.MergeStatus_NON_MERGING) } if !bypassedWindowingStrategies[wsID] { - check("WindowingStrategy.OnTimeBehavior", ws.GetOnTimeBehavior(), pipepb.OnTimeBehavior_FIRE_IF_NONEMPTY) + check("WindowingStrategy.OnTimeBehavior", ws.GetOnTimeBehavior(), pipepb.OnTimeBehavior_FIRE_IF_NONEMPTY, pipepb.OnTimeBehavior_FIRE_ALWAYS) check("WindowingStrategy.OutputTime", ws.GetOutputTime(), pipepb.OutputTime_END_OF_WINDOW) // Non nil triggers should fail. if ws.GetTrigger().GetDefault() == nil { diff --git a/sdks/go/pkg/beam/runners/prism/internal/stage.go b/sdks/go/pkg/beam/runners/prism/internal/stage.go index 4d8d4621168d..4ce3ce7ffeb6 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/stage.go +++ b/sdks/go/pkg/beam/runners/prism/internal/stage.go @@ -75,7 +75,7 @@ type stage struct { OutputsToCoders map[string]engine.PColInfo } -func (s *stage) Execute(ctx context.Context, j *jobservices.Job, wk *worker.W, comps *pipepb.Components, em *engine.ElementManager, rb engine.RunBundle) error { +func (s *stage) Execute(ctx context.Context, j *jobservices.Job, wk *worker.W, ds *worker.DataService, comps *pipepb.Components, em *engine.ElementManager, rb engine.RunBundle) error { slog.Debug("Execute: starting bundle", "bundle", rb) var b *worker.B @@ -204,8 +204,8 @@ progress: md := wk.MonitoringMetadata(ctx, unknownIDs) j.AddMetricShortIDs(md) } - // TODO handle side input data properly. - wk.D.Commit(b.OutputData) + // TODO(https://github.com/apache/beam/issues/28543) handle side input data properly. + ds.Commit(b.OutputData) var residualData [][]byte var minOutputWatermark map[string]mtime.Time for _, rr := range resp.GetResidualRoots() { @@ -270,7 +270,7 @@ func portFor(wInCid string, wk *worker.W) []byte { // It assumes that the side inputs are not sourced from PCollections generated by any transform in this stage. // // Because we need the local ids for routing the sources/sinks information. -func buildDescriptor(stg *stage, comps *pipepb.Components, wk *worker.W) error { +func buildDescriptor(stg *stage, comps *pipepb.Components, wk *worker.W, ds *worker.DataService) error { // Assume stage has an indicated primary input coders := map[string]*pipepb.Coder{} @@ -327,7 +327,7 @@ func buildDescriptor(stg *stage, comps *pipepb.Components, wk *worker.W) error { // Update side inputs to point to new PCollection with any replaced coders. transforms[si.transform].GetInputs()[si.local] = newGlobal } - prepSide, err := handleSideInput(si.transform, si.local, si.global, comps, coders, wk) + prepSide, err := handleSideInput(si.transform, si.local, si.global, comps, coders, ds) if err != nil { slog.Error("buildDescriptor: handleSideInputs", err, slog.String("transformID", si.transform)) return err @@ -392,7 +392,7 @@ func buildDescriptor(stg *stage, comps *pipepb.Components, wk *worker.W) error { } // handleSideInput returns a closure that will look up the data for a side input appropriate for the given watermark. -func handleSideInput(tid, local, global string, comps *pipepb.Components, coders map[string]*pipepb.Coder, wk *worker.W) (func(b *worker.B, watermark mtime.Time), error) { +func handleSideInput(tid, local, global string, comps *pipepb.Components, coders map[string]*pipepb.Coder, ds *worker.DataService) (func(b *worker.B, watermark mtime.Time), error) { t := comps.GetTransforms()[tid] sis, err := getSideInputs(t) if err != nil { @@ -412,7 +412,7 @@ func handleSideInput(tid, local, global string, comps *pipepb.Components, coders global, local := global, local return func(b *worker.B, watermark mtime.Time) { - data := wk.D.GetAllData(global) + data := ds.GetAllData(global) if b.IterableSideInputData == nil { b.IterableSideInputData = map[string]map[string]map[typex.Window][][]byte{} @@ -447,7 +447,7 @@ func handleSideInput(tid, local, global string, comps *pipepb.Components, coders global, local := global, local return func(b *worker.B, watermark mtime.Time) { // May be of zero length, but that's OK. Side inputs can be empty. - data := wk.D.GetAllData(global) + data := ds.GetAllData(global) if b.MultiMapSideInputData == nil { b.MultiMapSideInputData = map[string]map[string]map[typex.Window]map[string][][]byte{} } diff --git a/sdks/go/pkg/beam/runners/prism/internal/urns/urns.go b/sdks/go/pkg/beam/runners/prism/internal/urns/urns.go index 9fc2c1a923c5..bf1e36656661 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/urns/urns.go +++ b/sdks/go/pkg/beam/runners/prism/internal/urns/urns.go @@ -57,7 +57,9 @@ var ( // SDK transforms. TransformParDo = ptUrn(pipepb.StandardPTransforms_PAR_DO) TransformCombinePerKey = ctUrn(pipepb.StandardPTransforms_COMBINE_PER_KEY) + TransformCombineGlobally = ctUrn(pipepb.StandardPTransforms_COMBINE_GLOBALLY) TransformReshuffle = ctUrn(pipepb.StandardPTransforms_RESHUFFLE) + TransformCombineGroupedValues = cmbtUrn(pipepb.StandardPTransforms_COMBINE_GROUPED_VALUES) TransformPreCombine = cmbtUrn(pipepb.StandardPTransforms_COMBINE_PER_KEY_PRECOMBINE) TransformMerge = cmbtUrn(pipepb.StandardPTransforms_COMBINE_PER_KEY_MERGE_ACCUMULATORS) TransformExtract = cmbtUrn(pipepb.StandardPTransforms_COMBINE_PER_KEY_EXTRACT_OUTPUTS) diff --git a/sdks/go/pkg/beam/runners/prism/internal/worker/bundle.go b/sdks/go/pkg/beam/runners/prism/internal/worker/bundle.go index 98479e3db071..573bdf4aeb9d 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/worker/bundle.go +++ b/sdks/go/pkg/beam/runners/prism/internal/worker/bundle.go @@ -93,7 +93,7 @@ func (b *B) Respond(resp *fnpb.InstructionResponse) { } b.responded = true if resp.GetError() != "" { - b.BundleErr = fmt.Errorf("bundle %v failed:%v", resp.GetInstructionId(), resp.GetError()) + b.BundleErr = fmt.Errorf("bundle %v %v failed:%v", resp.GetInstructionId(), b.PBDID, resp.GetError()) close(b.Resp) return } diff --git a/sdks/go/pkg/beam/runners/prism/internal/worker/worker.go b/sdks/go/pkg/beam/runners/prism/internal/worker/worker.go index f33ff178c46d..4968c9eb433e 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/worker/worker.go +++ b/sdks/go/pkg/beam/runners/prism/internal/worker/worker.go @@ -67,7 +67,7 @@ type W struct { server *grpc.Server // These are the ID sources - inst, bund uint64 + inst uint64 connected, stopped atomic.Bool InstReqs chan *fnpb.InstructionRequest @@ -76,8 +76,6 @@ type W struct { mu sync.Mutex activeInstructions map[string]controlResponder // Active instructions keyed by InstructionID Descriptors map[string]*fnpb.ProcessBundleDescriptor // Stages keyed by PBDID - - D *DataService } type controlResponder interface { @@ -104,8 +102,6 @@ func New(id, env string) *W { activeInstructions: make(map[string]controlResponder), Descriptors: make(map[string]*fnpb.ProcessBundleDescriptor), - - D: &DataService{}, } slog.Debug("Serving Worker components", slog.String("endpoint", wk.Endpoint())) fnpb.RegisterBeamFnControlServer(wk.server, wk) @@ -149,11 +145,7 @@ func (wk *W) Stop() { } func (wk *W) NextInst() string { - return fmt.Sprintf("inst%03d", atomic.AddUint64(&wk.inst, 1)) -} - -func (wk *W) NextStage() string { - return fmt.Sprintf("stage%03d", atomic.AddUint64(&wk.bund, 1)) + return fmt.Sprintf("inst-%v-%03d", wk.Env, atomic.AddUint64(&wk.inst, 1)) } // TODO set logging level. @@ -263,6 +255,11 @@ func (wk *W) Connected() bool { return wk.connected.Load() } +// Stopped indicates that the worker has stopped. +func (wk *W) Stopped() bool { + return wk.stopped.Load() +} + // Control relays instructions to SDKs and back again, coordinated via unique instructionIDs. // // Requests come from the runner, and are sent to the client in the SDK. @@ -312,10 +309,12 @@ func (wk *W) Control(ctrl fnpb.BeamFnControl_ControlServer) error { wk.mu.Lock() // Fail extant instructions slog.Debug("SDK Disconnected", "worker", wk, "ctx_error", ctrl.Context().Err(), "outstanding_instructions", len(wk.activeInstructions)) + + msg := fmt.Sprintf("SDK worker disconnected: %v, %v active instructions", wk.String(), len(wk.activeInstructions)) for instID, b := range wk.activeInstructions { b.Respond(&fnpb.InstructionResponse{ InstructionId: instID, - Error: "SDK Disconnected", + Error: msg, }) } wk.mu.Unlock() @@ -536,7 +535,7 @@ func (wk *W) sendInstruction(ctx context.Context, req *fnpb.InstructionRequest) req.InstructionId = progInst - if wk.stopped.Load() { + if wk.Stopped() { return nil } wk.InstReqs <- req @@ -566,6 +565,7 @@ func (wk *W) MonitoringMetadata(ctx context.Context, unknownIDs []string) *fnpb. // DataService is slated to be deleted in favour of stage based state // management for side inputs. +// TODO(https://github.com/apache/beam/issues/28543), remove this concept. type DataService struct { mu sync.Mutex // TODO actually quick process the data to windows here as well. diff --git a/sdks/go/pkg/beam/runners/prism/internal/worker/worker_test.go b/sdks/go/pkg/beam/runners/prism/internal/worker/worker_test.go index ed61f484481c..6a90b463c45d 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/worker/worker_test.go +++ b/sdks/go/pkg/beam/runners/prism/internal/worker/worker_test.go @@ -50,18 +50,6 @@ func TestWorker_NextInst(t *testing.T) { } } -func TestWorker_NextStage(t *testing.T) { - w := New("test", "testEnv") - - stageIDs := map[string]struct{}{} - for i := 0; i < 100; i++ { - stageIDs[w.NextStage()] = struct{}{} - } - if got, want := len(stageIDs), 100; got != want { - t.Errorf("calling w.NextStage() got %v unique ids, want %v", got, want) - } -} - func TestWorker_GetProcessBundleDescriptor(t *testing.T) { w := New("test", "testEnv") @@ -189,7 +177,7 @@ func TestWorker_Data_HappyPath(t *testing.T) { b := &B{ InstID: instID, - PBDID: wk.NextStage(), + PBDID: "teststageID", InputData: [][]byte{ {1, 1, 1, 1, 1, 1}, }, diff --git a/sdks/go/test/integration/integration.go b/sdks/go/test/integration/integration.go index bb7f5275a163..f3cffd176110 100644 --- a/sdks/go/test/integration/integration.go +++ b/sdks/go/test/integration/integration.go @@ -140,8 +140,8 @@ var portableFilters = []string{ } var prismFilters = []string{ - // The prism runner does not yet support cross-language. - "TestXLang.*", + // The prism runner does not yet support Java's CoGBK. + "TestXLang_CoGroupBy", // The prism runner does not support the TestStream primitive "TestTestStream.*", // The trigger and pane tests uses TestStream From 7dfc0c03b1b793d9f798dc2dd93b0f81547568b3 Mon Sep 17 00:00:00 2001 From: Yi Hu Date: Wed, 20 Sep 2023 11:45:37 -0400 Subject: [PATCH 094/105] Sync it framework (#28541) * Sync it framework * spotless and checkstyle; exclude not-sync file * change to json extension to bypass RAT check --- .../cassandra/matchers/CassandraAsserts.java | 2 +- .../beam/it/common/utils/PipelineUtils.java | 21 +++++++- .../it/common/utils/PipelineUtilsTest.java | 8 ++- .../elasticsearch/ElasticsearchUtilsTest.java | 2 +- .../org/apache/beam/it/gcp/LoadTestBase.java | 53 ++++++++++++------ .../gcp/bigquery/BigQueryResourceManager.java | 1 + .../gcp/bigtable/BigtableResourceManager.java | 33 ++++++++++++ .../BigtableResourceManagerUtils.java | 2 +- .../dataflow/AbstractPipelineLauncher.java | 13 ++++- .../gcp/dataflow/DefaultPipelineLauncher.java | 6 +-- .../it/gcp/dataflow/DirectRunnerClient.java | 6 +-- .../it/gcp/datagenerator/DataGenerator.java | 54 ++++++++++--------- .../datastore/matchers/DatastoreAsserts.java | 3 +- .../beam/it/gcp/dlp/DlpResourceManager.java | 5 +- .../beam/it/gcp/kms/KMSResourceManager.java | 5 +- .../it/gcp/monitoring/MonitoringClient.java | 12 ++--- .../it/gcp/pubsub/PubsubResourceManager.java | 45 ++++++++++++---- .../src/main/resources/test-artifact.json | 1 + .../beam/it/gcp/bigquery/BigQueryIOLT.java | 18 ++++--- .../beam/it/gcp/bigtable/BigTableIOLT.java | 22 ++++---- .../bigtable/BigtableResourceManagerTest.java | 1 + .../dataflow/ClassicTemplateClientTest.java | 14 ++++- .../gcp/dataflow/FlexTemplateClientTest.java | 14 ++++- .../beam/it/gcp/storage/FileBasedIOLT.java | 14 ++--- .../gcp/storage/GcsResourceManagerTest.java | 2 +- .../src/test/resources/test-artifact.txt | 1 - .../beam/it/jdbc/MSSQLResourceManager.java | 7 +-- .../beam/it/jdbc/MySQLResourceManager.java | 4 +- .../beam/it/jdbc/OracleResourceManager.java | 8 +-- .../beam/it/jdbc/PostgresResourceManager.java | 9 ++-- .../beam/it/kafka/KafkaResourceManager.java | 11 ++-- .../org/apache/beam/it/kafka/KafkaIOLT.java | 4 +- .../it/mongodb/MongoDBResourceManager.java | 10 ++-- .../it/mongodb/matchers/MongoDBAsserts.java | 2 +- .../beam/it/neo4j/Neo4jResourceManager.java | 11 ++-- .../beam/it/splunk/SplunkResourceManager.java | 10 ++-- .../TestContainerResourceManager.java | 15 +++--- .../it/truthmatchers/LaunchInfoSubject.java | 2 +- .../beam/it/truthmatchers/RecordsSubject.java | 2 +- 39 files changed, 303 insertions(+), 150 deletions(-) create mode 100644 it/google-cloud-platform/src/main/resources/test-artifact.json delete mode 100644 it/google-cloud-platform/src/test/resources/test-artifact.txt diff --git a/it/cassandra/src/main/java/org/apache/beam/it/cassandra/matchers/CassandraAsserts.java b/it/cassandra/src/main/java/org/apache/beam/it/cassandra/matchers/CassandraAsserts.java index 61f730bf3579..6aecc6609cfb 100644 --- a/it/cassandra/src/main/java/org/apache/beam/it/cassandra/matchers/CassandraAsserts.java +++ b/it/cassandra/src/main/java/org/apache/beam/it/cassandra/matchers/CassandraAsserts.java @@ -31,7 +31,7 @@ public class CassandraAsserts { /** - * Convert Cassandra {@link Row} list to a list of maps. + * Convert Cassandra {@link com.datastax.oss.driver.api.core.cql.Row} list to a list of maps. * * @param rows Rows to parse. * @return List of maps to use in {@link RecordsSubject}. diff --git a/it/common/src/main/java/org/apache/beam/it/common/utils/PipelineUtils.java b/it/common/src/main/java/org/apache/beam/it/common/utils/PipelineUtils.java index c696457bbdd9..d249d43d3789 100644 --- a/it/common/src/main/java/org/apache/beam/it/common/utils/PipelineUtils.java +++ b/it/common/src/main/java/org/apache/beam/it/common/utils/PipelineUtils.java @@ -27,6 +27,7 @@ import org.apache.beam.sdk.options.PipelineOptions; import org.apache.beam.sdk.options.PipelineOptionsFactory; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.CaseFormat; +import org.apache.commons.lang3.RandomStringUtils; /** Utilities to make working with Dataflow easier. */ public class PipelineUtils { @@ -73,6 +74,15 @@ public static boolean waitUntil( } } + /** + * Creates a job name. Method uses {@link #createJobName(String, int)}} without a random suffix. + * + * @see #createJobName(String, int) + */ + public static String createJobName(String prefix) { + return createJobName(prefix, 0); + } + /** * Creates a job name. * @@ -83,17 +93,24 @@ public static boolean waitUntil( * same prefix are requested in a short period of time. * * @param prefix a prefix for the job + * @param randomChars if the string should contain random chars at the end, to increase the + * likelihood of being unique. * @return the prefix plus some way of identifying it separate from other jobs with the same * prefix */ - public static String createJobName(String prefix) { + public static String createJobName(String prefix, int randomChars) { String convertedPrefix = CaseFormat.UPPER_CAMEL.converterTo(CaseFormat.LOWER_HYPHEN).convert(prefix); String formattedTimestamp = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS") .withZone(ZoneId.of("UTC")) .format(Instant.now()); - return String.format("%s-%s", convertedPrefix, formattedTimestamp); + + String suffix = ""; + if (randomChars > 0) { + suffix = "-" + RandomStringUtils.randomAlphanumeric(randomChars).toLowerCase(); + } + return String.format("%s-%s%s", convertedPrefix, formattedTimestamp, suffix); } /** Get raw job name (without prefix) from a jobName generated by createJobName. */ diff --git a/it/common/src/test/java/org/apache/beam/it/common/utils/PipelineUtilsTest.java b/it/common/src/test/java/org/apache/beam/it/common/utils/PipelineUtilsTest.java index acf203b06e6e..316283cdf7d2 100644 --- a/it/common/src/test/java/org/apache/beam/it/common/utils/PipelineUtilsTest.java +++ b/it/common/src/test/java/org/apache/beam/it/common/utils/PipelineUtilsTest.java @@ -37,7 +37,13 @@ public void testCreateJobName() { @Test public void testCreateJobNameWithUppercase() { - assertThat(createJobName("testWithUpperCase")).matches("test-with-upper-case" + "-\\d{17}"); + assertThat(createJobName("testWithUpperCase")).matches("test-with-upper-case-\\d{17}"); + } + + @Test + public void testCreateJobNameWithUppercaseSuffix() { + assertThat(createJobName("testWithUpperCase", 8)) + .matches("test-with-upper-case-\\d{17}-[a-z0-9]{8}"); } @Test diff --git a/it/elasticsearch/src/test/java/org/apache/beam/it/elasticsearch/ElasticsearchUtilsTest.java b/it/elasticsearch/src/test/java/org/apache/beam/it/elasticsearch/ElasticsearchUtilsTest.java index eb250a1c5f82..61d6b5d57c2c 100644 --- a/it/elasticsearch/src/test/java/org/apache/beam/it/elasticsearch/ElasticsearchUtilsTest.java +++ b/it/elasticsearch/src/test/java/org/apache/beam/it/elasticsearch/ElasticsearchUtilsTest.java @@ -34,7 +34,7 @@ public class ElasticsearchUtilsTest { @Test public void testGenerateIndexNameShouldReplaceForwardSlash() { String testBaseString = "Test/DB/Name"; - String actual = generateIndexName(testBaseString); + String actual = ElasticsearchUtils.generateIndexName(testBaseString); assertThat(actual).matches("test-db-name-\\d{8}-\\d{6}-\\d{6}"); } diff --git a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/LoadTestBase.java b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/LoadTestBase.java index f6e359fed963..d9c1990ef079 100644 --- a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/LoadTestBase.java +++ b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/LoadTestBase.java @@ -18,6 +18,7 @@ package org.apache.beam.it.gcp; import static org.apache.beam.it.common.logging.LogStrings.formatForLogging; +import static org.apache.beam.it.gcp.dataflow.AbstractPipelineLauncher.RUNNER_V2; import com.google.api.gax.core.CredentialsProvider; import com.google.api.gax.core.FixedCredentialsProvider; @@ -49,6 +50,7 @@ import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.MoreObjects; import org.junit.After; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Rule; import org.junit.rules.TestRule; import org.junit.rules.TestWatcher; @@ -107,11 +109,14 @@ protected void starting(Description description) { } }; - @Before - @SuppressFBWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD") - public void setUp() throws IOException { + @BeforeClass + public static void setUpClass() { project = TestProperties.project(); region = TestProperties.region(); + } + + @Before + public void setUp() throws IOException { monitoringClient = MonitoringClient.builder(CREDENTIALS_PROVIDER).build(); pipelineLauncher = launcher(); pipelineOperator = new PipelineOperator(pipelineLauncher); @@ -239,7 +244,7 @@ private void computeDataflowMetrics( metrics.put("EstimatedDataProcessedGB", dataProcessed / 1e9d); } metrics.putAll(getCpuUtilizationMetrics(launchInfo.jobId(), workerTimeInterval)); - metrics.putAll(getThroughputMetrics(launchInfo.jobId(), config, workerTimeInterval)); + metrics.putAll(getThroughputMetrics(launchInfo, config, workerTimeInterval)); } /** @@ -349,25 +354,30 @@ protected Map getCpuUtilizationMetrics(String jobId, TimeInterva /** * Computes throughput metrics of the given pcollection in dataflow job. * - * @param jobId dataflow job id + * @param jobInfo dataflow job LaunchInfo * @param config the {@class MetricsConfiguration} * @param timeInterval interval for the monitoring query * @return throughput metrics of the pcollection */ protected Map getThroughputMetrics( - String jobId, MetricsConfiguration config, TimeInterval timeInterval) { + LaunchInfo jobInfo, MetricsConfiguration config, TimeInterval timeInterval) { + String jobId = jobInfo.jobId(); + String iColl = + RUNNER_V2.equals(jobInfo.runner()) + ? config.inputPCollectionV2() + : config.inputPCollection(); + String oColl = + RUNNER_V2.equals(jobInfo.runner()) + ? config.outputPCollectionV2() + : config.outputPCollection(); List inputThroughputBytesPerSec = - monitoringClient.getThroughputBytesPerSecond( - project, jobId, config.inputPCollection(), timeInterval); + monitoringClient.getThroughputBytesPerSecond(project, jobId, iColl, timeInterval); List inputThroughputElementsPerSec = - monitoringClient.getThroughputElementsPerSecond( - project, jobId, config.inputPCollection(), timeInterval); + monitoringClient.getThroughputElementsPerSecond(project, jobId, iColl, timeInterval); List outputThroughputBytesPerSec = - monitoringClient.getThroughputBytesPerSecond( - project, jobId, config.outputPCollection(), timeInterval); + monitoringClient.getThroughputBytesPerSecond(project, jobId, oColl, timeInterval); List outputThroughputElementsPerSec = - monitoringClient.getThroughputElementsPerSecond( - project, jobId, config.outputPCollection(), timeInterval); + monitoringClient.getThroughputElementsPerSecond(project, jobId, oColl, timeInterval); return getThroughputMetrics( inputThroughputBytesPerSec, inputThroughputElementsPerSec, @@ -495,22 +505,31 @@ public abstract static class MetricsConfiguration { */ public abstract @Nullable String inputPCollection(); + /** Input PCollection name under Dataflow runner v2. */ + public abstract @Nullable String inputPCollectionV2(); + /** * Input PCollection of the Dataflow job to query additional metrics. If not provided, the * metrics for inputPCollection will not be calculated. */ public abstract @Nullable String outputPCollection(); - public static Builder builder() { + public abstract @Nullable String outputPCollectionV2(); + + public static MetricsConfiguration.Builder builder() { return new AutoValue_LoadTestBase_MetricsConfiguration.Builder(); } @AutoValue.Builder public abstract static class Builder { - public abstract Builder setInputPCollection(@Nullable String value); + public abstract MetricsConfiguration.Builder setInputPCollection(@Nullable String value); + + public abstract MetricsConfiguration.Builder setInputPCollectionV2(@Nullable String value); + + public abstract MetricsConfiguration.Builder setOutputPCollection(@Nullable String value); - public abstract Builder setOutputPCollection(@Nullable String value); + public abstract MetricsConfiguration.Builder setOutputPCollectionV2(@Nullable String value); public abstract MetricsConfiguration build(); } diff --git a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/bigquery/BigQueryResourceManager.java b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/bigquery/BigQueryResourceManager.java index 80bf5cfd9382..d6d348f524b2 100644 --- a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/bigquery/BigQueryResourceManager.java +++ b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/bigquery/BigQueryResourceManager.java @@ -461,6 +461,7 @@ public synchronized void cleanupAll() throws BigQueryResourceManagerException { projectId, dataset.getDatasetId().getDataset(), table.getTableId().getTable())); } bigQuery.delete(dataset.getDatasetId()); + dataset = null; } } catch (Exception e) { throw new BigQueryResourceManagerException("Failed to delete resources.", e); diff --git a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/bigtable/BigtableResourceManager.java b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/bigtable/BigtableResourceManager.java index 1e6750cc81e4..713880229281 100644 --- a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/bigtable/BigtableResourceManager.java +++ b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/bigtable/BigtableResourceManager.java @@ -33,6 +33,7 @@ import com.google.cloud.bigtable.admin.v2.models.AppProfile.MultiClusterRoutingPolicy; import com.google.cloud.bigtable.admin.v2.models.AppProfile.RoutingPolicy; import com.google.cloud.bigtable.admin.v2.models.AppProfile.SingleClusterRoutingPolicy; +import com.google.cloud.bigtable.admin.v2.models.Cluster; import com.google.cloud.bigtable.admin.v2.models.CreateAppProfileRequest; import com.google.cloud.bigtable.admin.v2.models.CreateInstanceRequest; import com.google.cloud.bigtable.admin.v2.models.CreateTableRequest; @@ -54,6 +55,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import javax.annotation.Nullable; import org.apache.beam.it.common.ResourceManager; import org.apache.commons.lang3.StringUtils; @@ -93,6 +96,8 @@ public class BigtableResourceManager implements ResourceManager { private final Set cdcEnabledTables; private boolean hasInstance; + private Iterable clusters; + private final boolean usingStaticInstance; private BigtableResourceManager(Builder builder) throws IOException { @@ -111,6 +116,7 @@ private BigtableResourceManager(Builder builder) throws IOException { this.createdTables = new ArrayList<>(); this.createdAppProfiles = new ArrayList<>(); this.cdcEnabledTables = new HashSet<>(); + this.clusters = new ArrayList<>(); // Check if RM was configured to use static Bigtable instance. if (builder.useStaticInstance) { @@ -223,6 +229,7 @@ public synchronized void createInstance(Iterable "Failed to create instance " + instanceId + ".", e); } hasInstance = true; + this.clusters = clusters; LOG.info("Successfully created instance {}.", instanceId); } @@ -544,6 +551,32 @@ public synchronized ImmutableList readTable(String tableId, @Nullable Long return tableRows; } + /** Get all the cluster names of the current instance. */ + public List getClusterNames() { + return StreamSupport.stream(getClusters().spliterator(), false) + .map(BigtableResourceManagerCluster::clusterId) + .collect(Collectors.toList()); + } + + private Iterable getClusters() { + if (usingStaticInstance && this.clusters == null) { + try (BigtableInstanceAdminClient instanceAdminClient = + bigtableResourceManagerClientFactory.bigtableInstanceAdminClient()) { + List managedClusters = new ArrayList<>(); + for (Cluster cluster : instanceAdminClient.listClusters(instanceId)) { + managedClusters.add( + BigtableResourceManagerCluster.create( + cluster.getId(), + cluster.getZone(), + cluster.getServeNodes(), + cluster.getStorageType())); + } + this.clusters = managedClusters; + } + } + return this.clusters; + } + /** * Deletes all created resources (instance and tables) and cleans up all Bigtable clients, making * the manager object unusable. diff --git a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/bigtable/BigtableResourceManagerUtils.java b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/bigtable/BigtableResourceManagerUtils.java index eb2323e52974..a893493d766e 100644 --- a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/bigtable/BigtableResourceManagerUtils.java +++ b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/bigtable/BigtableResourceManagerUtils.java @@ -34,7 +34,7 @@ public final class BigtableResourceManagerUtils { private static final Pattern ILLEGAL_INSTANCE_ID_CHARS = Pattern.compile("[^a-z0-9-]"); private static final String REPLACE_INSTANCE_ID_CHAR = "-"; private static final int MIN_TABLE_ID_LENGTH = 1; - private static final int MAX_TABLE_ID_LENGTH = 30; + private static final int MAX_TABLE_ID_LENGTH = 40; private static final Pattern ILLEGAL_TABLE_CHARS = Pattern.compile("[^a-zA-Z0-9-_.]"); private static final String REPLACE_TABLE_ID_CHAR = "-"; diff --git a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/dataflow/AbstractPipelineLauncher.java b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/dataflow/AbstractPipelineLauncher.java index 08688d88b104..b5c9535953b7 100644 --- a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/dataflow/AbstractPipelineLauncher.java +++ b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/dataflow/AbstractPipelineLauncher.java @@ -58,6 +58,11 @@ public abstract class AbstractPipelineLauncher implements PipelineLauncher { private static final Logger LOG = LoggerFactory.getLogger(AbstractPipelineLauncher.class); private static final Pattern CURRENT_METRICS = Pattern.compile(".*Current.*"); + public static final String LEGACY_RUNNER = "Dataflow Legacy Runner"; + public static final String RUNNER_V2 = "Dataflow Runner V2"; + public static final String PARAM_RUNNER = "runner"; + public static final String PARAM_JOB_TYPE = "jobType"; + public static final String PARAM_JOB_ID = "jobId"; protected final List launchedJobs = new ArrayList<>(); @@ -244,12 +249,12 @@ protected JobState handleJobState(Job job) { */ protected LaunchInfo.Builder getJobInfoBuilder(LaunchConfig options, JobState state, Job job) { Map labels = job.getLabels(); - String runner = "Dataflow Legacy Runner"; + String runner = LEGACY_RUNNER; Environment environment = job.getEnvironment(); if (environment != null && environment.getExperiments() != null && environment.getExperiments().contains("use_runner_v2")) { - runner = "Dataflow Runner V2"; + runner = RUNNER_V2; } LaunchInfo.Builder builder = LaunchInfo.builder() @@ -266,6 +271,10 @@ protected LaunchInfo.Builder getJobInfoBuilder(LaunchConfig options, JobState st // tests Map parameters = new HashMap<>(options.parameters()); options.environment().forEach((key, val) -> parameters.put(key, val.toString())); + // attach basic job info to parameters so that these are exported for load tests + parameters.put(PARAM_RUNNER, runner); + parameters.put(PARAM_JOB_TYPE, job.getType()); + parameters.put(PARAM_JOB_ID, job.getId()); builder.setParameters(ImmutableMap.copyOf(parameters)); if (labels != null && !labels.isEmpty()) { // template job diff --git a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/dataflow/DefaultPipelineLauncher.java b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/dataflow/DefaultPipelineLauncher.java index 7918dd6227d9..ad2dcafc007b 100644 --- a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/dataflow/DefaultPipelineLauncher.java +++ b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/dataflow/DefaultPipelineLauncher.java @@ -99,7 +99,7 @@ public class DefaultPipelineLauncher extends AbstractPipelineLauncher { .put(PipelineResult.State.UNRECOGNIZED, JobState.UNKNOWN) .build(); - private DefaultPipelineLauncher(Builder builder) { + private DefaultPipelineLauncher(DefaultPipelineLauncher.Builder builder) { super( new Dataflow( Utils.getDefaultTransport(), @@ -109,8 +109,8 @@ private DefaultPipelineLauncher(Builder builder) { : new HttpCredentialsAdapter(builder.getCredentials()))); } - public static Builder builder(Credentials credentials) { - return new Builder(credentials); + public static DefaultPipelineLauncher.Builder builder(Credentials credentials) { + return new DefaultPipelineLauncher.Builder(credentials); } @Override diff --git a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/dataflow/DirectRunnerClient.java b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/dataflow/DirectRunnerClient.java index 8017009ff378..57f8ad40c1b6 100644 --- a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/dataflow/DirectRunnerClient.java +++ b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/dataflow/DirectRunnerClient.java @@ -53,8 +53,8 @@ public class DirectRunnerClient implements PipelineLauncher { this.mainClass = builder.getMainClass(); } - public static Builder builder(Class mainClass) { - return new Builder(mainClass); + public static DirectRunnerClient.Builder builder(Class mainClass) { + return new DirectRunnerClient.Builder(mainClass); } @Override @@ -172,7 +172,7 @@ public Class getMainClass() { return mainClass; } - public Builder setCredentials(Credentials value) { + public DirectRunnerClient.Builder setCredentials(Credentials value) { credentials = value; return this; } diff --git a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/datagenerator/DataGenerator.java b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/datagenerator/DataGenerator.java index 99016b5dd3a4..832a75defd95 100644 --- a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/datagenerator/DataGenerator.java +++ b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/datagenerator/DataGenerator.java @@ -61,14 +61,16 @@ private DataGenerator(Builder builder) { .build(); } - public static Builder builderWithSchemaLocation(String testName, String schemaLocation) { - return new Builder(testName + "-data-generator") + public static DataGenerator.Builder builderWithSchemaLocation( + String testName, String schemaLocation) { + return new DataGenerator.Builder(testName + "-data-generator") .setSchemaLocation(schemaLocation) .setAutoscalingAlgorithm(AutoscalingAlgorithmType.THROUGHPUT_BASED); } - public static Builder builderWithSchemaTemplate(String testName, String schemaTemplate) { - return new Builder(testName + "-data-generator") + public static DataGenerator.Builder builderWithSchemaTemplate( + String testName, String schemaTemplate) { + return new DataGenerator.Builder(testName + "-data-generator") .setSchemaTemplate(schemaTemplate) .setAutoscalingAlgorithm(AutoscalingAlgorithmType.THROUGHPUT_BASED); } @@ -129,27 +131,27 @@ public Map getParameters() { return parameters; } - public Builder setSchemaTemplate(String value) { + public DataGenerator.Builder setSchemaTemplate(String value) { parameters.put("schemaTemplate", value); return this; } - public Builder setSchemaLocation(String value) { + public DataGenerator.Builder setSchemaLocation(String value) { parameters.put("schemaLocation", value); return this; } - public Builder setMessagesLimit(String value) { + public DataGenerator.Builder setMessagesLimit(String value) { parameters.put(MESSAGES_LIMIT, value); return this; } - public Builder setQPS(String value) { + public DataGenerator.Builder setQPS(String value) { parameters.put("qps", value); return this; } - public Builder setSinkType(String value) { + public DataGenerator.Builder setSinkType(String value) { parameters.put("sinkType", value); return this; } @@ -164,87 +166,87 @@ public Builder setNumWorkers(String value) { return this; } - public Builder setMaxNumWorkers(String value) { + public DataGenerator.Builder setMaxNumWorkers(String value) { parameters.put("maxNumWorkers", value); return this; } - public Builder setAutoscalingAlgorithm(AutoscalingAlgorithmType value) { + public DataGenerator.Builder setAutoscalingAlgorithm(AutoscalingAlgorithmType value) { parameters.put("autoscalingAlgorithm", value.toString()); return this; } - public Builder setOutputDirectory(String value) { + public DataGenerator.Builder setOutputDirectory(String value) { parameters.put("outputDirectory", value); return this; } - public Builder setOutputType(String value) { + public DataGenerator.Builder setOutputType(String value) { parameters.put("outputType", value); return this; } - public Builder setNumShards(String value) { + public DataGenerator.Builder setNumShards(String value) { parameters.put("numShards", value); return this; } - public Builder setAvroSchemaLocation(String value) { + public DataGenerator.Builder setAvroSchemaLocation(String value) { parameters.put("avroSchemaLocation", value); return this; } - public Builder setTopic(String value) { + public DataGenerator.Builder setTopic(String value) { parameters.put("topic", value); return this; } - public Builder setProjectId(String value) { + public DataGenerator.Builder setProjectId(String value) { parameters.put("projectId", value); return this; } - public Builder setSpannerInstanceName(String value) { + public DataGenerator.Builder setSpannerInstanceName(String value) { parameters.put("spannerInstanceName", value); return this; } - public Builder setSpannerDatabaseName(String value) { + public DataGenerator.Builder setSpannerDatabaseName(String value) { parameters.put("spannerDatabaseName", value); return this; } - public Builder setSpannerTableName(String value) { + public DataGenerator.Builder setSpannerTableName(String value) { parameters.put("spannerTableName", value); return this; } - public Builder setDriverClassName(String value) { + public DataGenerator.Builder setDriverClassName(String value) { parameters.put("driverClassName", value); return this; } - public Builder setConnectionUrl(String value) { + public DataGenerator.Builder setConnectionUrl(String value) { parameters.put("connectionUrl", value); return this; } - public Builder setUsername(String value) { + public DataGenerator.Builder setUsername(String value) { parameters.put("username", value); return this; } - public Builder setPassword(String value) { + public DataGenerator.Builder setPassword(String value) { parameters.put("password", value); return this; } - public Builder setConnectionProperties(String value) { + public DataGenerator.Builder setConnectionProperties(String value) { parameters.put("connectionProperties", value); return this; } - public Builder setStatement(String value) { + public DataGenerator.Builder setStatement(String value) { parameters.put("statement", value); return this; } diff --git a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/datastore/matchers/DatastoreAsserts.java b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/datastore/matchers/DatastoreAsserts.java index ef67a5a5c4fb..78fa7543150f 100644 --- a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/datastore/matchers/DatastoreAsserts.java +++ b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/datastore/matchers/DatastoreAsserts.java @@ -61,7 +61,8 @@ public static List> datastoreResultsToRecords(Collection results) { diff --git a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/dlp/DlpResourceManager.java b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/dlp/DlpResourceManager.java index f59794af3e1f..de818a1bbff1 100644 --- a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/dlp/DlpResourceManager.java +++ b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/dlp/DlpResourceManager.java @@ -113,8 +113,9 @@ public void cleanupAll() { * @param project the GCP project ID * @return a new instance of Builder */ - public static Builder builder(String project, CredentialsProvider credentialsProvider) { - return new Builder(project, credentialsProvider); + public static DlpResourceManager.Builder builder( + String project, CredentialsProvider credentialsProvider) { + return new DlpResourceManager.Builder(project, credentialsProvider); } /** A builder class for creating instances of {@link DlpResourceManager}. */ diff --git a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/kms/KMSResourceManager.java b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/kms/KMSResourceManager.java index 7e1a403c7352..2cad6d0b9fab 100644 --- a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/kms/KMSResourceManager.java +++ b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/kms/KMSResourceManager.java @@ -72,8 +72,9 @@ private KMSResourceManager(Builder builder) { this.keyRing = null; } - public static Builder builder(String projectId, CredentialsProvider credentialsProvider) { - return new Builder(projectId, credentialsProvider); + public static KMSResourceManager.Builder builder( + String projectId, CredentialsProvider credentialsProvider) { + return new KMSResourceManager.Builder(projectId, credentialsProvider); } /** diff --git a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/monitoring/MonitoringClient.java b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/monitoring/MonitoringClient.java index 0fc5614a3630..06591ea4fe0a 100644 --- a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/monitoring/MonitoringClient.java +++ b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/monitoring/MonitoringClient.java @@ -150,8 +150,8 @@ public List listTimeSeriesAsLong(ListTimeSeriesRequest request) { Aggregation aggregation = Aggregation.newBuilder() .setAlignmentPeriod(Duration.newBuilder().setSeconds(60).build()) - .setPerSeriesAligner(Aligner.ALIGN_MEAN) - .setCrossSeriesReducer(Reducer.REDUCE_MEAN) + .setPerSeriesAligner(Aggregation.Aligner.ALIGN_MEAN) + .setCrossSeriesReducer(Aggregation.Reducer.REDUCE_MEAN) .addGroupByFields("resource.instance_id") .build(); ListTimeSeriesRequest request = @@ -188,7 +188,7 @@ public List listTimeSeriesAsLong(ListTimeSeriesRequest request) { Aggregation aggregation = Aggregation.newBuilder() .setAlignmentPeriod(Duration.newBuilder().setSeconds(60).build()) - .setPerSeriesAligner(Aligner.ALIGN_MEAN) + .setPerSeriesAligner(Aggregation.Aligner.ALIGN_MEAN) .setCrossSeriesReducer(Reducer.REDUCE_MAX) .build(); ListTimeSeriesRequest request = @@ -225,7 +225,7 @@ public List listTimeSeriesAsLong(ListTimeSeriesRequest request) { Aggregation aggregation = Aggregation.newBuilder() .setAlignmentPeriod(Duration.newBuilder().setSeconds(60).build()) - .setPerSeriesAligner(Aligner.ALIGN_MEAN) + .setPerSeriesAligner(Aggregation.Aligner.ALIGN_MEAN) .setCrossSeriesReducer(Reducer.REDUCE_MAX) .build(); ListTimeSeriesRequest request = @@ -269,7 +269,7 @@ public List listTimeSeriesAsLong(ListTimeSeriesRequest request) { Aggregation aggregation = Aggregation.newBuilder() .setAlignmentPeriod(Duration.newBuilder().setSeconds(60).build()) - .setPerSeriesAligner(Aligner.ALIGN_RATE) + .setPerSeriesAligner(Aggregation.Aligner.ALIGN_RATE) .build(); ListTimeSeriesRequest request = ListTimeSeriesRequest.newBuilder() @@ -312,7 +312,7 @@ public List listTimeSeriesAsLong(ListTimeSeriesRequest request) { Aggregation aggregation = Aggregation.newBuilder() .setAlignmentPeriod(Duration.newBuilder().setSeconds(60).build()) - .setPerSeriesAligner(Aligner.ALIGN_RATE) + .setPerSeriesAligner(Aggregation.Aligner.ALIGN_RATE) .build(); ListTimeSeriesRequest request = ListTimeSeriesRequest.newBuilder() diff --git a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/pubsub/PubsubResourceManager.java b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/pubsub/PubsubResourceManager.java index 3a684d34c045..738620c15b7e 100644 --- a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/pubsub/PubsubResourceManager.java +++ b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/pubsub/PubsubResourceManager.java @@ -20,6 +20,7 @@ import static org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkArgument; import com.google.api.gax.core.CredentialsProvider; +import com.google.api.gax.rpc.DeadlineExceededException; import com.google.cloud.pubsub.v1.Publisher; import com.google.cloud.pubsub.v1.SchemaServiceClient; import com.google.cloud.pubsub.v1.SchemaServiceSettings; @@ -42,12 +43,16 @@ import com.google.pubsub.v1.Topic; import com.google.pubsub.v1.TopicName; import com.google.pubsub.v1.UpdateTopicRequest; +import dev.failsafe.Failsafe; +import dev.failsafe.RetryPolicy; import java.io.IOException; +import java.time.Duration; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.beam.it.common.ResourceManager; +import org.apache.beam.it.common.utils.ExceptionUtils; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.annotations.VisibleForTesting; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Strings; import org.slf4j.Logger; @@ -66,6 +71,12 @@ public final class PubsubResourceManager implements ResourceManager { private static final int DEFAULT_ACK_DEADLINE_SECONDS = 600; private static final String RESOURCE_NAME_SEPARATOR = "-"; + // Retry settings for client operations + private static final int FAILSAFE_MAX_RETRIES = 5; + private static final Duration FAILSAFE_RETRY_DELAY = Duration.ofSeconds(10); + private static final Duration FAILSAFE_RETRY_MAX_DELAY = Duration.ofSeconds(60); + private static final double FAILSAFE_RETRY_JITTER = 0.1; + private final String testId; private final String projectId; private final PubsubPublisherFactory publisherFactory; @@ -184,11 +195,14 @@ public SubscriptionName createSubscription(TopicName topicName, String subscript LOG.info("Creating subscription '{}' for topic '{}'", subscriptionName, topicName); Subscription subscription = - subscriptionAdminClient.createSubscription( - getSubscriptionName(subscriptionName), - topicName, - PushConfig.getDefaultInstance(), - DEFAULT_ACK_DEADLINE_SECONDS); + Failsafe.with(retryOnDeadlineExceeded()) + .get( + () -> + subscriptionAdminClient.createSubscription( + getSubscriptionName(subscriptionName), + topicName, + PushConfig.getDefaultInstance(), + DEFAULT_ACK_DEADLINE_SECONDS)); SubscriptionName reference = PubsubUtils.toSubscriptionName(subscription); createdSubscriptions.add(getSubscriptionName(subscriptionName)); @@ -299,17 +313,19 @@ public synchronized void cleanupAll() { try { for (SubscriptionName subscription : createdSubscriptions) { LOG.info("Deleting subscription '{}'", subscription); - subscriptionAdminClient.deleteSubscription(subscription); + Failsafe.with(retryOnDeadlineExceeded()) + .run(() -> subscriptionAdminClient.deleteSubscription(subscription)); } for (TopicName topic : createdTopics) { LOG.info("Deleting topic '{}'", topic); - topicAdminClient.deleteTopic(topic); + Failsafe.with(retryOnDeadlineExceeded()).run(() -> topicAdminClient.deleteTopic(topic)); } for (SchemaName schemaName : createdSchemas) { LOG.info("Deleting schema '{}'", schemaName); - schemaServiceClient.deleteSchema(schemaName); + Failsafe.with(retryOnDeadlineExceeded()) + .run(() -> schemaServiceClient.deleteSchema(schemaName)); } } finally { subscriptionAdminClient.close(); @@ -342,7 +358,8 @@ private void checkIsUsable() throws IllegalStateException { private TopicName createTopicInternal(TopicName topicName) { LOG.info("Creating topic '{}'...", topicName.toString()); - Topic topic = topicAdminClient.createTopic(topicName); + Topic topic = + Failsafe.with(retryOnDeadlineExceeded()).get(() -> topicAdminClient.createTopic(topicName)); TopicName reference = PubsubUtils.toTopicName(topic); createdTopics.add(reference); @@ -355,6 +372,16 @@ private boolean isNotUsable() { return topicAdminClient.isShutdown() || subscriptionAdminClient.isShutdown(); } + private static RetryPolicy retryOnDeadlineExceeded() { + return RetryPolicy.builder() + .handleIf( + exception -> ExceptionUtils.containsType(exception, DeadlineExceededException.class)) + .withMaxRetries(FAILSAFE_MAX_RETRIES) + .withBackoff(FAILSAFE_RETRY_DELAY, FAILSAFE_RETRY_MAX_DELAY) + .withJitter(FAILSAFE_RETRY_JITTER) + .build(); + } + /** Builder for {@link PubsubResourceManager}. */ public static final class Builder { diff --git a/it/google-cloud-platform/src/main/resources/test-artifact.json b/it/google-cloud-platform/src/main/resources/test-artifact.json new file mode 100644 index 000000000000..551c80d14a66 --- /dev/null +++ b/it/google-cloud-platform/src/main/resources/test-artifact.json @@ -0,0 +1 @@ +["This is a test artifact."] \ No newline at end of file diff --git a/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/bigquery/BigQueryIOLT.java b/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/bigquery/BigQueryIOLT.java index 03f6e8abfd41..a9ae68142778 100644 --- a/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/bigquery/BigQueryIOLT.java +++ b/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/bigquery/BigQueryIOLT.java @@ -99,12 +99,8 @@ public final class BigQueryIOLT extends IOLoadTestBase { private static final String READ_ELEMENT_METRIC_NAME = "read_count"; private Configuration configuration; private String tempLocation; - private TableSchema schema; - private static final String READ_PCOLLECTION = "Counting element.out0"; - private static final String WRITE_PCOLLECTION = "Map records.out0"; - @Rule public TestPipeline writePipeline = TestPipeline.create(); @Rule public TestPipeline readPipeline = TestPipeline.create(); @@ -268,7 +264,7 @@ private void testWrite(BigQueryIO.Write writeIO) throws IOException { .withCustomGcsTempLocation(ValueProvider.StaticValueProvider.of(tempLocation))); PipelineLauncher.LaunchConfig options = - PipelineLauncher.LaunchConfig.builder("test-bigquery-write") + PipelineLauncher.LaunchConfig.builder("write-bigquery") .setSdk(PipelineLauncher.Sdk.JAVA) .setPipeline(writePipeline) .addParameter("runner", configuration.runner) @@ -284,7 +280,10 @@ private void testWrite(BigQueryIO.Write writeIO) throws IOException { // export metrics MetricsConfiguration metricsConfig = - MetricsConfiguration.builder().setInputPCollection(WRITE_PCOLLECTION).build(); + MetricsConfiguration.builder() + .setInputPCollection("Map records.out0") + .setInputPCollectionV2("Map records/ParMultiDo(MapKVToV).out0") + .build(); try { exportMetricsToBigQuery(launchInfo, getMetrics(launchInfo, metricsConfig)); } catch (ParseException | InterruptedException e) { @@ -301,7 +300,7 @@ private void testRead() throws IOException { .apply("Counting element", ParDo.of(new CountingFn<>(READ_ELEMENT_METRIC_NAME))); PipelineLauncher.LaunchConfig options = - PipelineLauncher.LaunchConfig.builder("test-bigquery-read") + PipelineLauncher.LaunchConfig.builder("read-bigquery") .setSdk(PipelineLauncher.Sdk.JAVA) .setPipeline(readPipeline) .addParameter("runner", configuration.runner) @@ -326,7 +325,10 @@ private void testRead() throws IOException { // export metrics MetricsConfiguration metricsConfig = - MetricsConfiguration.builder().setOutputPCollection(READ_PCOLLECTION).build(); + MetricsConfiguration.builder() + .setOutputPCollection("Counting element.out0") + .setOutputPCollectionV2("Counting element/ParMultiDo(Counting).out0") + .build(); try { exportMetricsToBigQuery(launchInfo, getMetrics(launchInfo, metricsConfig)); } catch (ParseException | InterruptedException e) { diff --git a/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/bigtable/BigTableIOLT.java b/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/bigtable/BigTableIOLT.java index fc7bd87707fc..e232ed31cb5a 100644 --- a/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/bigtable/BigTableIOLT.java +++ b/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/bigtable/BigTableIOLT.java @@ -115,8 +115,6 @@ public void teardown() { /** Run integration test with configurations specified by TestProperties. */ @Test public void testWriteAndRead() throws IOException { - final String readPCollection = "Counting element.out0"; - final String writePCollection = "Map records.out0"; tableId = generateTableId(testName); resourceManager.createTable( @@ -149,8 +147,10 @@ public void testWriteAndRead() throws IOException { // export metrics MetricsConfiguration metricsConfig = MetricsConfiguration.builder() - .setInputPCollection(writePCollection) - .setOutputPCollection(readPCollection) + .setInputPCollection("Map records.out0") + .setInputPCollectionV2("Map records/ParMultiDo(MapToBigTableFormat).out0") + .setOutputPCollection("Counting element.out0") + .setOutputPCollectionV2("Counting element/ParMultiDo(Counting).out0") .build(); try { exportMetricsToBigQuery(writeInfo, getMetrics(writeInfo, metricsConfig)); @@ -174,7 +174,7 @@ private PipelineLauncher.LaunchInfo testWrite() throws IOException { .apply("Write to BigTable", writeIO); PipelineLauncher.LaunchConfig options = - PipelineLauncher.LaunchConfig.builder("test-bigtable-write") + PipelineLauncher.LaunchConfig.builder("write-bigtable") .setSdk(PipelineLauncher.Sdk.JAVA) .setPipeline(writePipeline) .addParameter("runner", configuration.getRunner()) @@ -196,7 +196,7 @@ private PipelineLauncher.LaunchInfo testRead() throws IOException { .apply("Counting element", ParDo.of(new CountingFn<>(READ_ELEMENT_METRIC_NAME))); PipelineLauncher.LaunchConfig options = - PipelineLauncher.LaunchConfig.builder("test-bigtable-read") + PipelineLauncher.LaunchConfig.builder("read-bigtable") .setSdk(PipelineLauncher.Sdk.JAVA) .setPipeline(readPipeline) .addParameter("runner", configuration.getRunner()) @@ -227,18 +227,18 @@ static Configuration of(long numRows, int pipelineTimeout, String runner, int va @AutoValue.Builder abstract static class Builder { - abstract Builder setNumRows(long numRows); + abstract Configuration.Builder setNumRows(long numRows); - abstract Builder setPipelineTimeout(int timeOutMinutes); + abstract Configuration.Builder setPipelineTimeout(int timeOutMinutes); - abstract Builder setRunner(String runner); + abstract Configuration.Builder setRunner(String runner); - abstract Builder setValueSizeBytes(int valueSizeBytes); + abstract Configuration.Builder setValueSizeBytes(int valueSizeBytes); abstract Configuration build(); } - abstract Builder toBuilder(); + abstract Configuration.Builder toBuilder(); } /** Maps long number to the BigTable format record. */ diff --git a/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/bigtable/BigtableResourceManagerTest.java b/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/bigtable/BigtableResourceManagerTest.java index 65745aea49be..f8673ed696cc 100644 --- a/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/bigtable/BigtableResourceManagerTest.java +++ b/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/bigtable/BigtableResourceManagerTest.java @@ -442,6 +442,7 @@ public void testCleanupAllShouldWorkWhenBigtableDoesNotThrowAnyError() { setupReadyTable(); testManager.createTable(TABLE_ID, ImmutableList.of("cf1")); + when(bigtableResourceManagerClientFactory.bigtableTableAdminClient().exists(anyString())) .thenReturn(true); testManager.readTable(TABLE_ID); diff --git a/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/dataflow/ClassicTemplateClientTest.java b/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/dataflow/ClassicTemplateClientTest.java index cfd56e596e52..88c35589f2be 100644 --- a/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/dataflow/ClassicTemplateClientTest.java +++ b/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/dataflow/ClassicTemplateClientTest.java @@ -18,6 +18,10 @@ package org.apache.beam.it.gcp.dataflow; import static com.google.common.truth.Truth.assertThat; +import static org.apache.beam.it.gcp.dataflow.AbstractPipelineLauncher.LEGACY_RUNNER; +import static org.apache.beam.it.gcp.dataflow.AbstractPipelineLauncher.PARAM_JOB_ID; +import static org.apache.beam.it.gcp.dataflow.AbstractPipelineLauncher.PARAM_JOB_TYPE; +import static org.apache.beam.it.gcp.dataflow.AbstractPipelineLauncher.PARAM_RUNNER; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -153,8 +157,14 @@ public void testLaunchNewJob() throws IOException { .setSdk("Apache Beam Java") .setVersion("2.42.0") .setJobType("JOB_TYPE_BATCH") - .setRunner("Dataflow Legacy Runner") - .setParameters(ImmutableMap.of(PARAM_KEY, PARAM_VALUE)) + .setRunner(AbstractPipelineLauncher.LEGACY_RUNNER) + .setParameters( + ImmutableMap.builder() + .put(PARAM_KEY, PARAM_VALUE) + .put(PARAM_JOB_ID, JOB_ID) + .put(PARAM_RUNNER, LEGACY_RUNNER) + .put(PARAM_JOB_TYPE, "JOB_TYPE_BATCH") + .build()) .build(); assertThat(actual).isEqualTo(expected); } diff --git a/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/dataflow/FlexTemplateClientTest.java b/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/dataflow/FlexTemplateClientTest.java index 4088efe67514..06f44437414a 100644 --- a/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/dataflow/FlexTemplateClientTest.java +++ b/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/dataflow/FlexTemplateClientTest.java @@ -18,6 +18,10 @@ package org.apache.beam.it.gcp.dataflow; import static com.google.common.truth.Truth.assertThat; +import static org.apache.beam.it.gcp.dataflow.AbstractPipelineLauncher.LEGACY_RUNNER; +import static org.apache.beam.it.gcp.dataflow.AbstractPipelineLauncher.PARAM_JOB_ID; +import static org.apache.beam.it.gcp.dataflow.AbstractPipelineLauncher.PARAM_JOB_TYPE; +import static org.apache.beam.it.gcp.dataflow.AbstractPipelineLauncher.PARAM_RUNNER; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -158,8 +162,14 @@ public void testLaunchNewJob() throws IOException { .setSdk("Apache Beam Java") .setVersion("2.42.0") .setJobType("JOB_TYPE_BATCH") - .setRunner("Dataflow Legacy Runner") - .setParameters(ImmutableMap.of(PARAM_KEY, PARAM_VALUE)) + .setRunner(LEGACY_RUNNER) + .setParameters( + ImmutableMap.builder() + .put(PARAM_KEY, PARAM_VALUE) + .put(PARAM_JOB_ID, JOB_ID) + .put(PARAM_RUNNER, LEGACY_RUNNER) + .put(PARAM_JOB_TYPE, "JOB_TYPE_BATCH") + .build()) .build(); assertThat(actual).isEqualTo(expected); } diff --git a/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/storage/FileBasedIOLT.java b/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/storage/FileBasedIOLT.java index fd1bc1772f2d..704f8337c66f 100644 --- a/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/storage/FileBasedIOLT.java +++ b/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/storage/FileBasedIOLT.java @@ -90,7 +90,7 @@ public class FileBasedIOLT extends IOLoadTestBase { @Rule public TestPipeline readPipeline = TestPipeline.create(); - private static final Map TEST_CONFIGS_PRESET; + private static final Map TEST_CONFIGS_PRESET; static { try { @@ -160,8 +160,6 @@ public void setup() { @Test public void testTextIOWriteThenRead() throws IOException { - final String readPCollection = "Counting element.out0"; - final String writePCollection = "Map records.out0"; TextIO.TypedWrite write = TextIO.write() @@ -182,7 +180,7 @@ public void testTextIOWriteThenRead() throws IOException { .apply("Counting element", ParDo.of(new CountingFn<>(READ_ELEMENT_METRIC_NAME))); PipelineLauncher.LaunchConfig writeOptions = - PipelineLauncher.LaunchConfig.builder("test-textio-write") + PipelineLauncher.LaunchConfig.builder("write-textio") .setSdk(PipelineLauncher.Sdk.JAVA) .setPipeline(writePipeline) .addParameter("runner", configuration.runner) @@ -196,7 +194,7 @@ public void testTextIOWriteThenRead() throws IOException { assertThatResult(writeResult).isLaunchFinished(); PipelineLauncher.LaunchConfig readOptions = - PipelineLauncher.LaunchConfig.builder("test-textio-read") + PipelineLauncher.LaunchConfig.builder("read-textio") .setSdk(PipelineLauncher.Sdk.JAVA) .setPipeline(readPipeline) .addParameter("runner", configuration.runner) @@ -222,8 +220,10 @@ public void testTextIOWriteThenRead() throws IOException { // export metrics MetricsConfiguration metricsConfig = MetricsConfiguration.builder() - .setInputPCollection(writePCollection) - .setOutputPCollection(readPCollection) + .setInputPCollection("Map records.out0") + .setInputPCollectionV2("Map records/ParMultiDo(MapKVToString).out0") + .setOutputPCollection("Counting element.out0") + .setOutputPCollectionV2("Counting element/ParMultiDo(Counting).out0") .build(); try { exportMetricsToBigQuery(writeInfo, getMetrics(writeInfo, metricsConfig)); diff --git a/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/storage/GcsResourceManagerTest.java b/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/storage/GcsResourceManagerTest.java index 3ec96da81007..0153573feaed 100644 --- a/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/storage/GcsResourceManagerTest.java +++ b/it/google-cloud-platform/src/test/java/org/apache/beam/it/gcp/storage/GcsResourceManagerTest.java @@ -71,7 +71,7 @@ public final class GcsResourceManagerTest { @Mock private Blob blob; private GcsResourceManager gcsClient; - private static final String ARTIFACT_NAME = "test-artifact.txt"; + private static final String ARTIFACT_NAME = "test-artifact.json"; private static final Path LOCAL_PATH; private static final byte[] TEST_ARTIFACT_CONTENTS; diff --git a/it/google-cloud-platform/src/test/resources/test-artifact.txt b/it/google-cloud-platform/src/test/resources/test-artifact.txt deleted file mode 100644 index 22c4e1d122a7..000000000000 --- a/it/google-cloud-platform/src/test/resources/test-artifact.txt +++ /dev/null @@ -1 +0,0 @@ -This is a test artifact. \ No newline at end of file diff --git a/it/jdbc/src/main/java/org/apache/beam/it/jdbc/MSSQLResourceManager.java b/it/jdbc/src/main/java/org/apache/beam/it/jdbc/MSSQLResourceManager.java index 0bcb16c61095..c515b2c4844f 100644 --- a/it/jdbc/src/main/java/org/apache/beam/it/jdbc/MSSQLResourceManager.java +++ b/it/jdbc/src/main/java/org/apache/beam/it/jdbc/MSSQLResourceManager.java @@ -61,13 +61,14 @@ private MSSQLResourceManager(Builder builder) { } @VisibleForTesting - > MSSQLResourceManager(T container, Builder builder) { + > MSSQLResourceManager( + T container, Builder builder) { super(container, builder); initialized = true; } - public static Builder builder(String testId) { - return new Builder(testId); + public static MSSQLResourceManager.Builder builder(String testId) { + return new MSSQLResourceManager.Builder(testId); } private synchronized void createDatabase(String databaseName) { diff --git a/it/jdbc/src/main/java/org/apache/beam/it/jdbc/MySQLResourceManager.java b/it/jdbc/src/main/java/org/apache/beam/it/jdbc/MySQLResourceManager.java index e1bf3640b53d..688c26dfb56d 100644 --- a/it/jdbc/src/main/java/org/apache/beam/it/jdbc/MySQLResourceManager.java +++ b/it/jdbc/src/main/java/org/apache/beam/it/jdbc/MySQLResourceManager.java @@ -49,8 +49,8 @@ private MySQLResourceManager(Builder builder) { super(container, builder); } - public static Builder builder(String testId) { - return new Builder(testId); + public static MySQLResourceManager.Builder builder(String testId) { + return new MySQLResourceManager.Builder(testId); } @Override diff --git a/it/jdbc/src/main/java/org/apache/beam/it/jdbc/OracleResourceManager.java b/it/jdbc/src/main/java/org/apache/beam/it/jdbc/OracleResourceManager.java index f44e939936d2..8054d26c33f7 100644 --- a/it/jdbc/src/main/java/org/apache/beam/it/jdbc/OracleResourceManager.java +++ b/it/jdbc/src/main/java/org/apache/beam/it/jdbc/OracleResourceManager.java @@ -45,7 +45,7 @@ public class OracleResourceManager extends AbstractJDBCResourceManager( DockerImageName.parse(builder.containerImageName).withTag(builder.containerImageTag)), @@ -46,12 +46,13 @@ private PostgresResourceManager(Builder builder) { } @VisibleForTesting - PostgresResourceManager(PostgreSQLContainer container, Builder builder) { + PostgresResourceManager( + PostgreSQLContainer container, PostgresResourceManager.Builder builder) { super(container, builder); } - public static Builder builder(String testId) { - return new Builder(testId); + public static PostgresResourceManager.Builder builder(String testId) { + return new PostgresResourceManager.Builder(testId); } @Override diff --git a/it/kafka/src/main/java/org/apache/beam/it/kafka/KafkaResourceManager.java b/it/kafka/src/main/java/org/apache/beam/it/kafka/KafkaResourceManager.java index d9a647dbeebd..7f7fb5b69569 100644 --- a/it/kafka/src/main/java/org/apache/beam/it/kafka/KafkaResourceManager.java +++ b/it/kafka/src/main/java/org/apache/beam/it/kafka/KafkaResourceManager.java @@ -71,13 +71,16 @@ public class KafkaResourceManager extends TestContainerResourceManager 0; @@ -102,8 +105,8 @@ private KafkaResourceManager(Builder builder) { : AdminClient.create(ImmutableMap.of("bootstrap.servers", this.connectionString)); } - public static Builder builder(String testId) { - return new Builder(testId); + public static KafkaResourceManager.Builder builder(String testId) { + return new KafkaResourceManager.Builder(testId); } /** Returns the kafka bootstrap server connection string. */ diff --git a/it/kafka/src/test/java/org/apache/beam/it/kafka/KafkaIOLT.java b/it/kafka/src/test/java/org/apache/beam/it/kafka/KafkaIOLT.java index a03030664de4..ce6ad877c375 100644 --- a/it/kafka/src/test/java/org/apache/beam/it/kafka/KafkaIOLT.java +++ b/it/kafka/src/test/java/org/apache/beam/it/kafka/KafkaIOLT.java @@ -175,7 +175,7 @@ private PipelineLauncher.LaunchInfo testWrite() throws IOException { .apply("Write to Kafka", writeIO.withTopic(kafkaTopic)); PipelineLauncher.LaunchConfig options = - PipelineLauncher.LaunchConfig.builder("test-kafka-write") + PipelineLauncher.LaunchConfig.builder("write-kafka") .setSdk(PipelineLauncher.Sdk.JAVA) .setPipeline(writePipeline) .addParameter("runner", configuration.getRunner()) @@ -195,7 +195,7 @@ private PipelineLauncher.LaunchInfo testRead() throws IOException { .apply("Counting element", ParDo.of(new CountingFn<>(READ_ELEMENT_METRIC_NAME))); PipelineLauncher.LaunchConfig options = - PipelineLauncher.LaunchConfig.builder("test-kafka-read") + PipelineLauncher.LaunchConfig.builder("read-kafka") .setSdk(PipelineLauncher.Sdk.JAVA) .setPipeline(readPipeline) .addParameter("runner", configuration.getRunner()) diff --git a/it/mongodb/src/main/java/org/apache/beam/it/mongodb/MongoDBResourceManager.java b/it/mongodb/src/main/java/org/apache/beam/it/mongodb/MongoDBResourceManager.java index ed0e556bf0df..80216b14ac0e 100644 --- a/it/mongodb/src/main/java/org/apache/beam/it/mongodb/MongoDBResourceManager.java +++ b/it/mongodb/src/main/java/org/apache/beam/it/mongodb/MongoDBResourceManager.java @@ -69,7 +69,7 @@ public class MongoDBResourceManager extends TestContainerResourceManager( @@ -79,7 +79,10 @@ private Neo4jResourceManager(Builder builder) { @VisibleForTesting @SuppressWarnings("nullness") - Neo4jResourceManager(@Nullable Driver neo4jDriver, Neo4jContainer container, Builder builder) { + Neo4jResourceManager( + @Nullable Driver neo4jDriver, + Neo4jContainer container, + Neo4jResourceManager.Builder builder) { super(container, builder); this.adminPassword = builder.adminPassword; @@ -98,8 +101,8 @@ private Neo4jResourceManager(Builder builder) { } } - public static Builder builder(String testId) { - return new Builder(testId); + public static Neo4jResourceManager.Builder builder(String testId) { + return new Neo4jResourceManager.Builder(testId); } /** Returns the URI connection string to the Neo4j Database. */ diff --git a/it/splunk/src/main/java/org/apache/beam/it/splunk/SplunkResourceManager.java b/it/splunk/src/main/java/org/apache/beam/it/splunk/SplunkResourceManager.java index 0115a791eefe..1ef4726df43a 100644 --- a/it/splunk/src/main/java/org/apache/beam/it/splunk/SplunkResourceManager.java +++ b/it/splunk/src/main/java/org/apache/beam/it/splunk/SplunkResourceManager.java @@ -85,7 +85,7 @@ public class SplunkResourceManager extends TestContainerResourceManagerOptionally, a static resource can be specified by calling the useStaticContainer() method in - * the {@link Builder} class. A static resource is a pre-configured database or other resource that - * is ready to be connected to by the resource manager. This could be a pre-existing TestContainer - * that has not been closed, a local database instance, a remote VM, or any other source that can be - * connected to. If a static container is used, the host and port must also be configured using the - * Builder's setHost() and setPort() methods, respectively. + * the {@link TestContainerResourceManager.Builder} class. A static resource is a pre-configured + * database or other resource that is ready to be connected to by the resource manager. This could + * be a pre-existing TestContainer that has not been closed, a local database instance, a remote VM, + * or any other source that can be connected to. If a static container is used, the host and port + * must also be configured using the Builder's setHost() and setPort() methods, respectively. */ public abstract class TestContainerResourceManager> implements ResourceManager { @@ -48,11 +48,12 @@ public abstract class TestContainerResourceManager private final String host; protected int port; - protected > TestContainerResourceManager(T container, B builder) { + protected > TestContainerResourceManager( + T container, B builder) { this(container, builder, null); } - protected > TestContainerResourceManager( + protected > TestContainerResourceManager( T container, B builder, @Nullable Callable setup) { this.container = container; this.usingStaticContainer = builder.useStaticContainer; diff --git a/it/truthmatchers/src/main/java/org/apache/beam/it/truthmatchers/LaunchInfoSubject.java b/it/truthmatchers/src/main/java/org/apache/beam/it/truthmatchers/LaunchInfoSubject.java index a496ecce9448..30a27c9ad259 100644 --- a/it/truthmatchers/src/main/java/org/apache/beam/it/truthmatchers/LaunchInfoSubject.java +++ b/it/truthmatchers/src/main/java/org/apache/beam/it/truthmatchers/LaunchInfoSubject.java @@ -43,7 +43,7 @@ public static Factory launchInfo() { } /** - * Check if the subject reflects succeeded states. A successfully {@link LaunchInfo} does not mean + * Check if the subject reflects succeeded states. A successful {@link LaunchInfo} does not mean * that the pipeline finished and no errors happened, it just means that the job was able to get * itself into an active state (RUNNING, UPDATED). */ diff --git a/it/truthmatchers/src/main/java/org/apache/beam/it/truthmatchers/RecordsSubject.java b/it/truthmatchers/src/main/java/org/apache/beam/it/truthmatchers/RecordsSubject.java index 75d5ce3a67cd..39a0c0cebedc 100644 --- a/it/truthmatchers/src/main/java/org/apache/beam/it/truthmatchers/RecordsSubject.java +++ b/it/truthmatchers/src/main/java/org/apache/beam/it/truthmatchers/RecordsSubject.java @@ -81,7 +81,7 @@ public void hasRecordSubset(Map subset) { Map expected = convertMapToTreeMap(subset); for (Map candidate : actual) { boolean match = true; - for (Entry entry : subset.entrySet()) { + for (Map.Entry entry : subset.entrySet()) { if (!candidate.containsKey(entry.getKey()) || !candidate.get(entry.getKey()).equals(entry.getValue())) { match = false; From 93de970ea664d4219d1658c05f220ee747709e1a Mon Sep 17 00:00:00 2001 From: "gabry.wu" Date: Wed, 20 Sep 2023 23:56:01 +0800 Subject: [PATCH 095/105] add setJoinSubsetType to inject joinSubsetType to BeamSqlSeekableTable (#28477) * add setJoinSubsetType to inject joinSubsetType to BeamSqlSeekableTable * 1. delete setJoinSubsetType 2. move joinSubsetType to setUp method * add comments to Breaking Changes section * Update sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/BeamSqlSeekableTable.java --- CHANGES.md | 1 + .../beam/sdk/extensions/sql/BeamSqlSeekableTable.java | 9 +++++++-- .../sql/impl/transform/BeamJoinTransforms.java | 2 +- .../sql/impl/rel/BeamSideInputLookupJoinRelTest.java | 9 +++++++++ 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index bbe9d539531b..a990a5fd7304 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -75,6 +75,7 @@ * Removed fastjson library dependency for Beam SQL. Table property is changed to be based on jackson ObjectNode (Java) ([#24154](https://github.com/apache/beam/issues/24154)). * Removed TensorFlow from Beam Python container images [PR](https://github.com/apache/beam/pull/28424). If you have been negatively affected by this change, please comment on [#20605](https://github.com/apache/beam/issues/20605). * Removed the parameter `t reflect.Type` from `parquetio.Write`. The element type is derived from the input PCollection (Go) ([#28490](https://github.com/apache/beam/issues/28490)) +* Refactor BeamSqlSeekableTable.setUp adding a parameter joinSubsetType. [#28283](https://github.com/apache/beam/issues/28283) ## Deprecations diff --git a/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/BeamSqlSeekableTable.java b/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/BeamSqlSeekableTable.java index 7b924cf6b6da..4dc9bd5777ff 100644 --- a/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/BeamSqlSeekableTable.java +++ b/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/BeamSqlSeekableTable.java @@ -20,6 +20,7 @@ import java.io.Serializable; import java.util.List; import org.apache.beam.sdk.options.PipelineOptions; +import org.apache.beam.sdk.schemas.Schema; import org.apache.beam.sdk.transforms.DoFn; import org.apache.beam.sdk.values.Row; @@ -28,8 +29,12 @@ * FROM FACT_TABLE JOIN LOOKUP_TABLE ON ...}. */ public interface BeamSqlSeekableTable extends Serializable { - /** prepare the instance. */ - default void setUp() {} + /** + * prepare the instance. + * + * @param joinSubsetType joining subset schema + */ + default void setUp(Schema joinSubsetType) {} default void startBundle( DoFn.StartBundleContext context, PipelineOptions pipelineOptions) {} diff --git a/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/transform/BeamJoinTransforms.java b/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/transform/BeamJoinTransforms.java index e4d62c2b5de7..d25f98729bd4 100644 --- a/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/transform/BeamJoinTransforms.java +++ b/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/transform/BeamJoinTransforms.java @@ -153,7 +153,7 @@ public PCollection expand(PCollection input) { new DoFn() { @Setup public void setup() { - seekableTable.setUp(); + seekableTable.setUp(joinSubsetType); } @StartBundle diff --git a/sdks/java/extensions/sql/src/test/java/org/apache/beam/sdk/extensions/sql/impl/rel/BeamSideInputLookupJoinRelTest.java b/sdks/java/extensions/sql/src/test/java/org/apache/beam/sdk/extensions/sql/impl/rel/BeamSideInputLookupJoinRelTest.java index 2e2971ebd6e9..b5fd03045cbc 100644 --- a/sdks/java/extensions/sql/src/test/java/org/apache/beam/sdk/extensions/sql/impl/rel/BeamSideInputLookupJoinRelTest.java +++ b/sdks/java/extensions/sql/src/test/java/org/apache/beam/sdk/extensions/sql/impl/rel/BeamSideInputLookupJoinRelTest.java @@ -34,6 +34,7 @@ import org.apache.beam.sdk.values.POutput; import org.apache.beam.sdk.values.Row; import org.hamcrest.core.StringContains; +import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; @@ -47,11 +48,18 @@ public class BeamSideInputLookupJoinRelTest extends BaseRelTest { /** Test table for JOIN-AS-LOOKUP. */ public static class SiteLookupTable extends SchemaBaseBeamTable implements BeamSqlSeekableTable { + private Schema joinSubsetType; public SiteLookupTable(Schema schema) { super(schema); } + @Override + public void setUp(Schema joinSubsetType) { + this.joinSubsetType = joinSubsetType; + Assert.assertNotNull(joinSubsetType); + } + @Override public PCollection.IsBounded isBounded() { return PCollection.IsBounded.BOUNDED; @@ -69,6 +77,7 @@ public POutput buildIOWriter(PCollection input) { @Override public List seekRow(Row lookupSubRow) { + Assert.assertEquals(joinSubsetType, lookupSubRow.getSchema()); if (lookupSubRow.getInt32("site_id") == 2) { return Arrays.asList(Row.withSchema(getSchema()).addValues(2, "SITE1").build()); } From f676d93030cf9d0c849337f1e3a4efff1d8f2509 Mon Sep 17 00:00:00 2001 From: caneff Date: Wed, 20 Sep 2023 12:34:41 -0400 Subject: [PATCH 096/105] When comparing Series, sort the values in Dataframe tests (#28557) --- sdks/python/apache_beam/dataframe/frames_test.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sdks/python/apache_beam/dataframe/frames_test.py b/sdks/python/apache_beam/dataframe/frames_test.py index 4998683461b9..30d992480515 100644 --- a/sdks/python/apache_beam/dataframe/frames_test.py +++ b/sdks/python/apache_beam/dataframe/frames_test.py @@ -193,6 +193,9 @@ def _run_test( if expected.index.is_unique: expected = expected.sort_index() actual = actual.sort_index() + elif isinstance(expected, pd.Series): + expected = expected.sort_values() + actual = actual.sort_values() else: expected = expected.sort_values(list(expected.columns)) actual = actual.sort_values(list(actual.columns)) From ef0d8d4041cd55f153ebb3503486fb8d558e812a Mon Sep 17 00:00:00 2001 From: Ahmed Abualsaud <65791736+ahmedabu98@users.noreply.github.com> Date: Wed, 20 Sep 2023 17:30:55 +0000 Subject: [PATCH 097/105] Label Python external SchemaTransform with its URN (#28540) --- sdks/python/apache_beam/transforms/external.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sdks/python/apache_beam/transforms/external.py b/sdks/python/apache_beam/transforms/external.py index 4b8e708bfc5c..44bf2398a6dd 100644 --- a/sdks/python/apache_beam/transforms/external.py +++ b/sdks/python/apache_beam/transforms/external.py @@ -185,6 +185,14 @@ def __init__(self, identifier, **kwargs): self._identifier = identifier self._kwargs = kwargs + def identifier(self): + """ + The URN referencing this SchemaTransform + + :return: str + """ + return self._identifier + def build(self): schema_proto, payload = self._get_schema_proto_and_payload(**self._kwargs) payload = external_transforms_pb2.SchemaTransformPayload( @@ -194,7 +202,7 @@ def build(self): return payload -class ExplicitSchemaTransformPayloadBuilder(PayloadBuilder): +class ExplicitSchemaTransformPayloadBuilder(SchemaTransformPayloadBuilder): def __init__(self, identifier, schema_proto, **kwargs): self._identifier = identifier self._schema_proto = schema_proto @@ -414,7 +422,7 @@ def __init__( def expand(self, pcolls): # Expand the transform using the expansion service. - return pcolls | ExternalTransform( + return pcolls | self._payload_builder.identifier() >> ExternalTransform( common_urns.schematransform_based_expand.urn, self._payload_builder, self._expansion_service) From 955cd8920f4ce28964a2f6ab54b5a4a705531062 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Sep 2023 13:52:32 -0400 Subject: [PATCH 098/105] Bump actions/checkout from 3 to 4 (#28552) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/beam_PostCommit_Java_Nexmark_Dataflow.yml | 2 +- .github/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2.yml | 2 +- .../workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2_Java.yml | 2 +- .github/workflows/beam_PostCommit_Java_Nexmark_Direct.yml | 2 +- .github/workflows/beam_PostCommit_Java_Nexmark_Flink.yml | 2 +- .github/workflows/beam_PostCommit_Java_Nexmark_Spark.yml | 2 +- .github/workflows/beam_PostCommit_Javadoc.yml | 2 +- .github/workflows/beam_PostCommit_Python_Nexmark_Direct.yml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow.yml b/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow.yml index cf5162307078..ef90fbad5bf0 100644 --- a/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow.yml +++ b/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow.yml @@ -90,7 +90,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Dataflow Runner Nexmark Tests' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2.yml b/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2.yml index 0ee017ecce22..3eb93e6687f8 100644 --- a/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2.yml +++ b/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2.yml @@ -90,7 +90,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Dataflow Runner V2 Nexmark Tests' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2_Java.yml b/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2_Java.yml index 001e37775048..06438510400b 100644 --- a/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2_Java.yml +++ b/.github/workflows/beam_PostCommit_Java_Nexmark_Dataflow_V2_Java.yml @@ -92,7 +92,7 @@ jobs: (contains(github.event.comment.body, 'Run Dataflow Runner V2 Java') && contains(github.event.comment.body, 'Nexmark Tests')) steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Nexmark_Direct.yml b/.github/workflows/beam_PostCommit_Java_Nexmark_Direct.yml index 00727ba64d48..2386d7e26f38 100644 --- a/.github/workflows/beam_PostCommit_Java_Nexmark_Direct.yml +++ b/.github/workflows/beam_PostCommit_Java_Nexmark_Direct.yml @@ -85,7 +85,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Direct Runner Nexmark Tests' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Nexmark_Flink.yml b/.github/workflows/beam_PostCommit_Java_Nexmark_Flink.yml index fce3ee045065..9123c9079605 100644 --- a/.github/workflows/beam_PostCommit_Java_Nexmark_Flink.yml +++ b/.github/workflows/beam_PostCommit_Java_Nexmark_Flink.yml @@ -84,7 +84,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Flink Runner Nexmark Tests' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Java_Nexmark_Spark.yml b/.github/workflows/beam_PostCommit_Java_Nexmark_Spark.yml index a3735a00738f..7492eb9b8262 100644 --- a/.github/workflows/beam_PostCommit_Java_Nexmark_Spark.yml +++ b/.github/workflows/beam_PostCommit_Java_Nexmark_Spark.yml @@ -84,7 +84,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Spark Runner Nexmark Tests' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Javadoc.yml b/.github/workflows/beam_PostCommit_Javadoc.yml index f60f9dafd535..240b0e43d271 100644 --- a/.github/workflows/beam_PostCommit_Javadoc.yml +++ b/.github/workflows/beam_PostCommit_Javadoc.yml @@ -64,7 +64,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Javadoc PostCommit' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: diff --git a/.github/workflows/beam_PostCommit_Python_Nexmark_Direct.yml b/.github/workflows/beam_PostCommit_Python_Nexmark_Direct.yml index a493d6a1656b..81c9b4a8b484 100644 --- a/.github/workflows/beam_PostCommit_Python_Nexmark_Direct.yml +++ b/.github/workflows/beam_PostCommit_Python_Nexmark_Direct.yml @@ -107,7 +107,7 @@ jobs: github.event_name == 'schedule' || github.event.comment.body == 'Run Python Direct Runner Nexmark Tests' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup repository uses: ./.github/actions/setup-action with: From 3b7e1dcc4e621f977eab9d19dae739328f9169c0 Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Wed, 20 Sep 2023 10:53:40 -0700 Subject: [PATCH 099/105] Typescript changes for Beam 2.51.0 (#28553) * Revert dependbot changes. If we upgrade proto, we need to regenerate the protos. * Add needed dataflow experiments. This is probably related to BEAM-28399; best to fix here to be explicit. * Pin to patch version. --- sdks/typescript/package-lock.json | 238 ++---------------- sdks/typescript/package.json | 4 +- .../src/apache_beam/runners/dataflow.ts | 2 + 3 files changed, 22 insertions(+), 222 deletions(-) diff --git a/sdks/typescript/package-lock.json b/sdks/typescript/package-lock.json index 51a10d2b4a8f..e4556449fde4 100644 --- a/sdks/typescript/package-lock.json +++ b/sdks/typescript/package-lock.json @@ -1,15 +1,15 @@ { "name": "apache-beam", - "version": "2.50.0-SNAPSHOT", + "version": "2.50.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "apache-beam", - "version": "2.50.0-SNAPSHOT", + "version": "2.50.0", "dependencies": { "@google-cloud/pubsub": "^2.19.4", - "@grpc/grpc-js": "^1.8.8", + "@grpc/grpc-js": "^1.4.6", "@protobuf-ts/grpc-transport": "^2.1.0", "@protobuf-ts/plugin": "^2.1.0", "bson": "^4.6.0", @@ -19,7 +19,7 @@ "fast-deep-equal": "^3.1.3", "find-git-root": "^1.0.4", "long": "^4.0.0", - "protobufjs": "^7.2.4", + "protobufjs": "^6.11.3", "queue-typescript": "^1.0.1", "serialize-closures": "^0.2.7", "ts-closure-transform": "^0.1.7", @@ -190,73 +190,17 @@ } }, "node_modules/@grpc/grpc-js": { - "version": "1.8.8", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.8.8.tgz", - "integrity": "sha512-4gfDqMLXTrorvYTKA1jL22zLvVwiHJ73t6Re1OHwdCFRjdGTDOVtSJuaWhtHaivyeDGg0LeCkmU77MTKoV3wPA==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.4.6.tgz", + "integrity": "sha512-Byau4xiXfIixb1PnW30V/P9mkrZ05lknyNqiK+cVY9J5hj3gecxd/anwaUbAM8j834zg1x78NvAbwGnMfWEu7A==", "dependencies": { - "@grpc/proto-loader": "^0.7.0", + "@grpc/proto-loader": "^0.6.4", "@types/node": ">=12.12.47" }, "engines": { "node": "^8.13.0 || >=10.10.0" } }, - "node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.7.tgz", - "integrity": "sha512-1TIeXOi8TuSCQprPItwoMymZXxWT0CPxUhkrkeCUH+D8U7QDwQ6b7SUz2MaLuWM2llT+J/TVFLmQI5KtML3BhQ==", - "dependencies": { - "@types/long": "^4.0.1", - "lodash.camelcase": "^4.3.0", - "long": "^4.0.0", - "protobufjs": "^7.0.0", - "yargs": "^17.7.2" - }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@grpc/grpc-js/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@grpc/grpc-js/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@grpc/grpc-js/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" - } - }, "node_modules/@grpc/proto-loader": { "version": "0.6.9", "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.9.tgz", @@ -275,31 +219,6 @@ "node": ">=6" } }, - "node_modules/@grpc/proto-loader/node_modules/protobufjs": { - "version": "6.11.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", - "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", - "hasInstallScript": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": ">=13.7.0", - "long": "^4.0.0" - }, - "bin": { - "pbjs": "bin/pbjs", - "pbts": "bin/pbts" - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.9.5", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", @@ -3293,7 +3212,7 @@ "protobufjs": "^6.11.2" } }, - "node_modules/proto3-json-serializer/node_modules/protobufjs": { + "node_modules/protobufjs": { "version": "6.11.3", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", @@ -3318,34 +3237,6 @@ "pbts": "bin/pbts" } }, - "node_modules/protobufjs": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.4.tgz", - "integrity": "sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ==", - "hasInstallScript": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/protobufjs/node_modules/long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" - }, "node_modules/punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -4296,55 +4187,12 @@ } }, "@grpc/grpc-js": { - "version": "1.8.8", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.8.8.tgz", - "integrity": "sha512-4gfDqMLXTrorvYTKA1jL22zLvVwiHJ73t6Re1OHwdCFRjdGTDOVtSJuaWhtHaivyeDGg0LeCkmU77MTKoV3wPA==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.4.6.tgz", + "integrity": "sha512-Byau4xiXfIixb1PnW30V/P9mkrZ05lknyNqiK+cVY9J5hj3gecxd/anwaUbAM8j834zg1x78NvAbwGnMfWEu7A==", "requires": { - "@grpc/proto-loader": "^0.7.0", + "@grpc/proto-loader": "^0.6.4", "@types/node": ">=12.12.47" - }, - "dependencies": { - "@grpc/proto-loader": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.7.tgz", - "integrity": "sha512-1TIeXOi8TuSCQprPItwoMymZXxWT0CPxUhkrkeCUH+D8U7QDwQ6b7SUz2MaLuWM2llT+J/TVFLmQI5KtML3BhQ==", - "requires": { - "@types/long": "^4.0.1", - "lodash.camelcase": "^4.3.0", - "long": "^4.0.0", - "protobufjs": "^7.0.0", - "yargs": "^17.7.2" - } - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" - } } }, "@grpc/proto-loader": { @@ -4357,28 +4205,6 @@ "long": "^4.0.0", "protobufjs": "^6.10.0", "yargs": "^16.2.0" - }, - "dependencies": { - "protobufjs": { - "version": "6.11.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", - "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", - "requires": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": ">=13.7.0", - "long": "^4.0.0" - } - } } }, "@humanwhocodes/config-array": { @@ -6583,34 +6409,12 @@ "integrity": "sha512-A60IisqvnuI45qNRygJjrnNjX2TMdQGMY+57tR3nul3ZgO2zXkR9OGR8AXxJhkqx84g0FTnrfi3D5fWMSdANdQ==", "requires": { "protobufjs": "^6.11.2" - }, - "dependencies": { - "protobufjs": { - "version": "6.11.3", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", - "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", - "requires": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": ">=13.7.0", - "long": "^4.0.0" - } - } } }, "protobufjs": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.4.tgz", - "integrity": "sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ==", + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", "requires": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", @@ -6622,15 +6426,9 @@ "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "dependencies": { - "long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" - } + "long": "^4.0.0" } }, "punycode": { diff --git a/sdks/typescript/package.json b/sdks/typescript/package.json index 041121bd41ea..35a1e8134e29 100644 --- a/sdks/typescript/package.json +++ b/sdks/typescript/package.json @@ -36,7 +36,7 @@ }, "dependencies": { "@google-cloud/pubsub": "^2.19.4", - "@grpc/grpc-js": "^1.8.8", + "@grpc/grpc-js": "~1.4.6", "@protobuf-ts/grpc-transport": "^2.1.0", "@protobuf-ts/plugin": "^2.1.0", "bson": "^4.6.0", @@ -46,7 +46,7 @@ "fast-deep-equal": "^3.1.3", "find-git-root": "^1.0.4", "long": "^4.0.0", - "protobufjs": "^7.2.4", + "protobufjs": "~6.11.3", "queue-typescript": "^1.0.1", "serialize-closures": "^0.2.7", "ts-closure-transform": "^0.1.7", diff --git a/sdks/typescript/src/apache_beam/runners/dataflow.ts b/sdks/typescript/src/apache_beam/runners/dataflow.ts index 950e630d82d9..e7da1f7ada51 100644 --- a/sdks/typescript/src/apache_beam/runners/dataflow.ts +++ b/sdks/typescript/src/apache_beam/runners/dataflow.ts @@ -33,6 +33,8 @@ export function dataflowRunner(runnerOptions: { options: Object = {} ): Promise { var augmentedOptions = { experiments: [] as string[], ...options }; + augmentedOptions.experiments.push("use_runner_v2"); + augmentedOptions.experiments.push("use_portable_job_submission"); augmentedOptions.experiments.push("use_sibling_sdk_workers"); return new PortableRunner( runnerOptions as any, From 534f93acd18fc18e7c56a7495ca0a8434676cbc7 Mon Sep 17 00:00:00 2001 From: Yi Hu Date: Wed, 20 Sep 2023 13:55:52 -0400 Subject: [PATCH 100/105] Introduce PeriodicImpulse.stopAfter() (#28503) * Use it in streaming BigQueryIO integration test --- .../beam/runners/dataflow/DataflowRunner.java | 2 +- .../beam/sdk/transforms/PeriodicImpulse.java | 98 ++++++++++++++++--- .../beam/sdk/transforms/PeriodicSequence.java | 3 + .../bigquery/BigQueryIOStorageWriteIT.java | 42 ++++++-- 4 files changed, 123 insertions(+), 22 deletions(-) diff --git a/runners/google-cloud-dataflow-java/src/main/java/org/apache/beam/runners/dataflow/DataflowRunner.java b/runners/google-cloud-dataflow-java/src/main/java/org/apache/beam/runners/dataflow/DataflowRunner.java index 02f6f9acd7a6..17aea34045ff 100644 --- a/runners/google-cloud-dataflow-java/src/main/java/org/apache/beam/runners/dataflow/DataflowRunner.java +++ b/runners/google-cloud-dataflow-java/src/main/java/org/apache/beam/runners/dataflow/DataflowRunner.java @@ -1755,7 +1755,7 @@ void maybeRecordPCollectionWithAutoSharding(PCollection pcol) { options.isEnableStreamingEngine(), "Runner determined sharding not available in Dataflow for GroupIntoBatches for" + " non-Streaming-Engine jobs. In order to use runner determined sharding, please use" - + " --streaming --enable_streaming_engine"); + + " --streaming --experiments=enable_streaming_engine"); pCollectionsPreservedKeys.add(pcol); pcollectionsRequiringAutoSharding.add(pcol); } diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/PeriodicImpulse.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/PeriodicImpulse.java index 3679c3eb10f5..db4f141ee624 100644 --- a/sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/PeriodicImpulse.java +++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/PeriodicImpulse.java @@ -17,11 +17,15 @@ */ package org.apache.beam.sdk.transforms; +import static org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkArgument; + +import org.apache.beam.sdk.annotations.Internal; import org.apache.beam.sdk.transforms.windowing.BoundedWindow; import org.apache.beam.sdk.transforms.windowing.FixedWindows; import org.apache.beam.sdk.transforms.windowing.Window; import org.apache.beam.sdk.values.PBegin; import org.apache.beam.sdk.values.PCollection; +import org.checkerframework.checker.nullness.qual.Nullable; import org.joda.time.Duration; import org.joda.time.Instant; @@ -34,28 +38,58 @@ */ public class PeriodicImpulse extends PTransform> { - Instant startTimestamp = Instant.now(); - Instant stopTimestamp = BoundedWindow.TIMESTAMP_MAX_VALUE; - Duration fireInterval = Duration.standardMinutes(1); + Instant startTimestamp; + Instant stopTimestamp; + @Nullable Duration stopDuration; + Duration fireInterval; boolean applyWindowing = false; boolean catchUpToNow = true; - private PeriodicImpulse() {} + private PeriodicImpulse() { + this.startTimestamp = Instant.now(); + this.stopTimestamp = BoundedWindow.TIMESTAMP_MAX_VALUE; + this.fireInterval = Duration.standardMinutes(1); + } public static PeriodicImpulse create() { return new PeriodicImpulse(); } + /** + * Assign a timestamp when the pipeliene starts to produce data. + * + *

Cannot be used along with {@link #stopAfter}. + */ public PeriodicImpulse startAt(Instant startTime) { + checkArgument(stopDuration == null, "startAt and stopAfter cannot be set at the same time"); this.startTimestamp = startTime; return this; } + /** + * Assign a timestamp when the pipeliene stops producing data. + * + *

Cannot be used along with {@link #stopAfter}. + */ public PeriodicImpulse stopAt(Instant stopTime) { + checkArgument(stopDuration == null, "stopAt and stopAfter cannot be set at the same time"); this.stopTimestamp = stopTime; return this; } + /** + * For internal use only; no backwards-compatibility guarantees. + * + *

Assign a time interval at which the pipeliene produces data. This is different from setting + * {@link #startAt} and {@link #stopAt}, as the first timestamp is determined at run time + * (pipeline starts processing). + */ + @Internal + public PeriodicImpulse stopAfter(Duration duration) { + this.stopDuration = duration; + return this; + } + public PeriodicImpulse withInterval(Duration interval) { this.fireInterval = interval; return this; @@ -67,10 +101,13 @@ public PeriodicImpulse applyWindowing() { } /** - * The default behavior is that PeriodicImpulse emits all instants until Instant.now(), then + * For internal use only; no backwards-compatibility guarantees. + * + *

The default behavior is that PeriodicImpulse emits all instants until Instant.now(), then * starts firing at the specified interval. If this is set to false, the PeriodicImpulse will * perform the interval wait before firing each instant. */ + @Internal public PeriodicImpulse catchUpToNow(boolean catchUpToNow) { this.catchUpToNow = catchUpToNow; return this; @@ -78,20 +115,51 @@ public PeriodicImpulse catchUpToNow(boolean catchUpToNow) { @Override public PCollection expand(PBegin input) { - PCollection result = - input - .apply( - Create.of( - new PeriodicSequence.SequenceDefinition( - startTimestamp, stopTimestamp, fireInterval, catchUpToNow))) - .apply(PeriodicSequence.create()); + PCollection seqDef; + if (stopDuration != null) { + // nonnull guaranteed + Duration d = stopDuration; + seqDef = + input + .apply(Impulse.create()) + .apply(ParDo.of(new RuntimeSequenceFn(d, fireInterval, catchUpToNow))); + } else { + seqDef = + input.apply( + Create.of( + new PeriodicSequence.SequenceDefinition( + startTimestamp, stopTimestamp, fireInterval, catchUpToNow))); + } + PCollection result = seqDef.apply(PeriodicSequence.create()); if (this.applyWindowing) { result = - result.apply( - Window.into(FixedWindows.of(Duration.millis(fireInterval.getMillis())))); + result.apply(Window.into(FixedWindows.of(Duration.millis(fireInterval.getMillis())))); } - return result; } + + /** + * A DoFn generated a SequenceDefinition at run time. This enables set first element timestamp at + * pipeline start processing data. + */ + private static class RuntimeSequenceFn extends DoFn { + Duration stopDuration; + Duration fireInterval; + boolean catchUpToNow; + + RuntimeSequenceFn(Duration stopDuration, Duration fireInterval, boolean catchUpToNow) { + this.stopDuration = stopDuration; + this.fireInterval = fireInterval; + this.catchUpToNow = catchUpToNow; + } + + @ProcessElement + public void process(ProcessContext c) { + Instant now = Instant.now(); + c.output( + new PeriodicSequence.SequenceDefinition( + now, now.plus(stopDuration), fireInterval, catchUpToNow)); + } + } } diff --git a/sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/PeriodicSequence.java b/sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/PeriodicSequence.java index b3cd2afde697..12cbecd04b02 100644 --- a/sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/PeriodicSequence.java +++ b/sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/PeriodicSequence.java @@ -22,6 +22,7 @@ import static org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkState; import java.util.Objects; +import org.apache.beam.sdk.annotations.Internal; import org.apache.beam.sdk.io.range.OffsetRange; import org.apache.beam.sdk.schemas.JavaFieldSchema; import org.apache.beam.sdk.schemas.annotations.DefaultSchema; @@ -67,6 +68,8 @@ public SequenceDefinition(Instant first, Instant last, Duration duration) { this.catchUpToNow = true; } + /** catchUpToNow is experimental; no backwards-compatibility guarantees. */ + @Internal public SequenceDefinition( Instant first, Instant last, Duration duration, boolean catchUpToNow) { this.first = first; diff --git a/sdks/java/io/google-cloud-platform/src/test/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIOStorageWriteIT.java b/sdks/java/io/google-cloud-platform/src/test/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIOStorageWriteIT.java index 81de67f38502..fc3ce0be4b69 100644 --- a/sdks/java/io/google-cloud-platform/src/test/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIOStorageWriteIT.java +++ b/sdks/java/io/google-cloud-platform/src/test/java/org/apache/beam/sdk/io/gcp/bigquery/BigQueryIOStorageWriteIT.java @@ -33,9 +33,16 @@ import org.apache.beam.sdk.options.PipelineOptionsFactory; import org.apache.beam.sdk.testing.TestPipeline; import org.apache.beam.sdk.transforms.DoFn; +import org.apache.beam.sdk.transforms.MapElements; +import org.apache.beam.sdk.transforms.PTransform; import org.apache.beam.sdk.transforms.ParDo; +import org.apache.beam.sdk.transforms.PeriodicImpulse; +import org.apache.beam.sdk.transforms.SimpleFunction; +import org.apache.beam.sdk.values.PBegin; +import org.apache.beam.sdk.values.PCollection; import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.collect.ImmutableList; import org.joda.time.Duration; +import org.joda.time.Instant; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -81,11 +88,32 @@ public void processElement(ProcessContext c) { } } - private GenerateSequence stream(int rowCount) { - int timestampIntervalInMilliseconds = 10; - return GenerateSequence.from(0) - .to(rowCount) - .withRate(1, Duration.millis(timestampIntervalInMilliseconds)); + static class UnboundedStream extends PTransform> { + + private final int rowCount; + + public UnboundedStream(int rowCount) { + this.rowCount = rowCount; + } + + @Override + public PCollection expand(PBegin input) { + int timestampIntervalInMillis = 10; + PeriodicImpulse impulse = + PeriodicImpulse.create() + .stopAfter(Duration.millis((long) timestampIntervalInMillis * rowCount - 1)) + .withInterval(Duration.millis(timestampIntervalInMillis)); + return input + .apply(impulse) + .apply( + MapElements.via( + new SimpleFunction() { + @Override + public Long apply(Instant input) { + return input.getMillis(); + } + })); + } } private void runBigQueryIOStorageWritePipeline( @@ -102,7 +130,9 @@ private void runBigQueryIOStorageWritePipeline( new TableFieldSchema().setName("str").setType("STRING"))); Pipeline p = Pipeline.create(bqOptions); - p.apply("Input", isStreaming ? stream(rowCount) : GenerateSequence.from(0).to(rowCount)) + p.apply( + "Input", + isStreaming ? new UnboundedStream(rowCount) : GenerateSequence.from(0).to(rowCount)) .apply("GenerateMessage", ParDo.of(new FillRowFn())) .apply( "WriteToBQ", From 0b131c9ae7cafd7a43f875b0df1fb714683bdcda Mon Sep 17 00:00:00 2001 From: caneff Date: Wed, 20 Sep 2023 15:16:51 -0400 Subject: [PATCH 101/105] Change handling of copy=None defaults for Pandas 2 (#28523) --- sdks/python/apache_beam/dataframe/frame_base.py | 8 ++++++++ .../apache_beam/dataframe/frame_base_test.py | 15 +++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/sdks/python/apache_beam/dataframe/frame_base.py b/sdks/python/apache_beam/dataframe/frame_base.py index 48a4c29d0589..4e89e473b730 100644 --- a/sdks/python/apache_beam/dataframe/frame_base.py +++ b/sdks/python/apache_beam/dataframe/frame_base.py @@ -674,11 +674,19 @@ def wrap(func): if removed_args: defaults_to_populate -= set(removed_args) + # In pandas 2, many methods rely on the default copy=None + # to mean that copy is the value of copy_on_write. Since + # copy_on_write will always be true for Beam, just fill it + # in here. In pandas 1, the default was True anyway. + if 'copy' in arg_to_default and arg_to_default['copy'] is None: + arg_to_default['copy'] = True + @functools.wraps(func) def wrapper(**kwargs): for name in defaults_to_populate: if name not in kwargs: kwargs[name] = arg_to_default[name] + return func(**kwargs) return wrapper diff --git a/sdks/python/apache_beam/dataframe/frame_base_test.py b/sdks/python/apache_beam/dataframe/frame_base_test.py index b3077320720f..0a73905339fd 100644 --- a/sdks/python/apache_beam/dataframe/frame_base_test.py +++ b/sdks/python/apache_beam/dataframe/frame_base_test.py @@ -174,6 +174,21 @@ def func(self, a, **kwargs): 'a': 2, 'b': 4, 'c': 6, 'kw_only': 8 }) + def test_populate_defaults_overwrites_copy(self): + class Base(object): + def func(self, a=1, b=2, c=3, *, copy=None): + pass + + class Proxy(object): + @frame_base.args_to_kwargs(Base) + @frame_base.populate_defaults(Base) + def func(self, a, copy, **kwargs): + return dict(kwargs, a=a, copy=copy) + + proxy = Proxy() + self.assertEqual(proxy.func(), {'a': 1, 'copy': True}) + self.assertEqual(proxy.func(copy=False), {'a': 1, 'copy': False}) + if __name__ == '__main__': unittest.main() From 5fb13e05d545d1277af6a5049f5978504e509b36 Mon Sep 17 00:00:00 2001 From: Pranav Bhandari Date: Wed, 20 Sep 2023 16:59:03 -0400 Subject: [PATCH 102/105] Make launcher method public in LoadTestBase. (#28568) --- .../src/main/java/org/apache/beam/it/gcp/IOLoadTestBase.java | 2 +- .../src/main/java/org/apache/beam/it/gcp/LoadTestBase.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/IOLoadTestBase.java b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/IOLoadTestBase.java index 32f262f2eac1..6b728a6a60db 100644 --- a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/IOLoadTestBase.java +++ b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/IOLoadTestBase.java @@ -62,7 +62,7 @@ public void tearDownBase() throws IOException { } @Override - PipelineLauncher launcher() { + public PipelineLauncher launcher() { return DefaultPipelineLauncher.builder(CREDENTIALS).build(); } diff --git a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/LoadTestBase.java b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/LoadTestBase.java index d9c1990ef079..14bb05394de2 100644 --- a/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/LoadTestBase.java +++ b/it/google-cloud-platform/src/main/java/org/apache/beam/it/gcp/LoadTestBase.java @@ -128,7 +128,7 @@ public void tearDownLoadTestBase() throws IOException { monitoringClient.cleanupAll(); } - abstract PipelineLauncher launcher(); + public abstract PipelineLauncher launcher(); /** * Exports the metrics of given dataflow job to BigQuery. From 17db78c10dcb732a1ddcd29049edddfea6357aac Mon Sep 17 00:00:00 2001 From: Robert Burke Date: Wed, 20 Sep 2023 14:40:38 -0700 Subject: [PATCH 103/105] [prism] Auto remove containers after stop. (#28570) Co-authored-by: lostluck <13907733+lostluck@users.noreply.github.com> --- sdks/go/pkg/beam/runners/prism/internal/environments.go | 1 + 1 file changed, 1 insertion(+) diff --git a/sdks/go/pkg/beam/runners/prism/internal/environments.go b/sdks/go/pkg/beam/runners/prism/internal/environments.go index 7d54cb366ffe..3a429920fb28 100644 --- a/sdks/go/pkg/beam/runners/prism/internal/environments.go +++ b/sdks/go/pkg/beam/runners/prism/internal/environments.go @@ -155,6 +155,7 @@ func dockerEnvironment(ctx context.Context, logger *slog.Logger, dp *pipepb.Dock }, &container.HostConfig{ NetworkMode: "host", Mounts: mounts, + AutoRemove: true, }, nil, nil, "") if err != nil { cli.Close() From 1d94f5ffb84e3acbd5850639c740df5fb04a0080 Mon Sep 17 00:00:00 2001 From: caneff Date: Wed, 20 Sep 2023 18:18:14 -0400 Subject: [PATCH 104/105] Fix test for new group keys behavior for Pandas 2 (#28566) --- sdks/python/apache_beam/dataframe/frames_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdks/python/apache_beam/dataframe/frames_test.py b/sdks/python/apache_beam/dataframe/frames_test.py index 30d992480515..e3555b50187b 100644 --- a/sdks/python/apache_beam/dataframe/frames_test.py +++ b/sdks/python/apache_beam/dataframe/frames_test.py @@ -1837,8 +1837,8 @@ def test_groupby_apply_preserves_column_order(self): df = GROUPBY_DF self._run_test( - lambda df: df[['foo', 'group', 'bar']].groupby('group').apply( - lambda x: x), + lambda df: df[['foo', 'group', 'bar']].groupby( + 'group', group_keys=False).apply(lambda x: x), df) def test_groupby_transform(self): From c5e6c7962e60ee8366bfc92edf0812341e940020 Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Wed, 20 Sep 2023 16:37:17 -0700 Subject: [PATCH 105/105] Refactor and cleanup yaml MapToFields. (#28462) * Avoid the use of MetaProviders, which was always kind of hacky. We may want to remove this infrastructure altogether as it does not play nicely with provider inference. * Split MapToFields into separate mapping, filtering, and exploding operations. * Allow MapToFields to act on non-schema'd PCollections. The various langauge flavors of these UDFs are now handled by a preprocessing step. This will make it easier to extend to other langauges, including in particular possible multiple (equivalent) implementations of javascript to minimize cross-langauge boundary crossings. --------- Co-authored-by: Danny McCormick --- sdks/python/apache_beam/transforms/core.py | 8 + sdks/python/apache_beam/yaml/readme_test.py | 23 +- sdks/python/apache_beam/yaml/yaml_mapping.md | 35 +- sdks/python/apache_beam/yaml/yaml_mapping.py | 367 ++++++++++-------- .../apache_beam/yaml/yaml_mapping_test.py | 32 +- sdks/python/apache_beam/yaml/yaml_provider.py | 7 +- .../python/apache_beam/yaml/yaml_transform.py | 15 + .../apache_beam/yaml/yaml_transform_test.py | 14 +- sdks/python/apache_beam/yaml/yaml_udf_test.py | 38 +- 9 files changed, 311 insertions(+), 228 deletions(-) diff --git a/sdks/python/apache_beam/transforms/core.py b/sdks/python/apache_beam/transforms/core.py index 66ac8fbad967..671af54e47be 100644 --- a/sdks/python/apache_beam/transforms/core.py +++ b/sdks/python/apache_beam/transforms/core.py @@ -2258,6 +2258,10 @@ def __init__(self, pcoll, exception_handling_args, upstream_errors=()): self._exception_handling_args = exception_handling_args self._upstream_errors = upstream_errors + @property + def element_type(self): + return self._pcoll.element_type + def main_output_tag(self): return self._exception_handling_args.get('main_tag', 'good') @@ -2309,6 +2313,10 @@ def __init__(self, pvalue, exception_handling_args=None): else: self._pvalue = _PValueWithErrors(pvalue, exception_handling_args) + @property + def element_type(self): + return self._pvalue.element_type + def __or__(self, transform): return self.apply(transform) diff --git a/sdks/python/apache_beam/yaml/readme_test.py b/sdks/python/apache_beam/yaml/readme_test.py index 958d9cb5783a..d918d18e11dd 100644 --- a/sdks/python/apache_beam/yaml/readme_test.py +++ b/sdks/python/apache_beam/yaml/readme_test.py @@ -32,6 +32,7 @@ import apache_beam as beam from apache_beam.options.pipeline_options import PipelineOptions from apache_beam.typehints import trivial_inference +from apache_beam.yaml import yaml_mapping from apache_beam.yaml import yaml_provider from apache_beam.yaml import yaml_transform @@ -85,13 +86,16 @@ def guess_name_and_type(expr): typ, = [t for t in typ.__args__ if t is not type(None)] return name, typ - output_schema = [ - guess_name_and_type(expr) for expr in m.group(1).split(',') - ] - output_element = beam.Row(**{name: typ() for name, typ in output_schema}) - return next(iter(inputs.values())) | beam.Map( - lambda _: output_element).with_output_types( - trivial_inference.instance_to_type(output_element)) + if m.group(1) == '*': + return inputs['PCOLLECTION'] | beam.Filter(lambda _: True) + else: + output_schema = [ + guess_name_and_type(expr) for expr in m.group(1).split(',') + ] + output_element = beam.Row(**{name: typ() for name, typ in output_schema}) + return next(iter(inputs.values())) | beam.Map( + lambda _: output_element).with_output_types( + trivial_inference.instance_to_type(output_element)) class FakeReadFromPubSub(beam.PTransform): @@ -204,12 +208,13 @@ def test(self): ] options['render_leaf_composite_nodes'] = ['.*'] test_provider = TestProvider(TEST_TRANSFORMS) + test_sql_mapping_provider = yaml_mapping.SqlMappingProvider(test_provider) p = beam.Pipeline(options=PipelineOptions(**options)) yaml_transform.expand_pipeline( p, modified_yaml, - {t: test_provider - for t in test_provider.provided_transforms()}) + yaml_provider.merge_providers( + [test_provider, test_sql_mapping_provider])) if test_type == 'BUILD': return p.run().wait_until_finish() diff --git a/sdks/python/apache_beam/yaml/yaml_mapping.md b/sdks/python/apache_beam/yaml/yaml_mapping.md index b5e84e1a8054..653b4abe8b89 100644 --- a/sdks/python/apache_beam/yaml/yaml_mapping.md +++ b/sdks/python/apache_beam/yaml/yaml_mapping.md @@ -131,7 +131,7 @@ Currently, in addition to Python, SQL expressions are supported as well Sometimes it may be desirable to emit more (or less) than one record for each input record. This can be accomplished by mapping to an iterable type and -noting that the specific field should be exploded, e.g. +following the mapping with an Explode operation, e.g. ``` - type: MapToFields @@ -140,7 +140,9 @@ noting that the specific field should be exploded, e.g. fields: new_col: "[col1.upper(), col1.lower(), col1.title()]" another_col: "col2 + col3" - explode: new_col +- type: Explode + config: + fields: new_col ``` will result in three output records for every input record. @@ -155,7 +157,9 @@ product over all fields should be taken. For example fields: new_col: "[col1.upper(), col1.lower(), col1.title()]" another_col: "[col2 - 1, col2, col2 + 1]" - explode: [new_col, another_col] +- type: Explode + config: + fields: [new_col, another_col] cross_product: true ``` @@ -168,38 +172,27 @@ will emit nine records whereas fields: new_col: "[col1.upper(), col1.lower(), col1.title()]" another_col: "[col2 - 1, col2, col2 + 1]" - explode: [new_col, another_col] +- type: Explode + config: + fields: [new_col, another_col] cross_product: false ``` will only emit three. -If one is only exploding existing fields, a simpler `Explode` transform may be -used instead +The `Explode` operation can be used on its own if the field in question is +already an iterable type. ``` - type: Explode config: - explode: [col1] + fields: [col1] ``` ## Filtering Sometimes it can be desirable to only keep records that satisfy a certain -criteria. This can be accomplished by specifying a keep parameter, e.g. - -``` -- type: MapToFields - config: - language: python - fields: - new_col: "col1.upper()" - another_col: "col2 + col3" - keep: "col2 > 0" -``` - -Like explode, there is a simpler `Filter` transform useful when no mapping is -being done +criteria. This can be accomplished with a `Filter` transform, e.g. ``` - type: Filter diff --git a/sdks/python/apache_beam/yaml/yaml_mapping.py b/sdks/python/apache_beam/yaml/yaml_mapping.py index b6dea894b3e9..221c6f018d67 100644 --- a/sdks/python/apache_beam/yaml/yaml_mapping.py +++ b/sdks/python/apache_beam/yaml/yaml_mapping.py @@ -17,6 +17,14 @@ """This module defines the basic MapToFields operation.""" import itertools +from typing import Any +from typing import Callable +from typing import Collection +from typing import Dict +from typing import Iterable +from typing import Mapping +from typing import Optional +from typing import Union import js2py @@ -139,18 +147,73 @@ def _as_callable(original_fields, expr, transform_name, language): 'Supported languages are "javascript" and "python."') +def exception_handling_args(error_handling_spec): + if error_handling_spec: + return { + 'dead_letter_tag' if k == 'output' else k: v + for (k, v) in error_handling_spec.items() + } + else: + return None + + +def _map_errors_to_standard_format(): + # TODO(https://github.com/apache/beam/issues/24755): Switch to MapTuple. + return beam.Map( + lambda x: beam.Row(element=x[0], msg=str(x[1][1]), stack=str(x[1][2]))) + + +def maybe_with_exception_handling(inner_expand): + def expand(self, pcoll): + wrapped_pcoll = beam.core._MaybePValueWithErrors( + pcoll, self._exception_handling_args) + return inner_expand(self, wrapped_pcoll).as_result( + _map_errors_to_standard_format()) + + return expand + + +def maybe_with_exception_handling_transform_fn(transform_fn): + def expand(pcoll, error_handling=None, **kwargs): + wrapped_pcoll = beam.core._MaybePValueWithErrors( + pcoll, exception_handling_args(error_handling)) + return transform_fn(wrapped_pcoll, + **kwargs).as_result(_map_errors_to_standard_format()) + + return expand + + # TODO(yaml): This should be available in all environments, in which case # we choose the one that matches best. class _Explode(beam.PTransform): - def __init__(self, fields, cross_product): + def __init__( + self, + fields: Union[str, Collection[str]], + cross_product: Optional[bool] = None, + error_handling: Optional[Mapping[str, Any]] = None): + if isinstance(fields, str): + fields = [fields] + if cross_product is None: + if len(fields) > 1: + raise ValueError( + 'cross_product must be specified true or false ' + 'when exploding multiple fields') + else: + # Doesn't matter. + cross_product = True self._fields = fields self._cross_product = cross_product - self._exception_handling_args = None + # TODO(yaml): Support standard error handling argument. + self._exception_handling_args = exception_handling_args(error_handling) + @maybe_with_exception_handling def expand(self, pcoll): all_fields = [ x for x, _ in named_fields_from_element_type(pcoll.element_type) ] + for field in self._fields: + if field not in all_fields: + raise ValueError(f'Exploding unknown field "{field}"') to_explode = self._fields def explode_cross_product(base, fields): @@ -171,12 +234,12 @@ def explode_zip(base, fields): yield beam.Row(**copy) return ( - beam.core._MaybePValueWithErrors(pcoll, self._exception_handling_args) + pcoll | beam.FlatMap( lambda row: (explode_cross_product if self._cross_product else explode_zip) ({name: getattr(row, name) - for name in all_fields}, to_explode))).as_result() + for name in all_fields}, to_explode))) def infer_output_type(self, input_type): return row_type.RowTypeConstraint.from_fields([( @@ -190,189 +253,171 @@ def with_exception_handling(self, **kwargs): return self -# TODO(yaml): Should Filter and Explode be distinct operations from Project? -# We'll want these per-language. @beam.ptransform.ptransform_fn -def _PythonProjectionTransform( - pcoll, - *, - fields, - transform_name, - language, - keep=None, - explode=(), - cross_product=True, - error_handling=None): - original_fields = [ - name for (name, _) in named_fields_from_element_type(pcoll.element_type) - ] +@maybe_with_exception_handling_transform_fn +def _PyJsFilter( + pcoll, keep: Union[str, Dict[str, str]], language: Optional[str] = None): - if error_handling is None: - error_handling_args = None + input_schema = dict(named_fields_from_element_type(pcoll.element_type)) + if isinstance(keep, str) and keep in input_schema: + keep_fn = lambda row: getattr(row, keep) else: - error_handling_args = { - 'dead_letter_tag' if k == 'output' else k: v - for (k, v) in error_handling.items() - } + keep_fn = _as_callable(list(input_schema.keys()), keep, "keep", language) + return pcoll | beam.Filter(keep_fn) - pcoll = beam.core._MaybePValueWithErrors(pcoll, error_handling_args) - if keep: - if isinstance(keep, str) and keep in original_fields: - keep_fn = lambda row: getattr(row, keep) - else: - keep_fn = _as_callable(original_fields, keep, transform_name, language) - filtered = pcoll | beam.Filter(keep_fn) - else: - filtered = pcoll +def is_expr(v): + return isinstance(v, str) or (isinstance(v, dict) and 'expression' in v) - projected = filtered | beam.Select( - **{ - name: _as_callable(original_fields, expr, transform_name, language) - for (name, expr) in fields.items() - }) - if explode: - result = projected | _Explode(explode, cross_product=cross_product) - else: - result = projected - - return result.as_result( - # TODO(https://github.com/apache/beam/issues/24755): Switch to MapTuple. - beam.Map( - lambda x: beam.Row( - element=x[0], msg=str(x[1][1]), stack=str(x[1][2])))) - - -@beam.ptransform.ptransform_fn -def MapToFields( - pcoll, - yaml_create_transform, - *, - fields, - keep=None, - explode=(), - cross_product=None, - append=False, - drop=(), - language=None, - error_handling=None, - transform_name="MapToFields", - **language_keywords): - if isinstance(explode, str): - explode = [explode] - if cross_product is None: - if len(explode) > 1: - # TODO(robertwb): Consider if true is an OK default. - raise ValueError( - 'cross_product must be specified true or false ' - 'when exploding multiple fields') - else: - # Doesn't matter. - cross_product = True +def normalize_fields(pcoll, fields, drop=(), append=False, language='generic'): + try: + input_schema = dict(named_fields_from_element_type(pcoll.element_type)) + except ValueError as exn: + if drop: + raise ValueError("Can only drop fields on a schema'd input.") from exn + if append: + raise ValueError("Can only append fields on a schema'd input.") from exn + elif any(is_expr(x) for x in fields.values()): + raise ValueError("Can only use expressions on a schema'd input.") from exn + input_schema = {} - input_schema = dict(named_fields_from_element_type(pcoll.element_type)) + if isinstance(drop, str): + drop = [drop] if drop and not append: raise ValueError("Can only drop fields if append is true.") for name in drop: if name not in input_schema: raise ValueError(f'Dropping unknown field "{name}"') - for name in explode: - if not (name in fields or (append and name in input_schema)): - raise ValueError(f'Exploding unknown field "{name}"') if append: for name in fields: if name in input_schema and name not in drop: - raise ValueError(f'Redefinition of field "{name}"') + raise ValueError( + f'Redefinition of field "{name}". ' + 'Cannot append a field that already exists in original input.') + + if language == 'generic': + for expr in fields.values(): + if not isinstance(expr, str): + raise ValueError( + "Missing language specification. " + "Must specify a language when using a map with custom logic.") + missing = set(fields.values()) - set(input_schema.keys()) + if missing: + raise ValueError( + f"Missing language specification or unknown input fields: {missing}") if append: - fields = { + return input_schema, { **{name: name for name in input_schema.keys() if name not in drop}, **fields } + else: + return input_schema, fields - if language is None: - for name, expr in fields.items(): - if not isinstance(expr, str) or expr not in input_schema: - # TODO(robertw): Could consider defaulting to SQL, or another - # lowest-common-denominator expression language. - raise ValueError("Missing language specification.") - - # We should support this for all languages. - language = "python" - - if language in ("sql", "calcite"): - if error_handling: - raise ValueError('Error handling unsupported for sql.') - selects = [f'{expr} AS {name}' for (name, expr) in fields.items()] - query = "SELECT " + ", ".join(selects) + " FROM PCOLLECTION" - if keep: - query += " WHERE " + keep - - result = pcoll | yaml_create_transform({ - 'type': 'Sql', - 'config': { - 'query': query, **language_keywords - }, - }, [pcoll]) - if explode: - # TODO(yaml): Implement via unnest. - result = result | _Explode(explode, cross_product) - - return result - - elif language == 'python' or language == 'javascript': - return pcoll | yaml_create_transform({ - 'type': 'PyTransform', - 'config': { - 'constructor': __name__ + '._PythonProjectionTransform', - 'kwargs': { - 'fields': fields, - 'transform_name': transform_name, - 'language': language, - 'keep': keep, - 'explode': explode, - 'cross_product': cross_product, - 'error_handling': error_handling, - }, - **language_keywords - }, - }, [pcoll]) - else: - # TODO(yaml): Support javascript expressions and UDFs. - # TODO(yaml): Support java by fully qualified name. - # TODO(yaml): Maybe support java lambdas? - raise ValueError( - f'Unknown language: {language}. ' - 'Supported languages are "sql" (alias calcite) and "python."') +@beam.ptransform.ptransform_fn +@maybe_with_exception_handling_transform_fn +def _PyJsMapToFields(pcoll, language='generic', **mapping_args): + input_schema, fields = normalize_fields( + pcoll, language=language, **mapping_args) + original_fields = list(input_schema.keys()) + + return pcoll | beam.Select( + **{ + name: _as_callable(original_fields, expr, name, language) + for (name, expr) in fields.items() + }) + + +class SqlMappingProvider(yaml_provider.Provider): + def __init__(self, sql_provider=None): + if sql_provider is None: + sql_provider = yaml_provider.beam_jar( + urns={'Sql': 'beam:external:java:sql:v1'}, + gradle_target='sdks:java:extensions:sql:expansion-service:shadowJar') + self._sql_provider = sql_provider + + def available(self): + return self._sql_provider.available() + + def cache_artifacts(self): + return self._sql_provider.cache_artifacts() + + def provided_transforms(self) -> Iterable[str]: + return [ + 'Filter-sql', + 'Filter-calcite', + 'MapToFields-sql', + 'MapToFields-calcite' + ] + + def create_transform( + self, + typ: str, + args: Mapping[str, Any], + yaml_create_transform: Callable[ + [Mapping[str, Any], Iterable[beam.PCollection]], beam.PTransform] + ) -> beam.PTransform: + if typ.startswith('Filter-'): + return _SqlFilterTransform( + self._sql_provider, yaml_create_transform, **args) + if typ.startswith('MapToFields-'): + return _SqlMapToFieldsTransform( + self._sql_provider, yaml_create_transform, **args) + else: + raise NotImplementedError(typ) + + def underlying_provider(self): + return self._sql_provider + + def to_json(self): + return {'type': "SqlMappingProvider"} + + +@beam.ptransform.ptransform_fn +def _SqlFilterTransform( + pcoll, sql_provider, yaml_create_transform, keep, language): + return pcoll | sql_provider.create_transform( + 'Sql', {'query': f'SELECT * FROM PCOLLECTION WHERE {keep}'}, + yaml_create_transform) -def create_mapping_provider(): +@beam.ptransform.ptransform_fn +def _SqlMapToFieldsTransform( + pcoll, sql_provider, yaml_create_transform, **mapping_args): + _, fields = normalize_fields(pcoll, **mapping_args) + + def extract_expr(name, v): + if isinstance(v, str): + return v + elif 'expression' in v: + return v['expression'] + else: + raise ValueError("Only expressions allowed in SQL at {name}.") + + selects = [ + f'({extract_expr(name, expr)}) AS {name}' + for (name, expr) in fields.items() + ] + query = "SELECT " + ", ".join(selects) + " FROM PCOLLECTION" + return pcoll | sql_provider.create_transform( + 'Sql', {'query': query}, yaml_create_transform) + + +def create_mapping_providers(): # These are MetaInlineProviders because their expansion is in terms of other # YamlTransforms, but in a way that needs to be deferred until the input # schema is known. - return yaml_provider.MetaInlineProvider({ - 'MapToFields': MapToFields, - 'Filter': ( - lambda yaml_create_transform, - keep, - **kwargs: MapToFields( - yaml_create_transform, - keep=keep, - fields={}, - append=True, - transform_name='Filter', - **kwargs)), - 'Explode': ( - lambda yaml_create_transform, - explode, - **kwargs: MapToFields( - yaml_create_transform, - explode=explode, - fields={}, - append=True, - transform_name='Explode', - **kwargs)), - }) + return [ + yaml_provider.InlineProvider({ + 'Explode': _Explode, + 'Filter-python': _PyJsFilter, + 'Filter-javascript': _PyJsFilter, + 'MapToFields-python': _PyJsMapToFields, + 'MapToFields-javascript': _PyJsMapToFields, + 'MapToFields-generic': _PyJsMapToFields, + }), + SqlMappingProvider(), + ] diff --git a/sdks/python/apache_beam/yaml/yaml_mapping_test.py b/sdks/python/apache_beam/yaml/yaml_mapping_test.py index 728476b1fd5d..55032aeae52e 100644 --- a/sdks/python/apache_beam/yaml/yaml_mapping_test.py +++ b/sdks/python/apache_beam/yaml/yaml_mapping_test.py @@ -82,18 +82,18 @@ def test_filter(self): elements = p | beam.Create(DATA) result = elements | YamlTransform( ''' - type: MapToFields + type: Filter input: input config: language: python - fields: - label: label keep: "rank > 0" ''') assert_that( - result, equal_to([ - beam.Row(label='37a'), - beam.Row(label='389a'), + result + | beam.Map(lambda named_tuple: beam.Row(**named_tuple._asdict())), + equal_to([ + beam.Row(label='37a', conductor=37, rank=1), + beam.Row(label='389a', conductor=389, rank=2), ])) def test_explode(self): @@ -105,15 +105,19 @@ def test_explode(self): ]) result = elements | YamlTransform( ''' - type: MapToFields + type: chain input: input - config: - language: python - append: true - fields: - range: "range(a)" - explode: [range, b] - cross_product: true + transforms: + - type: MapToFields + config: + language: python + append: true + fields: + range: "range(a)" + - type: Explode + config: + fields: [range, b] + cross_product: true ''') assert_that( result, diff --git a/sdks/python/apache_beam/yaml/yaml_provider.py b/sdks/python/apache_beam/yaml/yaml_provider.py index d01852a69c39..0cd9bdcadcc3 100644 --- a/sdks/python/apache_beam/yaml/yaml_provider.py +++ b/sdks/python/apache_beam/yaml/yaml_provider.py @@ -209,6 +209,7 @@ def provider_from_spec(cls, spec): def register_provider_type(cls, type_name): def apply(constructor): cls._provider_types[type_name] = constructor + return constructor return apply @@ -709,19 +710,21 @@ def merge_providers(*provider_sets): transform_type: [provider] for transform_type in provider.provided_transforms() } + elif isinstance(provider_set, list): + provider_set = merge_providers(*provider_set) for transform_type, providers in provider_set.items(): result[transform_type].extend(providers) return result def standard_providers(): - from apache_beam.yaml.yaml_mapping import create_mapping_provider + from apache_beam.yaml.yaml_mapping import create_mapping_providers from apache_beam.yaml.yaml_io import io_providers with open(os.path.join(os.path.dirname(__file__), 'standard_providers.yaml')) as fin: standard_providers = yaml.load(fin, Loader=SafeLoader) return merge_providers( create_builtin_provider(), - create_mapping_provider(), + create_mapping_providers(), io_providers(), parse_providers(standard_providers)) diff --git a/sdks/python/apache_beam/yaml/yaml_transform.py b/sdks/python/apache_beam/yaml/yaml_transform.py index da9bf526cd59..78546aa28cb1 100644 --- a/sdks/python/apache_beam/yaml/yaml_transform.py +++ b/sdks/python/apache_beam/yaml/yaml_transform.py @@ -879,8 +879,23 @@ def ensure_transforms_have_providers(spec): f'Unknown type or missing provider for {identify_object(spec)}') return spec + def preprocess_langauges(spec): + if spec['type'] in ('Filter', 'MapToFields'): + language = spec.get('config', {}).get('language', 'generic') + new_type = spec['type'] + '-' + language + if known_transforms and new_type not in known_transforms: + if language == 'generic': + raise ValueError(f'Missing language for {identify_object(spec)}') + else: + raise ValueError( + f'Unknown language {language} for {identify_object(spec)}') + return dict(spec, type=new_type, name=spec.get('name', spec['type'])) + else: + return spec + for phase in [ ensure_transforms_have_types, + preprocess_langauges, ensure_transforms_have_providers, preprocess_source_sink, preprocess_chain, diff --git a/sdks/python/apache_beam/yaml/yaml_transform_test.py b/sdks/python/apache_beam/yaml/yaml_transform_test.py index 993f9ea6639b..ebf12710d3f2 100644 --- a/sdks/python/apache_beam/yaml/yaml_transform_test.py +++ b/sdks/python/apache_beam/yaml/yaml_transform_test.py @@ -419,21 +419,27 @@ def test_mapping_errors(self): input: Create config: fn: "lambda x: beam.Row(num=x, str='a' * x or 'bbb')" + - type: Filter + input: ToRow + config: + language: python + keep: + str[1] >= 'a' + error_handling: + output: errors - type: MapToFields name: MapWithErrorHandling - input: ToRow + input: Filter config: language: python fields: num: num inverse: float(1 / num) - keep: - str[1] >= 'a' error_handling: output: errors - type: PyMap name: TrimErrors - input: MapWithErrorHandling.errors + input: [MapWithErrorHandling.errors, Filter.errors] config: fn: "lambda x: x.msg" - type: MapToFields diff --git a/sdks/python/apache_beam/yaml/yaml_udf_test.py b/sdks/python/apache_beam/yaml/yaml_udf_test.py index bb15cd494757..5e9faa08253c 100644 --- a/sdks/python/apache_beam/yaml/yaml_udf_test.py +++ b/sdks/python/apache_beam/yaml/yaml_udf_test.py @@ -28,6 +28,10 @@ from apache_beam.yaml.yaml_transform import YamlTransform +def AsRows(): + return beam.Map(lambda named_tuple: beam.Row(**named_tuple._asdict())) + + class YamlUDFMappingTest(unittest.TestCase): def __init__(self, method_name='runYamlMappingTest'): super().__init__(method_name) @@ -59,12 +63,11 @@ def test_map_to_fields_filter_inline_js(self): callable: "function label_map(x) {return x.label + 'x'}" conductor: callable: "function conductor_map(x) {return x.conductor + 1}" - keep: - callable: "function filter(x) {return x.rank > 0}" ''') assert_that( result, equal_to([ + beam.Row(label='11ax', conductor=12), beam.Row(label='37ax', conductor=38), beam.Row(label='389ax', conductor=390), ])) @@ -84,12 +87,11 @@ def test_map_to_fields_filter_inline_py(self): callable: "lambda x: x.label + 'x'" conductor: callable: "lambda x: x.conductor + 1" - keep: - callable: "lambda x: x.rank > 0" ''') assert_that( result, equal_to([ + beam.Row(label='11ax', conductor=12), beam.Row(label='37ax', conductor=38), beam.Row(label='389ax', conductor=390), ])) @@ -104,11 +106,11 @@ def test_filter_inline_js(self): input: input config: language: javascript - keep: + keep: callable: "function filter(x) {return x.rank > 0}" ''') assert_that( - result, + result | AsRows(), equal_to([ beam.Row(label='37a', conductor=37, rank=1), beam.Row(label='389a', conductor=389, rank=2), @@ -124,11 +126,11 @@ def test_filter_inline_py(self): input: input config: language: python - keep: + keep: callable: "lambda x: x.rank > 0" ''') assert_that( - result, + result | AsRows(), equal_to([ beam.Row(label='37a', conductor=37, rank=1), beam.Row(label='389a', conductor=389, rank=2), @@ -144,11 +146,12 @@ def test_filter_expression_js(self): input: input config: language: javascript - keep: + keep: expression: "label.toUpperCase().indexOf('3') == -1 && conductor" ''') assert_that( - result, equal_to([ + result | AsRows(), + equal_to([ beam.Row(label='11a', conductor=11, rank=0), ])) @@ -162,11 +165,12 @@ def test_filter_expression_py(self): input: input config: language: python - keep: + keep: expression: "'3' not in label" ''') assert_that( - result, equal_to([ + result | AsRows(), + equal_to([ beam.Row(label='11a', conductor=11, rank=0), ])) @@ -175,7 +179,7 @@ def test_filter_inline_js_file(self): function f(x) { return x.rank > 0 } - + function g(x) { return x.rank > 1 } @@ -193,12 +197,12 @@ def test_filter_inline_js_file(self): input: input config: language: javascript - keep: + keep: path: {path} name: "f" ''') assert_that( - result, + result | AsRows(), equal_to([ beam.Row(label='37a', conductor=37, rank=1), beam.Row(label='389a', conductor=389, rank=2), @@ -225,12 +229,12 @@ def g(x): input: input config: language: python - keep: + keep: path: {path} name: "f" ''') assert_that( - result, + result | AsRows(), equal_to([ beam.Row(label='37a', conductor=37, rank=1), beam.Row(label='389a', conductor=389, rank=2),

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