diff --git a/.test-infra/mock-apis/README.md b/.test-infra/mock-apis/README.md new file mode 100644 index 000000000000..0165421dbb21 --- /dev/null +++ b/.test-infra/mock-apis/README.md @@ -0,0 +1,116 @@ + + +# Overview + +This directory holds code and related artifacts to support API related +integration tests. + +## System review + +The diagram below summarizes the system design. Integration tests use an API +client that makes calls to a backend service. Prior to fulfilling the response, +the service checks and decrements a quota. Said quota persists in a backend +redis instance that is refreshed on an interval by the +[Refresher](./src/main/go/cmd/service/refresher). + +## Echo Service + +The [Echo Service](./src/main/go/cmd/service/echo) implements a simple gRPC +service that echos a payload. See [echo.proto](./proto/echo/v1/echo.proto) +for details. + +```mermaid +flowchart LR + echoClient --> echoSvc + subgraph "Integration Tests" + echoClient[Echo Client] + end + subgraph Backend + echoSvc[Echo Service<./src/main/go/cmd/service/echo>] + refresher[Refresher<./src/main/go/cmd/service/refresher>] + redis[redis://:6739] + refresher -- SetQuota(<string>,<int64>,<time.Duration>) --> redis + echoSvc -- DecrementQuota(<string>) --> redis + end +``` + +# Writing Integration Tests + +TODO: See https://github.com/apache/beam/issues/28859 + +# Development Dependencies + +| Dependency | Reason | +|-----------------------------------------------------|----------------------------------------------------------------------------------------| +| [go](https://go.dev) | For making code changes in this directory. See [go.mod](go.mod) for required version. | +| [buf](https://github.com/bufbuild/buf#installation) | Optional for when making changes to proto. | +| [ko](https://ko.build/install/) | To easily build Go container images. | + +# Testing + +## Unit + +To run unit tests in this project, execute the following command: + +``` +go test ./src/main/go/internal/... +``` + +## Integration + +TODO: See https://github.com/apache/beam/issues/28859 + +# Local Usage + +## Requirements + +To execute the services on your local machine, you'll need [redis](https://redis.io/docs/getting-started/installation/). + +## Execute services + +Follow these steps to run the services on your local machine. + + +1. Start redis + + Start redis using the following command. + ``` + redis-server + ``` + +1. Start the refresher service in a new terminal. + ``` + export CACHE_HOST=localhost:6379; \ + export QUOTA_ID=$(uuidgen); \ + export QUOTA_REFRESH_INTERVAL=10s; \ + export QUOTA_SIZE=100; \ + go run ./src/main/go/cmd/service/refresher + ``` +1. Start the echo service in a new terminal. + ``` + export HTTP_PORT=8080; \ + export GRPC_PORT=50051; \ + export CACHE_HOST=localhost:6379; \ + go run ./src/main/go/cmd/service/echo + ``` + +# Deployment + +TODO: See https://github.com/apache/beam/issues/28709 diff --git a/.test-infra/mock-apis/buf.gen.yaml b/.test-infra/mock-apis/buf.gen.yaml new file mode 100644 index 000000000000..31e57ff2da1e --- /dev/null +++ b/.test-infra/mock-apis/buf.gen.yaml @@ -0,0 +1,40 @@ +# 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. + +# buf.gen.yaml configures proto stub generation using buf. +# +# Requirements: +# - go install google.golang.org/protobuf/cmd/protoc-gen-go@latest +# - go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest +# - https://github.com/grpc/grpc-java/blob/master/compiler/README.md#grpc-java-codegen-plugin-for-protobuf-compiler +# - https://grpc.io/docs/languages/python/quickstart/#grpc-tools +# +# Usage: +# Open a terminal in the same directory as this file and run: +# +# buf generate +# +# See https://buf.build/docs/ for more details. + +version: v1 +plugins: +- name: go + out: src/main/go/internal +- name: go-grpc + out: src/main/go/internal +- name: java + out: src/main/java +- name: grpc-java + out: src/main/java \ No newline at end of file diff --git a/.test-infra/mock-apis/buf.lock b/.test-infra/mock-apis/buf.lock new file mode 100644 index 000000000000..1304ceb9973e --- /dev/null +++ b/.test-infra/mock-apis/buf.lock @@ -0,0 +1,7 @@ +# Generated by buf. DO NOT EDIT. +version: v1 +deps: + - remote: buf.build + owner: googleapis + repository: googleapis + commit: 28151c0d0a1641bf938a7672c500e01d diff --git a/.test-infra/mock-apis/buf.yaml b/.test-infra/mock-apis/buf.yaml new file mode 100644 index 000000000000..419e020247f9 --- /dev/null +++ b/.test-infra/mock-apis/buf.yaml @@ -0,0 +1,20 @@ +# 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. + +# Configures buf to include various proto dependencies. +# See buf.build for details. +version: v1 +deps: +- buf.build/googleapis/googleapis \ No newline at end of file diff --git a/.test-infra/mock-apis/build.gradle b/.test-infra/mock-apis/build.gradle new file mode 100644 index 000000000000..64b7e8c614cc --- /dev/null +++ b/.test-infra/mock-apis/build.gradle @@ -0,0 +1,44 @@ +/* + * 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. + */ + + plugins { + id 'org.apache.beam.module' + } + + applyJavaNature( + exportJavadoc: false, + publish: false, + ) + +description = "Apache Beam :: Test Infra :: Mock APIs" +ext.summary = "Supports API related integration tests." + +def guavaVersion = "31.1-jre" +def ioGrpcApiVersion = "1.53.0" +def protobufVersion = "1.55.1" +def protobufJavaVersion = "3.23.2" + + dependencies { + + // Required by autogenerated proto classes. + implementation "io.grpc:grpc-api:${ioGrpcApiVersion}" + implementation "com.google.guava:guava:${guavaVersion}" + implementation "io.grpc:grpc-protobuf:${protobufVersion}" + implementation "com.google.protobuf:protobuf-java:${protobufJavaVersion}" + implementation "io.grpc:grpc-stub:${protobufVersion}" + } \ No newline at end of file diff --git a/.test-infra/mock-apis/go.mod b/.test-infra/mock-apis/go.mod new file mode 100644 index 000000000000..6f88254dba81 --- /dev/null +++ b/.test-infra/mock-apis/go.mod @@ -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. + +// This module contains all Go code used for Beam's SDKs. This file is placed +// in this directory in order to cover the go code required for Java and Python +// containers, as well as the entire Go SDK. Placing this file in the repository +// root is not possible because it causes conflicts with a pre-existing vendor +// directory. +module github.com/apache/beam/test-infra/mock-apis + +go 1.21 + +require ( + cloud.google.com/go/logging v1.8.1 + cloud.google.com/go/monitoring v1.16.0 + github.com/google/go-cmp v0.5.9 + github.com/redis/go-redis/v9 v9.2.1 + google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d + google.golang.org/grpc v1.58.2 + google.golang.org/protobuf v1.31.0 +) + +require ( + cloud.google.com/go v0.110.6 // indirect + cloud.google.com/go/compute v1.23.0 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/longrunning v0.5.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/s2a-go v0.1.4 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.4 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect + go.opencensus.io v0.24.0 // indirect + golang.org/x/crypto v0.13.0 // indirect + golang.org/x/net v0.15.0 // indirect + golang.org/x/oauth2 v0.12.0 // indirect + golang.org/x/sync v0.3.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/api v0.128.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect +) diff --git a/.test-infra/mock-apis/go.sum b/.test-infra/mock-apis/go.sum new file mode 100644 index 000000000000..56d3a2b193dc --- /dev/null +++ b/.test-infra/mock-apis/go.sum @@ -0,0 +1,214 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q= +cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= +cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y= +cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= +cloud.google.com/go/logging v1.8.1 h1:26skQWPeYhvIasWKm48+Eq7oUqdcdbwsCVwz5Ys0FvU= +cloud.google.com/go/logging v1.8.1/go.mod h1:TJjR+SimHwuC8MZ9cjByQulAMgni+RkXeI3wwctHJEI= +cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI= +cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc= +cloud.google.com/go/monitoring v1.16.0 h1:rlndy4K8yknMY9JuGe2aK4SbCh21FXoCdX7SAGHmRgI= +cloud.google.com/go/monitoring v1.16.0/go.mod h1:Ptp15HgAyM1fNICAojDMoNc/wUmn67mLHQfyqbw+poY= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= +github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.2.4 h1:uGy6JWR/uMIILU8wbf+OkstIrNiMjGpEIyhx8f6W7s4= +github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg= +github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +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/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.128.0 h1:RjPESny5CnQRn9V6siglged+DZCgfu9l6mO9dkX9VOg= +google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750= +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.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= +google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= +google.golang.org/grpc v1.58.2/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= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/.test-infra/mock-apis/proto/echo/v1/echo.proto b/.test-infra/mock-apis/proto/echo/v1/echo.proto new file mode 100644 index 000000000000..826dc0f233fd --- /dev/null +++ b/.test-infra/mock-apis/proto/echo/v1/echo.proto @@ -0,0 +1,46 @@ +/* + * 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. + */ + +/* + * Protocol buffers describing a simple mock API that echos a request. + */ + +syntax = "proto3"; + +package proto.echo.v1; +option go_package = "proto/echo/v1"; +option java_package = "org.apache.beam.testinfra.mockapis.echo.v1"; + +// EchoService simulates a mock API that echos a request. +service EchoService { + + // Echo an EchoRequest payload in an EchoResponse. + rpc Echo(EchoRequest) returns (EchoResponse) {} +} + +// The request to echo a payload. +message EchoRequest { + string id = 1; + bytes payload = 2; +} + +// The response echo of a request payload. +message EchoResponse { + string id = 1; + bytes payload = 2; +} \ No newline at end of file diff --git a/.test-infra/mock-apis/src/main/go/cmd/service/echo/main.go b/.test-infra/mock-apis/src/main/go/cmd/service/echo/main.go new file mode 100644 index 000000000000..891468a603a1 --- /dev/null +++ b/.test-infra/mock-apis/src/main/go/cmd/service/echo/main.go @@ -0,0 +1,148 @@ +// 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. + +// echo is an executable that runs the echov1.EchoService. +package main + +import ( + "context" + "fmt" + "log/slog" + "net" + "net/http" + "os" + "os/signal" + + gcplogging "cloud.google.com/go/logging" + "github.com/apache/beam/test-infra/mock-apis/src/main/go/internal/cache" + "github.com/apache/beam/test-infra/mock-apis/src/main/go/internal/environment" + "github.com/apache/beam/test-infra/mock-apis/src/main/go/internal/logging" + "github.com/apache/beam/test-infra/mock-apis/src/main/go/internal/service/echo" + "github.com/redis/go-redis/v9" + "google.golang.org/grpc" +) + +var ( + env = []environment.Variable{ + environment.CacheHost, + environment.GrpcPort, + environment.HttpPort, + } + + logger *slog.Logger + logAttrs []slog.Attr + opts = &logging.Options{ + Name: "echo", + } +) + +func init() { + for _, v := range env { + logAttrs = append(logAttrs, slog.Attr{ + Key: v.Key(), + Value: slog.StringValue(v.Value()), + }) + } +} + +func main() { + ctx := context.Background() + + if !environment.ProjectId.Missing() { + client, err := gcplogging.NewClient(ctx, environment.ProjectId.Value()) + if err != nil { + slog.LogAttrs(ctx, slog.LevelError, err.Error(), logAttrs...) + os.Exit(1) + } + + opts.Client = client + } + + logger = logging.New(opts) + + if err := run(ctx); err != nil { + logger.LogAttrs(ctx, slog.LevelError, err.Error(), logAttrs...) + os.Exit(1) + } +} + +func run(ctx context.Context) error { + ctx, cancel := signal.NotifyContext(ctx, os.Interrupt) + defer cancel() + + if err := environment.Missing(env...); err != nil { + return err + } + + grpcPort, err := environment.GrpcPort.Int() + if err != nil { + return err + } + grpcAddress := fmt.Sprintf(":%v", grpcPort) + + httpPort, err := environment.HttpPort.Int() + if err != nil { + return err + } + httpAddress := fmt.Sprintf(":%v", httpPort) + + s := grpc.NewServer() + defer s.GracefulStop() + + r := redis.NewClient(&redis.Options{ + Addr: environment.CacheHost.Value(), + }) + + echoOpts := &echo.Options{ + Decrementer: (*cache.RedisCache)(r), + LoggingAttrs: logAttrs, + Logger: logger, + // TODO(damondouglas): add GCP metrics client + // MetricsWriter: + } + + handler, err := echo.Register(s, echoOpts) + if err != nil { + return err + } + + logger.LogAttrs(ctx, slog.LevelInfo, "starting service", logAttrs...) + + lis, err := net.Listen("tcp", grpcAddress) + if err != nil { + return err + } + + errChan := make(chan error) + go func() { + if err := s.Serve(lis); err != nil { + errChan <- err + } + }() + + go func() { + if err := http.ListenAndServe(httpAddress, handler); err != nil { + errChan <- err + } + }() + + select { + case err := <-errChan: + return err + case <-ctx.Done(): + logger.LogAttrs(ctx, slog.LevelInfo, "shutting down", logAttrs...) + return nil + } +} diff --git a/.test-infra/mock-apis/src/main/go/cmd/service/refresher/main.go b/.test-infra/mock-apis/src/main/go/cmd/service/refresher/main.go new file mode 100644 index 000000000000..63e32671935c --- /dev/null +++ b/.test-infra/mock-apis/src/main/go/cmd/service/refresher/main.go @@ -0,0 +1,121 @@ +// 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. + +// refresher is an executable that runs the cache.Refresher service. +package main + +import ( + "context" + "log/slog" + "os" + "os/signal" + + gcplogging "cloud.google.com/go/logging" + "github.com/apache/beam/test-infra/mock-apis/src/main/go/internal/cache" + "github.com/apache/beam/test-infra/mock-apis/src/main/go/internal/environment" + "github.com/apache/beam/test-infra/mock-apis/src/main/go/internal/logging" + "github.com/redis/go-redis/v9" +) + +var ( + env = []environment.Variable{ + environment.CacheHost, + environment.QuotaId, + environment.QuotaSize, + environment.QuotaRefreshInterval, + } + logger *slog.Logger + logAttrs []slog.Attr + opts = &logging.Options{ + Name: "refresher", + } +) + +func init() { + for _, v := range env { + logAttrs = append(logAttrs, slog.Attr{ + Key: v.Key(), + Value: slog.StringValue(v.Value()), + }) + } +} + +func main() { + ctx := context.Background() + + if !environment.ProjectId.Missing() { + client, err := gcplogging.NewClient(ctx, environment.ProjectId.Value()) + if err != nil { + slog.LogAttrs(ctx, slog.LevelError, err.Error(), logAttrs...) + os.Exit(1) + } + + opts.Client = client + } + + logger = logging.New(opts) + if err := run(ctx); err != nil { + logger.LogAttrs(ctx, slog.LevelError, err.Error(), logAttrs...) + os.Exit(1) + } +} + +func run(ctx context.Context) error { + ctx, cancel := signal.NotifyContext(ctx, os.Interrupt) + defer cancel() + + if err := environment.Missing(env...); err != nil { + return err + } + + size, err := environment.QuotaSize.UInt64() + if err != nil { + return err + } + + interval, err := environment.QuotaRefreshInterval.Duration() + if err != nil { + return err + } + + r := redis.NewClient(&redis.Options{ + Addr: environment.CacheHost.Value(), + }) + + opts := &cache.Options{ + Logger: logger, + Setter: (*cache.RedisCache)(r), + } + + ref, err := cache.NewRefresher(ctx, opts) + if err != nil { + return err + } + + errChan := make(chan error) + go func() { + if err := ref.Refresh(ctx, environment.QuotaId.Value(), size, interval); err != nil { + errChan <- err + } + }() + + select { + case err := <-errChan: + return err + case <-ctx.Done(): + return nil + } + +} diff --git a/.test-infra/mock-apis/src/main/go/internal/cache/cache.go b/.test-infra/mock-apis/src/main/go/internal/cache/cache.go new file mode 100644 index 000000000000..cab20ad998cc --- /dev/null +++ b/.test-infra/mock-apis/src/main/go/internal/cache/cache.go @@ -0,0 +1,122 @@ +// 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 cache + +import ( + "context" + "errors" + "fmt" + "log/slog" + "reflect" + "time" + + "github.com/apache/beam/test-infra/mock-apis/src/main/go/internal/logging" +) + +var ( + + // ErrNotExist is an error indicating that a resource does not exist + ErrNotExist = errors.New("resource does not exist") +) + +// IsNotExist is true when err is ErrNotExist. +func IsNotExist(err error) bool { + return errors.Is(err, ErrNotExist) +} + +// Options for running the Refresher. +type Options struct { + Setter UInt64Setter + Logger *slog.Logger +} + +// Refresher refreshes a value in a cache on a set interval. +type Refresher struct { + opts *Options + stop chan struct{} +} + +// NewRefresher instantiates a Refresher. +func NewRefresher(ctx context.Context, opts *Options) (*Refresher, error) { + if opts.Logger == nil { + opts.Logger = logging.New(&logging.Options{ + Name: reflect.TypeOf((*Refresher)(nil)).PkgPath(), + }) + } + + if opts.Setter == nil { + return nil, fmt.Errorf("%T.Setter is nil but required", opts) + } + + if err := opts.Setter.Alive(ctx); err != nil { + return nil, err + } + + ref := &Refresher{ + opts: opts, + } + + return ref, nil +} + +// Stop the Refresher. +func (ref *Refresher) Stop() { + ref.stop <- struct{}{} +} + +// Refresh the size of the associated key at an interval. +func (ref *Refresher) Refresh(ctx context.Context, key string, size uint64, interval time.Duration) error { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + ref.stop = make(chan struct{}) + attrs := []slog.Attr{ + { + Key: "key", + Value: slog.StringValue(key), + }, + { + Key: "size", + Value: slog.Uint64Value(size), + }, + { + Key: "interval", + Value: slog.StringValue(interval.String()), + }, + } + + ref.opts.Logger.LogAttrs(ctx, slog.LevelInfo, "starting refresher service", attrs...) + + if err := ref.opts.Setter.Set(ctx, key, size, interval); err != nil { + return err + } + ref.opts.Logger.LogAttrs(ctx, slog.LevelDebug, "successful initial refresh", attrs...) + + tick := time.Tick(interval) + for { + select { + case <-tick: + if err := ref.opts.Setter.Set(ctx, key, size, interval); err != nil { + return err + } + ref.opts.Logger.LogAttrs(ctx, slog.LevelDebug, "refresh successful", attrs...) + case <-ref.stop: + ref.opts.Logger.LogAttrs(ctx, slog.LevelInfo, "stopping refresher service", attrs...) + return nil + case <-ctx.Done(): + return nil + } + } +} diff --git a/.test-infra/mock-apis/src/main/go/internal/cache/doc.go b/.test-infra/mock-apis/src/main/go/internal/cache/doc.go new file mode 100644 index 000000000000..c0f937aa8d59 --- /dev/null +++ b/.test-infra/mock-apis/src/main/go/internal/cache/doc.go @@ -0,0 +1,17 @@ +// 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 cache stores and retrieves data from a cache. +package cache diff --git a/.test-infra/mock-apis/src/main/go/internal/cache/interface.go b/.test-infra/mock-apis/src/main/go/internal/cache/interface.go new file mode 100644 index 000000000000..8266f7205885 --- /dev/null +++ b/.test-infra/mock-apis/src/main/go/internal/cache/interface.go @@ -0,0 +1,45 @@ +// 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 cache + +import ( + "context" + "time" +) + +// HealthChecker checks the health and availability of a resource. +type HealthChecker interface { + + // Alive checks whether the resource is healthy and available. + Alive(ctx context.Context) error +} + +// UInt64Setter associates a key with a value for an expiry time.Duration. +type UInt64Setter interface { + HealthChecker + + // Set a key with a value for an expiry time.Duration. + Set(ctx context.Context, key string, value uint64, expiry time.Duration) error +} + +// Decrementer decrements a value associated with a key. +type Decrementer interface { + HealthChecker + + // Decrement the value associated with a key; returns the value after + // decrementing it. + Decrement(ctx context.Context, key string) (int64, error) +} diff --git a/.test-infra/mock-apis/src/main/go/internal/cache/redis.go b/.test-infra/mock-apis/src/main/go/internal/cache/redis.go new file mode 100644 index 000000000000..51ad73061cb7 --- /dev/null +++ b/.test-infra/mock-apis/src/main/go/internal/cache/redis.go @@ -0,0 +1,59 @@ +// 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 cache + +import ( + "context" + "time" + + "github.com/redis/go-redis/v9" +) + +// Validate interface implementations +var _ UInt64Setter = &RedisCache{} +var _ Decrementer = &RedisCache{} +var _ HealthChecker = &RedisCache{} + +// RedisCache implements a Decrementer and a Refresher. +type RedisCache redis.Client + +// Set implements Refresher's Set method using a redis cache where expiry of 0 +// has no expiration. Returns any error from the redis client. +func (client *RedisCache) Set(ctx context.Context, key string, value uint64, expiry time.Duration) error { + r := (*redis.Client)(client) + return r.Set(ctx, key, value, expiry).Err() +} + +// Decrement implements Decrementer's Decrement method using a redis cache. +// Returns an error when the key does not exist or from the redis client. +func (client *RedisCache) Decrement(ctx context.Context, key string) (int64, error) { + r := (*redis.Client)(client) + v, err := r.Exists(ctx, key).Result() + if err != nil { + return -1, err + } + if v == 0 { + return -1, ErrNotExist + } + return r.Decr(ctx, key).Result() +} + +// Alive implements HealthChecker's Alive checking the availability of a +// redis cache. Returns an error if no successful connection. +func (client *RedisCache) Alive(ctx context.Context) error { + r := (*redis.Client)(client) + return r.Ping(ctx).Err() +} diff --git a/.test-infra/mock-apis/src/main/go/internal/environment/variable.go b/.test-infra/mock-apis/src/main/go/internal/environment/variable.go new file mode 100644 index 000000000000..b1e3a8ec4011 --- /dev/null +++ b/.test-infra/mock-apis/src/main/go/internal/environment/variable.go @@ -0,0 +1,118 @@ +// 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 environment provides helpers for interacting with environment variables. +package environment + +import ( + "fmt" + "os" + "strconv" + "strings" + "time" +) + +var ( + // HttpPort is the port to bind an HTTP service. + HttpPort Variable = "HTTP_PORT" + + // GrpcPort is the port to bind a gRPC service. + GrpcPort Variable = "GRPC_PORT" + + // CacheHost is the host address of the cache. + CacheHost Variable = "CACHE_HOST" + + // ProjectId is the ID of the Google Cloud host project. + ProjectId Variable = "PROJECT_ID" + + // QuotaId uniquely identifies a quota measure. + QuotaId Variable = "QUOTA_ID" + + // QuotaSize specifies the size of the quota. + QuotaSize Variable = "QUOTA_SIZE" + + // QuotaRefreshInterval configures how often a quota is refreshed. + QuotaRefreshInterval Variable = "QUOTA_REFRESH_INTERVAL" +) + +// Variable defines an environment variable via a string type alias. +// Variable's string defaultValue assigns the system environment variable key. +type Variable string + +// Default a default value to the system environment. +func (v Variable) Default(value string) error { + if v.Missing() { + return os.Setenv((string)(v), value) + } + return nil +} + +// MustDefault a default value to the system environment. Panics on error. +func (v Variable) MustDefault(value string) { + if err := v.Default(value); err != nil { + panic(err) + } +} + +// Missing reports whether the system environment variable is an empty string. +func (v Variable) Missing() bool { + return v.Value() == "" +} + +// Key returns the system environment variable key. +func (v Variable) Key() string { + return (string)(v) +} + +// Value returns the system environment variable value. +func (v Variable) Value() string { + return os.Getenv((string)(v)) +} + +// Int returns the system environment variable parsed as an int. +func (v Variable) Int() (int, error) { + return strconv.Atoi(v.Value()) +} + +// UInt64 returns the system environment variable value parsed as a uint64. +func (v Variable) UInt64() (uint64, error) { + return strconv.ParseUint(v.Value(), 10, 64) +} + +// Duration returns the system environment variable value parsed as time.Duration. +func (v Variable) Duration() (time.Duration, error) { + return time.ParseDuration(v.Value()) +} + +// KeyValue returns a concatenated string of the system environment variable's +// =. +func (v Variable) KeyValue() string { + return fmt.Sprintf("%s=%s", (string)(v), v.Value()) +} + +// Missing reports as an error listing all Variable among vars that are +// not assigned in the system environment. +func Missing(vars ...Variable) error { + var missing []string + for _, v := range vars { + if v.Missing() { + missing = append(missing, v.KeyValue()) + } + } + if len(missing) > 0 { + return fmt.Errorf("variables empty but expected from environment: %s", strings.Join(missing, "; ")) + } + return nil +} diff --git a/.test-infra/mock-apis/src/main/go/internal/environment/variable_test.go b/.test-infra/mock-apis/src/main/go/internal/environment/variable_test.go new file mode 100644 index 000000000000..b566f14d446b --- /dev/null +++ b/.test-infra/mock-apis/src/main/go/internal/environment/variable_test.go @@ -0,0 +1,312 @@ +// 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 environment + +import ( + "errors" + "os" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestMissing(t *testing.T) { + type args struct { + vars []Variable + values []string + } + tests := []struct { + name string + args args + want error + }{ + { + name: "{}", + args: args{}, + }, + { + name: "{A=}", + args: args{ + vars: []Variable{ + "A", + }, + values: []string{ + "", + }, + }, + want: errors.New("variables empty but expected from environment: A="), + }, + { + name: "{A=1}", + args: args{ + vars: []Variable{ + "A", + }, + values: []string{ + "1", + }, + }, + want: nil, + }, + { + name: "{A=; B=}", + args: args{ + vars: []Variable{ + "A", + "B", + }, + values: []string{ + "", + "", + }, + }, + want: errors.New("variables empty but expected from environment: A=; B="), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got, want string + clearVars(tt.args.vars...) + set(t, tt.args.vars, tt.args.values) + err := Missing(tt.args.vars...) + if err != nil { + got = err.Error() + } + if tt.want != nil { + want = tt.want.Error() + } + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("Missing() error returned unexpected difference in error messages (-want +got):\n%s", diff) + } + }) + } +} + +func TestVariable_Default(t *testing.T) { + type args struct { + setValue string + defaultValue string + } + tests := []struct { + name string + v Variable + args args + want string + }{ + { + name: "environment variable not set", + v: "A", + args: args{ + defaultValue: "1", + }, + want: "1", + }, + { + name: "environment variable default is overridden by set value", + v: "A", + args: args{ + setValue: "2", + defaultValue: "1", + }, + want: "2", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + clearVars(tt.v) + if tt.args.setValue != "" { + set(t, []Variable{tt.v}, []string{tt.args.setValue}) + } + if err := tt.v.Default(tt.args.defaultValue); err != nil { + t.Fatalf("could not set default environment variable value during test execution: %v", err) + } + got := os.Getenv(tt.v.Key()) + if got != tt.want { + t.Errorf("Default() = %s, want %s", got, tt.want) + } + }) + } +} + +func TestVariable_KeyValue(t *testing.T) { + tests := []struct { + name string + v Variable + value string + want string + }{ + { + name: "environment variable not set", + v: "A", + want: "A=", + }, + { + name: "environment variable is set", + v: "A", + value: "1", + want: "A=1", + }, + } + for _, tt := range tests { + clearVars(tt.v) + t.Run(tt.name, func(t *testing.T) { + set(t, []Variable{tt.v}, []string{tt.value}) + got := tt.v.KeyValue() + if got != tt.want { + t.Errorf("KeyValue() = %s, want %s", got, tt.want) + } + }) + } +} + +func TestVariable_Missing(t *testing.T) { + type args struct { + setValue string + defaultValue string + } + tests := []struct { + name string + args args + v Variable + want bool + }{ + { + name: "no default and not set", + args: args{}, + v: "A", + want: true, + }, + { + name: "has default but not set", + args: args{ + defaultValue: "1", + }, + v: "A", + want: false, + }, + { + name: "no default but set", + args: args{ + setValue: "1", + }, + v: "A", + want: false, + }, + { + name: "has default and set", + args: args{ + setValue: "2", + defaultValue: "1", + }, + v: "A", + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + clearVars(tt.v) + if tt.args.defaultValue != "" { + if err := tt.v.Default(tt.args.defaultValue); err != nil { + t.Fatalf("could not set default environment variable value during test execution: %v", err) + } + } + if tt.args.setValue != "" { + set(t, []Variable{tt.v}, []string{tt.args.setValue}) + } + if got := tt.v.Missing(); got != tt.want { + t.Errorf("Missing() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestVariable_Value(t *testing.T) { + type args struct { + setValue string + defaultValue string + } + tests := []struct { + name string + args args + v Variable + want string + }{ + { + name: "no default and not set", + args: args{}, + v: "A", + want: "", + }, + { + name: "has default but not set", + args: args{ + defaultValue: "1", + }, + v: "A", + want: "1", + }, + { + name: "no default but set", + args: args{ + setValue: "1", + }, + v: "A", + want: "1", + }, + { + name: "has default and set", + args: args{ + setValue: "2", + defaultValue: "1", + }, + v: "A", + want: "2", + }, + } + for _, tt := range tests { + clearVars(tt.v) + if tt.args.defaultValue != "" { + if err := tt.v.Default(tt.args.defaultValue); err != nil { + t.Fatalf("could not set default environment variable value during test execution: %v", err) + } + } + if tt.args.setValue != "" { + set(t, []Variable{tt.v}, []string{tt.args.setValue}) + } + t.Run(tt.name, func(t *testing.T) { + if got := tt.v.Value(); got != tt.want { + t.Errorf("Value() = %v, want %v", got, tt.want) + } + }) + } +} + +func clearVars(vars ...Variable) { + for _, k := range vars { + _ = os.Setenv(k.Key(), "") + } +} + +func set(t *testing.T, vars []Variable, values []string) { + if len(vars) != len(values) { + t.Fatalf("test cases should be configured with matching args.vars and args.values: len(tt.args.vars): %v != len(tt.args.values): %v", len(vars), len(values)) + } + for i := range vars { + key := vars[i].Key() + value := values[i] + _ = os.Setenv(key, value) + } +} diff --git a/.test-infra/mock-apis/src/main/go/internal/logging/logging.go b/.test-infra/mock-apis/src/main/go/internal/logging/logging.go new file mode 100644 index 000000000000..53cead40b0d8 --- /dev/null +++ b/.test-infra/mock-apis/src/main/go/internal/logging/logging.go @@ -0,0 +1,137 @@ +// 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 logging performs structured output of log entries. +package logging + +import ( + "context" + "encoding/json" + "io" + "log/slog" + "os" + "path" + "runtime" + "sync" + + "cloud.google.com/go/logging" + "cloud.google.com/go/logging/apiv2/loggingpb" +) + +// Options for the slog.Logger +type Options struct { + *slog.HandlerOptions + Name string + Writer io.Writer + Client *logging.Client +} + +// New instantiates a slog.Logger to output using Google Cloud logging entries. +// When running locally, output is JSON strings of Cloud logging entries and +// does not make any API calls to the service. When running in Google Cloud, +// logging entries are submitted to the Cloud logging service. +func New(opts *Options) *slog.Logger { + if opts.HandlerOptions == nil { + opts.HandlerOptions = &slog.HandlerOptions{} + } + + opts.AddSource = true + + if opts.Writer == nil { + opts.Writer = os.Stdout + } + + handler := &gcpHandler{ + name: opts.Name, + mu: &sync.Mutex{}, + out: opts.Writer, + JSONHandler: slog.NewJSONHandler(opts.Writer, opts.HandlerOptions), + } + + if opts.Client != nil { + handler.logger = opts.Client.Logger(path.Base(opts.Name)) + } + + return slog.New(handler) +} + +var _ slog.Handler = &gcpHandler{} + +type gcpHandler struct { + name string + *slog.JSONHandler + mu *sync.Mutex + out io.Writer + logger *logging.Logger +} + +func (g *gcpHandler) Enabled(ctx context.Context, level slog.Level) bool { + return g.JSONHandler.Enabled(ctx, level) +} + +func severity(lvl slog.Level) logging.Severity { + switch lvl { + case slog.LevelDebug: + return logging.Debug + case slog.LevelInfo: + return logging.Info + case slog.LevelWarn: + return logging.Warning + case slog.LevelError: + return logging.Error + } + return logging.Default +} + +func (g *gcpHandler) Handle(_ context.Context, record slog.Record) error { + payload := map[string]any{ + "message": record.Message, + } + record.Attrs(func(attr slog.Attr) bool { + payload[attr.Key] = attr.Value.Any() + return true + }) + fs := runtime.CallersFrames([]uintptr{record.PC}) + f, _ := fs.Next() + entry := logging.Entry{ + LogName: g.name, + Timestamp: record.Time, + Severity: severity(record.Level), + Payload: payload, + SourceLocation: &loggingpb.LogEntrySourceLocation{ + File: f.File, + Line: int64(f.Line), + }, + } + g.mu.Lock() + defer g.mu.Unlock() + if g.logger == nil { + return json.NewEncoder(g.out).Encode(entry) + } + + entry.LogName = "" + g.logger.Log(entry) + return g.logger.Flush() +} + +func (g *gcpHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + h := g.JSONHandler + return h.WithAttrs(attrs) +} + +func (g *gcpHandler) WithGroup(name string) slog.Handler { + h := g.JSONHandler + return h.WithGroup(name) +} diff --git a/.test-infra/mock-apis/src/main/go/internal/logging/logging_test.go b/.test-infra/mock-apis/src/main/go/internal/logging/logging_test.go new file mode 100644 index 000000000000..87bfa160981c --- /dev/null +++ b/.test-infra/mock-apis/src/main/go/internal/logging/logging_test.go @@ -0,0 +1,153 @@ +// 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 logging_test + +import ( + "bytes" + "context" + "encoding/json" + "log/slog" + "runtime" + "testing" + "time" + + gcplogging "cloud.google.com/go/logging" + "cloud.google.com/go/logging/apiv2/loggingpb" + "github.com/apache/beam/test-infra/mock-apis/src/main/go/internal/logging" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" +) + +var ( + opts = []cmp.Option{ + cmpopts.IgnoreFields(loggingpb.LogEntrySourceLocation{}, "state", "sizeCache", "unknownFields"), + cmpopts.IgnoreFields(gcplogging.Entry{}, "Timestamp"), + } +) + +func Test_logger_Info(t *testing.T) { + type args struct { + message string + fields []slog.Attr + } + tests := []struct { + name string + args args + want gcplogging.Entry + }{ + { + name: "message only", + args: args{ + message: "hello log", + }, + want: gcplogging.Entry{ + LogName: "message only", + Severity: gcplogging.Info, + Payload: map[string]interface{}{ + "message": "hello log", + }, + }, + }, + { + name: "with flat fields", + args: args{ + message: "message with fields", + fields: []slog.Attr{ + { + Key: "string", + Value: slog.StringValue("a string"), + }, + { + Key: "int", + Value: slog.IntValue(1), + }, + { + Key: "bool", + Value: slog.BoolValue(true), + }, + { + Key: "float", + Value: slog.Float64Value(1.23456789), + }, + }, + }, + want: gcplogging.Entry{ + LogName: "with flat fields", + Severity: gcplogging.Info, + Payload: map[string]interface{}{ + "message": "message with fields", + "string": "a string", + "int": float64(1), + "bool": true, + "float": 1.23456789, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := bytes.Buffer{} + l := logging.New(&logging.Options{ + Name: tt.name, + Writer: &buf, + }) + l.LogAttrs(context.Background(), slog.LevelInfo, tt.args.message, tt.args.fields...) + _, file, line, _ := runtime.Caller(0) + tt.want.SourceLocation = &loggingpb.LogEntrySourceLocation{ + File: file, + Line: int64(line) - 1, + } + var got gcplogging.Entry + if err := json.NewDecoder(&buf).Decode(&got); err != nil { + t.Fatal(err) + } + if diff := cmp.Diff(tt.want, got, opts...); diff != "" { + t.Errorf("LogAttrs(Info) yielded unexpected difference in log entry (-want, +got):\n%s", diff) + } + }) + } +} +func Test_logger_Error(t *testing.T) { + buf := bytes.Buffer{} + l := logging.New(&logging.Options{ + Name: "test logger error", + Writer: &buf, + }) + message := "some error" + fields := []slog.Attr{ + { + Key: "observed", + Value: slog.TimeValue(time.Unix(1000000000, 0)), + }, + } + l.LogAttrs(context.Background(), slog.LevelError, message, fields...) + _, file, line, _ := runtime.Caller(0) + var got gcplogging.Entry + if err := json.NewDecoder(&buf).Decode(&got); err != nil { + t.Fatal(err) + } + if diff := cmp.Diff(gcplogging.Entry{ + LogName: "test logger error", + Severity: gcplogging.Error, + Payload: map[string]any{"message": "some error", "observed": "2001-09-09T01:46:40Z"}, + SourceLocation: &loggingpb.LogEntrySourceLocation{ + File: file, + Line: int64(line) - 1, + }, + }, got, opts...); diff != "" { + t.Errorf("LogAttrs(Error) yielded unexpected difference in log entry (-want, +got):\n%s", diff) + } +} diff --git a/.test-infra/mock-apis/src/main/go/internal/metric/doc.go b/.test-infra/mock-apis/src/main/go/internal/metric/doc.go new file mode 100644 index 000000000000..43bfc771c198 --- /dev/null +++ b/.test-infra/mock-apis/src/main/go/internal/metric/doc.go @@ -0,0 +1,17 @@ +// 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 metric supports monitoring. +package metric diff --git a/.test-infra/mock-apis/src/main/go/internal/metric/gcp.go b/.test-infra/mock-apis/src/main/go/internal/metric/gcp.go new file mode 100644 index 000000000000..3d23d538955a --- /dev/null +++ b/.test-infra/mock-apis/src/main/go/internal/metric/gcp.go @@ -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. + +package metric + +import ( + "context" + "path" + + monitoring "cloud.google.com/go/monitoring/apiv3" + "cloud.google.com/go/monitoring/apiv3/v2/monitoringpb" + "google.golang.org/genproto/googleapis/api/metric" + "google.golang.org/genproto/googleapis/api/monitoredres" + "google.golang.org/protobuf/types/known/timestamppb" +) + +const ( + metricTypePrefix = "custom.googleapis.com" + monitoredResourceType = "generic_task" +) + +// GcpGauge implements a Writer for a Google Cloud gauge. +// See https://cloud.google.com/monitoring/api/v3/kinds-and-types#metric-kinds +type GcpGauge monitoring.MetricClient + +// Write to a Google Cloud monitoring gauge. +func (writer *GcpGauge) Write(ctx context.Context, name string, unit string, points ...*Point) error { + var mPts []*monitoringpb.Point + for _, p := range points { + t := timestamppb.New(p.Timestamp) + mPts = append(mPts, &monitoringpb.Point{ + Interval: &monitoringpb.TimeInterval{ + StartTime: t, + EndTime: t, + }, + Value: &monitoringpb.TypedValue{ + Value: &monitoringpb.TypedValue_Int64Value{ + Int64Value: p.Value, + }, + }, + }) + } + ts := timeseries(name, unit, metric.MetricDescriptor_GAUGE, mPts) + + client := (*monitoring.MetricClient)(writer) + return client.CreateTimeSeries(ctx, &monitoringpb.CreateTimeSeriesRequest{ + Name: name, + TimeSeries: []*monitoringpb.TimeSeries{ts}, + }) +} + +func timeseries(name string, unit string, kind metric.MetricDescriptor_MetricKind, points []*monitoringpb.Point) *monitoringpb.TimeSeries { + return &monitoringpb.TimeSeries{ + Metric: &metric.Metric{ + Type: path.Join(metricTypePrefix, name), + }, + Resource: &monitoredres.MonitoredResource{ + Type: monitoredResourceType, + }, + MetricKind: kind, + ValueType: metric.MetricDescriptor_INT64, + Unit: unit, + Points: points, + } +} diff --git a/.test-infra/mock-apis/src/main/go/internal/metric/interface.go b/.test-infra/mock-apis/src/main/go/internal/metric/interface.go new file mode 100644 index 000000000000..d0f7e385227e --- /dev/null +++ b/.test-infra/mock-apis/src/main/go/internal/metric/interface.go @@ -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. + +package metric + +import ( + "context" + "time" +) + +// Writer writes to a metric sink. +type Writer interface { + + // Write to a metric sink. + Write(ctx context.Context, name string, unit string, points ...*Point) error +} + +// Point models a metric data point. +type Point struct { + + // Timestamp of the metric data point. + Timestamp time.Time + + // Value of the metric data point. + Value int64 +} diff --git a/.test-infra/mock-apis/src/main/go/internal/proto/echo/v1/echo.pb.go b/.test-infra/mock-apis/src/main/go/internal/proto/echo/v1/echo.pb.go new file mode 100644 index 000000000000..97ced9227d7f --- /dev/null +++ b/.test-infra/mock-apis/src/main/go/internal/proto/echo/v1/echo.pb.go @@ -0,0 +1,256 @@ +// +// 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. + +// +// Protocol buffers describing a simple mock API that echos a request. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc (unknown) +// source: proto/echo/v1/echo.proto + +package v1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// The request to echo a payload. +type EchoRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` +} + +func (x *EchoRequest) Reset() { + *x = EchoRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_echo_v1_echo_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EchoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EchoRequest) ProtoMessage() {} + +func (x *EchoRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_echo_v1_echo_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EchoRequest.ProtoReflect.Descriptor instead. +func (*EchoRequest) Descriptor() ([]byte, []int) { + return file_proto_echo_v1_echo_proto_rawDescGZIP(), []int{0} +} + +func (x *EchoRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *EchoRequest) GetPayload() []byte { + if x != nil { + return x.Payload + } + return nil +} + +// The response echo of a request payload. +type EchoResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` +} + +func (x *EchoResponse) Reset() { + *x = EchoResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_echo_v1_echo_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EchoResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EchoResponse) ProtoMessage() {} + +func (x *EchoResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_echo_v1_echo_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EchoResponse.ProtoReflect.Descriptor instead. +func (*EchoResponse) Descriptor() ([]byte, []int) { + return file_proto_echo_v1_echo_proto_rawDescGZIP(), []int{1} +} + +func (x *EchoResponse) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *EchoResponse) GetPayload() []byte { + if x != nil { + return x.Payload + } + return nil +} + +var File_proto_echo_v1_echo_proto protoreflect.FileDescriptor + +var file_proto_echo_v1_echo_proto_rawDesc = []byte{ + 0x0a, 0x18, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x2f, 0x76, 0x31, 0x2f, + 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x76, 0x31, 0x22, 0x37, 0x0a, 0x0b, 0x45, 0x63, 0x68, + 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x22, 0x38, 0x0a, 0x0c, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x32, 0x50, 0x0a, 0x0b, + 0x45, 0x63, 0x68, 0x6f, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x41, 0x0a, 0x04, 0x45, + 0x63, 0x68, 0x6f, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x65, 0x63, 0x68, 0x6f, + 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x76, 0x31, 0x2e, + 0x45, 0x63, 0x68, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x3b, + 0x0a, 0x2a, 0x6f, 0x72, 0x67, 0x2e, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x62, 0x65, 0x61, + 0x6d, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x66, 0x72, 0x61, 0x2e, 0x6d, 0x6f, 0x63, 0x6b, + 0x61, 0x70, 0x69, 0x73, 0x2e, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x76, 0x31, 0x5a, 0x0d, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x63, 0x68, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_proto_echo_v1_echo_proto_rawDescOnce sync.Once + file_proto_echo_v1_echo_proto_rawDescData = file_proto_echo_v1_echo_proto_rawDesc +) + +func file_proto_echo_v1_echo_proto_rawDescGZIP() []byte { + file_proto_echo_v1_echo_proto_rawDescOnce.Do(func() { + file_proto_echo_v1_echo_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_echo_v1_echo_proto_rawDescData) + }) + return file_proto_echo_v1_echo_proto_rawDescData +} + +var file_proto_echo_v1_echo_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_proto_echo_v1_echo_proto_goTypes = []interface{}{ + (*EchoRequest)(nil), // 0: proto.echo.v1.EchoRequest + (*EchoResponse)(nil), // 1: proto.echo.v1.EchoResponse +} +var file_proto_echo_v1_echo_proto_depIdxs = []int32{ + 0, // 0: proto.echo.v1.EchoService.Echo:input_type -> proto.echo.v1.EchoRequest + 1, // 1: proto.echo.v1.EchoService.Echo:output_type -> proto.echo.v1.EchoResponse + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_proto_echo_v1_echo_proto_init() } +func file_proto_echo_v1_echo_proto_init() { + if File_proto_echo_v1_echo_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_proto_echo_v1_echo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EchoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_echo_v1_echo_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EchoResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_echo_v1_echo_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_proto_echo_v1_echo_proto_goTypes, + DependencyIndexes: file_proto_echo_v1_echo_proto_depIdxs, + MessageInfos: file_proto_echo_v1_echo_proto_msgTypes, + }.Build() + File_proto_echo_v1_echo_proto = out.File + file_proto_echo_v1_echo_proto_rawDesc = nil + file_proto_echo_v1_echo_proto_goTypes = nil + file_proto_echo_v1_echo_proto_depIdxs = nil +} diff --git a/.test-infra/mock-apis/src/main/go/internal/proto/echo/v1/echo_grpc.pb.go b/.test-infra/mock-apis/src/main/go/internal/proto/echo/v1/echo_grpc.pb.go new file mode 100644 index 000000000000..3ce2bdeeeec1 --- /dev/null +++ b/.test-infra/mock-apis/src/main/go/internal/proto/echo/v1/echo_grpc.pb.go @@ -0,0 +1,107 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc (unknown) +// source: proto/echo/v1/echo.proto + +package v1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// EchoServiceClient is the client API for EchoService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type EchoServiceClient interface { + // Echo an EchoRequest payload in an EchoResponse. + Echo(ctx context.Context, in *EchoRequest, opts ...grpc.CallOption) (*EchoResponse, error) +} + +type echoServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewEchoServiceClient(cc grpc.ClientConnInterface) EchoServiceClient { + return &echoServiceClient{cc} +} + +func (c *echoServiceClient) Echo(ctx context.Context, in *EchoRequest, opts ...grpc.CallOption) (*EchoResponse, error) { + out := new(EchoResponse) + err := c.cc.Invoke(ctx, "/proto.echo.v1.EchoService/Echo", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// EchoServiceServer is the server API for EchoService service. +// All implementations must embed UnimplementedEchoServiceServer +// for forward compatibility +type EchoServiceServer interface { + // Echo an EchoRequest payload in an EchoResponse. + Echo(context.Context, *EchoRequest) (*EchoResponse, error) + mustEmbedUnimplementedEchoServiceServer() +} + +// UnimplementedEchoServiceServer must be embedded to have forward compatible implementations. +type UnimplementedEchoServiceServer struct { +} + +func (UnimplementedEchoServiceServer) Echo(context.Context, *EchoRequest) (*EchoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Echo not implemented") +} +func (UnimplementedEchoServiceServer) mustEmbedUnimplementedEchoServiceServer() {} + +// UnsafeEchoServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to EchoServiceServer will +// result in compilation errors. +type UnsafeEchoServiceServer interface { + mustEmbedUnimplementedEchoServiceServer() +} + +func RegisterEchoServiceServer(s grpc.ServiceRegistrar, srv EchoServiceServer) { + s.RegisterService(&EchoService_ServiceDesc, srv) +} + +func _EchoService_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EchoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EchoServiceServer).Echo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/proto.echo.v1.EchoService/Echo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EchoServiceServer).Echo(ctx, req.(*EchoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// EchoService_ServiceDesc is the grpc.ServiceDesc for EchoService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var EchoService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "proto.echo.v1.EchoService", + HandlerType: (*EchoServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Echo", + Handler: _EchoService_Echo_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/echo/v1/echo.proto", +} diff --git a/.test-infra/mock-apis/src/main/go/internal/service/echo/echo.go b/.test-infra/mock-apis/src/main/go/internal/service/echo/echo.go new file mode 100644 index 000000000000..d958d18576e5 --- /dev/null +++ b/.test-infra/mock-apis/src/main/go/internal/service/echo/echo.go @@ -0,0 +1,185 @@ +// 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 echo contains the EchoService API implementation. +package echo + +import ( + "context" + "encoding/json" + "fmt" + "log/slog" + "net/http" + "path" + "reflect" + "time" + + "github.com/apache/beam/test-infra/mock-apis/src/main/go/internal/cache" + "github.com/apache/beam/test-infra/mock-apis/src/main/go/internal/logging" + "github.com/apache/beam/test-infra/mock-apis/src/main/go/internal/metric" + echov1 "github.com/apache/beam/test-infra/mock-apis/src/main/go/internal/proto/echo/v1" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/health/grpc_health_v1" + "google.golang.org/grpc/status" +) + +const ( + metricsNamePrefix = "echo" + echoPath = "/proto.echo.v1.EchoService/Echo" + echoPathAlias = "/v1/echo" + healthPath = "/grpc.health.v1.Health/Check" + healthPathAlias = "/v1/healthz" +) + +type Options struct { + Decrementer cache.Decrementer + MetricsWriter metric.Writer + Logger *slog.Logger + LoggingAttrs []slog.Attr +} + +// Register a grpc.Server with the echov1.EchoService. Returns a http.Handler or error. +func Register(s *grpc.Server, opts *Options) (http.Handler, error) { + if opts.Logger == nil { + opts.Logger = logging.New(&logging.Options{ + Name: reflect.TypeOf((*echo)(nil)).PkgPath(), + }) + } + srv := &echo{ + opts: opts, + } + + echov1.RegisterEchoServiceServer(s, srv) + grpc_health_v1.RegisterHealthServer(s, srv) + + return srv, nil +} + +type echo struct { + echov1.UnimplementedEchoServiceServer + grpc_health_v1.UnimplementedHealthServer + opts *Options +} + +// ServeHTTP implements http.Handler, allowing echo to support HTTP clients in addition to gRPC. +func (srv *echo) ServeHTTP(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case echoPath, echoPathAlias: + srv.httpHandler(w, r) + case healthPath, healthPathAlias: + srv.checkHandler(w, r) + default: + http.Error(w, fmt.Sprintf("%s not found", r.URL.Path), http.StatusNotFound) + } +} + +// Check checks whether echo service's underlying decrementer is alive. +func (srv *echo) Check(ctx context.Context, _ *grpc_health_v1.HealthCheckRequest) (*grpc_health_v1.HealthCheckResponse, error) { + if err := srv.opts.Decrementer.Alive(ctx); err != nil { + return nil, err + } + return &grpc_health_v1.HealthCheckResponse{ + Status: grpc_health_v1.HealthCheckResponse_SERVING, + }, nil +} + +func (srv *echo) checkHandler(w http.ResponseWriter, r *http.Request) { + resp, err := srv.Check(r.Context(), nil) + if err != nil { + + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if err := json.NewEncoder(w).Encode(resp); err != nil { + srv.opts.Logger.LogAttrs(context.Background(), slog.LevelError, err.Error(), srv.opts.LoggingAttrs...) + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} + +// Watch the health of the echov1.EchoServiceServer. +func (srv *echo) Watch(request *grpc_health_v1.HealthCheckRequest, server grpc_health_v1.Health_WatchServer) error { + resp, err := srv.Check(server.Context(), request) + if err != nil { + srv.opts.Logger.LogAttrs(context.Background(), slog.LevelError, err.Error(), srv.opts.LoggingAttrs...) + return err + } + return server.Send(resp) +} + +// Echo a EchoRequest with a EchoResponse. Decrements an underlying quota identified by the id of the request. +// Returns a cache.IsNotExist if request's id does not map to a key in the cache. +// See cache.Refresher for how the cache refreshes the quota identified by the request id. +func (srv *echo) Echo(ctx context.Context, request *echov1.EchoRequest) (*echov1.EchoResponse, error) { + v, err := srv.opts.Decrementer.Decrement(ctx, request.Id) + if cache.IsNotExist(err) { + return nil, status.Errorf(codes.NotFound, "error: source not found: %s, err %v", request.Id, err) + } + if err != nil { + srv.opts.Logger.LogAttrs(context.Background(), slog.LevelError, err.Error(), srv.opts.LoggingAttrs...) + return nil, status.Errorf(codes.Internal, "error: encountered from cache for resource: %srv, err %v", request.Id, err) + } + + if err := srv.writeMetric(ctx, request.Id, v); err != nil { + return nil, err + } + + if v < 0 { + return nil, status.Errorf(codes.ResourceExhausted, "error: resource exhausted for: %srv", request.Id) + } + + return &echov1.EchoResponse{ + Id: request.Id, + Payload: request.Payload, + }, nil +} + +func (srv *echo) writeMetric(ctx context.Context, id string, value int64) error { + if srv.opts.MetricsWriter == nil { + return nil + } + if err := srv.opts.MetricsWriter.Write(ctx, path.Join(metricsNamePrefix, id), "unit", &metric.Point{ + Timestamp: time.Now(), + Value: value + 1, + }); err != nil { + srv.opts.Logger.LogAttrs(context.Background(), slog.LevelError, err.Error(), srv.opts.LoggingAttrs...) + } + return nil +} + +func (srv *echo) httpHandler(w http.ResponseWriter, r *http.Request) { + var body *echov1.EchoRequest + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + err = fmt.Errorf("error decoding request body, payload field of %T needs to be base64 encoded, error: %w", body, err) + srv.opts.Logger.LogAttrs(context.Background(), slog.LevelError, err.Error(), srv.opts.LoggingAttrs...) + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + resp, err := srv.Echo(r.Context(), body) + if status.Code(err) == http.StatusNotFound { + http.Error(w, err.Error(), http.StatusNotFound) + return + } + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if err := json.NewEncoder(w).Encode(resp); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} diff --git a/.test-infra/mock-apis/src/main/java/org/apache/beam/testinfra/mockapis/echo/v1/Echo.java b/.test-infra/mock-apis/src/main/java/org/apache/beam/testinfra/mockapis/echo/v1/Echo.java new file mode 100644 index 000000000000..4652ff716b87 --- /dev/null +++ b/.test-infra/mock-apis/src/main/java/org/apache/beam/testinfra/mockapis/echo/v1/Echo.java @@ -0,0 +1,1447 @@ +/* + * 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.testinfra.mockapis.echo.v1; + +@SuppressWarnings({ + "argument", + "assignment", + "initialization.fields.uninitialized", + "initialization.static.field.uninitialized", + "override.param", + "ClassTypeParameterName", + "ForbidNonVendoredGuava", + "JavadocStyle", + "LocalVariableName", + "MemberName", + "NeedBraces", + "MissingOverride", + "RedundantModifier", + "ReferenceEquality", + "UnusedVariable", +}) +public final class Echo { + private Echo() {} + + public static void registerAllExtensions(com.google.protobuf.ExtensionRegistryLite registry) {} + + public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions((com.google.protobuf.ExtensionRegistryLite) registry); + } + + public interface EchoRequestOrBuilder + extends + // @@protoc_insertion_point(interface_extends:proto.echo.v1.EchoRequest) + com.google.protobuf.MessageOrBuilder { + + /** + * string id = 1 [json_name = "id"]; + * + * @return The id. + */ + java.lang.String getId(); + /** + * string id = 1 [json_name = "id"]; + * + * @return The bytes for id. + */ + com.google.protobuf.ByteString getIdBytes(); + + /** + * bytes payload = 2 [json_name = "payload"]; + * + * @return The payload. + */ + com.google.protobuf.ByteString getPayload(); + } + /** + * + * + *
+   * The request to echo a payload.
+   * 
+ * + * Protobuf type {@code proto.echo.v1.EchoRequest} + */ + public static final class EchoRequest extends com.google.protobuf.GeneratedMessageV3 + implements + // @@protoc_insertion_point(message_implements:proto.echo.v1.EchoRequest) + EchoRequestOrBuilder { + private static final long serialVersionUID = 0L; + // Use EchoRequest.newBuilder() to construct. + private EchoRequest(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + + private EchoRequest() { + id_ = ""; + payload_ = com.google.protobuf.ByteString.EMPTY; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance(UnusedPrivateParameter unused) { + return new EchoRequest(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet getUnknownFields() { + return this.unknownFields; + } + + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return org.apache.beam.testinfra.mockapis.echo.v1.Echo + .internal_static_proto_echo_v1_EchoRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.apache.beam.testinfra.mockapis.echo.v1.Echo + .internal_static_proto_echo_v1_EchoRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest.class, + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest.Builder.class); + } + + public static final int ID_FIELD_NUMBER = 1; + + @SuppressWarnings("serial") + private volatile java.lang.Object id_ = ""; + /** + * string id = 1 [json_name = "id"]; + * + * @return The id. + */ + @java.lang.Override + public java.lang.String getId() { + java.lang.Object ref = id_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + id_ = s; + return s; + } + } + /** + * string id = 1 [json_name = "id"]; + * + * @return The bytes for id. + */ + @java.lang.Override + public com.google.protobuf.ByteString getIdBytes() { + java.lang.Object ref = id_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); + id_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int PAYLOAD_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString payload_ = com.google.protobuf.ByteString.EMPTY; + /** + * bytes payload = 2 [json_name = "payload"]; + * + * @return The payload. + */ + @java.lang.Override + public com.google.protobuf.ByteString getPayload() { + return payload_; + } + + private byte memoizedIsInitialized = -1; + + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(id_)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, id_); + } + if (!payload_.isEmpty()) { + output.writeBytes(2, payload_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(id_)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, id_); + } + if (!payload_.isEmpty()) { + size += com.google.protobuf.CodedOutputStream.computeBytesSize(2, payload_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest)) { + return super.equals(obj); + } + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest other = + (org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest) obj; + + if (!getId().equals(other.getId())) return false; + if (!getPayload().equals(other.getPayload())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + ID_FIELD_NUMBER; + hash = (53 * hash) + getId().hashCode(); + hash = (37 * hash) + PAYLOAD_FIELD_NUMBER; + hash = (53 * hash) + getPayload().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest parseFrom( + java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest parseFrom( + java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest parseFrom( + byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest parseFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest parseFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException( + PARSER, input, extensionRegistry); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest parseDelimitedFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest parseDelimitedFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException( + PARSER, input, extensionRegistry); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest parseFrom( + com.google.protobuf.CodedInputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException( + PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { + return newBuilder(); + } + + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + + public static Builder newBuilder( + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * + * + *
+     * The request to echo a payload.
+     * 
+ * + * Protobuf type {@code proto.echo.v1.EchoRequest} + */ + public static final class Builder + extends com.google.protobuf.GeneratedMessageV3.Builder + implements + // @@protoc_insertion_point(builder_implements:proto.echo.v1.EchoRequest) + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequestOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return org.apache.beam.testinfra.mockapis.echo.v1.Echo + .internal_static_proto_echo_v1_EchoRequest_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.apache.beam.testinfra.mockapis.echo.v1.Echo + .internal_static_proto_echo_v1_EchoRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest.class, + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest.Builder.class); + } + + // Construct using org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest.newBuilder() + private Builder() {} + + private Builder(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + } + + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + id_ = ""; + payload_ = com.google.protobuf.ByteString.EMPTY; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { + return org.apache.beam.testinfra.mockapis.echo.v1.Echo + .internal_static_proto_echo_v1_EchoRequest_descriptor; + } + + @java.lang.Override + public org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest + getDefaultInstanceForType() { + return org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest.getDefaultInstance(); + } + + @java.lang.Override + public org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest build() { + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest buildPartial() { + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest result = + new org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest(this); + if (bitField0_ != 0) { + buildPartial0(result); + } + onBuilt(); + return result; + } + + private void buildPartial0( + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.id_ = id_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.payload_ = payload_; + } + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { + return super.setField(field, value); + } + + @java.lang.Override + public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + + @java.lang.Override + public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, + java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { + return super.addRepeatedField(field, value); + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest) { + return mergeFrom((org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest) other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest other) { + if (other + == org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest.getDefaultInstance()) + return this; + if (!other.getId().isEmpty()) { + id_ = other.id_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (other.getPayload() != com.google.protobuf.ByteString.EMPTY) { + setPayload(other.getPayload()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: + { + id_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: + { + payload_ = input.readBytes(); + bitField0_ |= 0x00000002; + break; + } // case 18 + default: + { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + + private int bitField0_; + + private java.lang.Object id_ = ""; + /** + * string id = 1 [json_name = "id"]; + * + * @return The id. + */ + public java.lang.String getId() { + java.lang.Object ref = id_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + id_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string id = 1 [json_name = "id"]; + * + * @return The bytes for id. + */ + public com.google.protobuf.ByteString getIdBytes() { + java.lang.Object ref = id_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); + id_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string id = 1 [json_name = "id"]; + * + * @param value The id to set. + * @return This builder for chaining. + */ + public Builder setId(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + id_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + * string id = 1 [json_name = "id"]; + * + * @return This builder for chaining. + */ + public Builder clearId() { + id_ = getDefaultInstance().getId(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + * string id = 1 [json_name = "id"]; + * + * @param value The bytes for id to set. + * @return This builder for chaining. + */ + public Builder setIdBytes(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + id_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private com.google.protobuf.ByteString payload_ = com.google.protobuf.ByteString.EMPTY; + /** + * bytes payload = 2 [json_name = "payload"]; + * + * @return The payload. + */ + @java.lang.Override + public com.google.protobuf.ByteString getPayload() { + return payload_; + } + /** + * bytes payload = 2 [json_name = "payload"]; + * + * @param value The payload to set. + * @return This builder for chaining. + */ + public Builder setPayload(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + payload_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + * bytes payload = 2 [json_name = "payload"]; + * + * @return This builder for chaining. + */ + public Builder clearPayload() { + bitField0_ = (bitField0_ & ~0x00000002); + payload_ = getDefaultInstance().getPayload(); + onChanged(); + return this; + } + + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + // @@protoc_insertion_point(builder_scope:proto.echo.v1.EchoRequest) + } + + // @@protoc_insertion_point(class_scope:proto.echo.v1.EchoRequest) + private static final org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest + DEFAULT_INSTANCE; + + static { + DEFAULT_INSTANCE = new org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest(); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + @java.lang.Override + public EchoRequest parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException() + .setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + } + + public interface EchoResponseOrBuilder + extends + // @@protoc_insertion_point(interface_extends:proto.echo.v1.EchoResponse) + com.google.protobuf.MessageOrBuilder { + + /** + * string id = 1 [json_name = "id"]; + * + * @return The id. + */ + java.lang.String getId(); + /** + * string id = 1 [json_name = "id"]; + * + * @return The bytes for id. + */ + com.google.protobuf.ByteString getIdBytes(); + + /** + * bytes payload = 2 [json_name = "payload"]; + * + * @return The payload. + */ + com.google.protobuf.ByteString getPayload(); + } + /** + * + * + *
+   * The response echo of a request payload.
+   * 
+ * + * Protobuf type {@code proto.echo.v1.EchoResponse} + */ + public static final class EchoResponse extends com.google.protobuf.GeneratedMessageV3 + implements + // @@protoc_insertion_point(message_implements:proto.echo.v1.EchoResponse) + EchoResponseOrBuilder { + private static final long serialVersionUID = 0L; + // Use EchoResponse.newBuilder() to construct. + private EchoResponse(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + + private EchoResponse() { + id_ = ""; + payload_ = com.google.protobuf.ByteString.EMPTY; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance(UnusedPrivateParameter unused) { + return new EchoResponse(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet getUnknownFields() { + return this.unknownFields; + } + + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return org.apache.beam.testinfra.mockapis.echo.v1.Echo + .internal_static_proto_echo_v1_EchoResponse_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.apache.beam.testinfra.mockapis.echo.v1.Echo + .internal_static_proto_echo_v1_EchoResponse_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse.class, + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse.Builder.class); + } + + public static final int ID_FIELD_NUMBER = 1; + + @SuppressWarnings("serial") + private volatile java.lang.Object id_ = ""; + /** + * string id = 1 [json_name = "id"]; + * + * @return The id. + */ + @java.lang.Override + public java.lang.String getId() { + java.lang.Object ref = id_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + id_ = s; + return s; + } + } + /** + * string id = 1 [json_name = "id"]; + * + * @return The bytes for id. + */ + @java.lang.Override + public com.google.protobuf.ByteString getIdBytes() { + java.lang.Object ref = id_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); + id_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int PAYLOAD_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString payload_ = com.google.protobuf.ByteString.EMPTY; + /** + * bytes payload = 2 [json_name = "payload"]; + * + * @return The payload. + */ + @java.lang.Override + public com.google.protobuf.ByteString getPayload() { + return payload_; + } + + private byte memoizedIsInitialized = -1; + + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException { + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(id_)) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, id_); + } + if (!payload_.isEmpty()) { + output.writeBytes(2, payload_); + } + getUnknownFields().writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!com.google.protobuf.GeneratedMessageV3.isStringEmpty(id_)) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, id_); + } + if (!payload_.isEmpty()) { + size += com.google.protobuf.CodedOutputStream.computeBytesSize(2, payload_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse)) { + return super.equals(obj); + } + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse other = + (org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse) obj; + + if (!getId().equals(other.getId())) return false; + if (!getPayload().equals(other.getPayload())) return false; + if (!getUnknownFields().equals(other.getUnknownFields())) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + ID_FIELD_NUMBER; + hash = (53 * hash) + getId().hashCode(); + hash = (37 * hash) + PAYLOAD_FIELD_NUMBER; + hash = (53 * hash) + getPayload().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse parseFrom( + java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse parseFrom( + java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse parseFrom( + byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse parseFrom( + byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse parseFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse parseFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException( + PARSER, input, extensionRegistry); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse parseDelimitedFrom( + java.io.InputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse parseDelimitedFrom( + java.io.InputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException( + PARSER, input, extensionRegistry); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse parseFrom( + com.google.protobuf.CodedInputStream input) throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException( + PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { + return newBuilder(); + } + + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + + public static Builder newBuilder( + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * + * + *
+     * The response echo of a request payload.
+     * 
+ * + * Protobuf type {@code proto.echo.v1.EchoResponse} + */ + public static final class Builder + extends com.google.protobuf.GeneratedMessageV3.Builder + implements + // @@protoc_insertion_point(builder_implements:proto.echo.v1.EchoResponse) + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponseOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return org.apache.beam.testinfra.mockapis.echo.v1.Echo + .internal_static_proto_echo_v1_EchoResponse_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.apache.beam.testinfra.mockapis.echo.v1.Echo + .internal_static_proto_echo_v1_EchoResponse_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse.class, + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse.Builder.class); + } + + // Construct using org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse.newBuilder() + private Builder() {} + + private Builder(com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + } + + @java.lang.Override + public Builder clear() { + super.clear(); + bitField0_ = 0; + id_ = ""; + payload_ = com.google.protobuf.ByteString.EMPTY; + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { + return org.apache.beam.testinfra.mockapis.echo.v1.Echo + .internal_static_proto_echo_v1_EchoResponse_descriptor; + } + + @java.lang.Override + public org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse + getDefaultInstanceForType() { + return org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse.getDefaultInstance(); + } + + @java.lang.Override + public org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse build() { + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse buildPartial() { + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse result = + new org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse(this); + if (bitField0_ != 0) { + buildPartial0(result); + } + onBuilt(); + return result; + } + + private void buildPartial0( + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse result) { + int from_bitField0_ = bitField0_; + if (((from_bitField0_ & 0x00000001) != 0)) { + result.id_ = id_; + } + if (((from_bitField0_ & 0x00000002) != 0)) { + result.payload_ = payload_; + } + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { + return super.setField(field, value); + } + + @java.lang.Override + public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + + @java.lang.Override + public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, + java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, java.lang.Object value) { + return super.addRepeatedField(field, value); + } + + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse) { + return mergeFrom((org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse) other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse other) { + if (other + == org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse.getDefaultInstance()) + return this; + if (!other.getId().isEmpty()) { + id_ = other.id_; + bitField0_ |= 0x00000001; + onChanged(); + } + if (other.getPayload() != com.google.protobuf.ByteString.EMPTY) { + setPayload(other.getPayload()); + } + this.mergeUnknownFields(other.getUnknownFields()); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: + { + id_ = input.readStringRequireUtf8(); + bitField0_ |= 0x00000001; + break; + } // case 10 + case 18: + { + payload_ = input.readBytes(); + bitField0_ |= 0x00000002; + break; + } // case 18 + default: + { + if (!super.parseUnknownField(input, extensionRegistry, tag)) { + done = true; // was an endgroup tag + } + break; + } // default: + } // switch (tag) + } // while (!done) + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.unwrapIOException(); + } finally { + onChanged(); + } // finally + return this; + } + + private int bitField0_; + + private java.lang.Object id_ = ""; + /** + * string id = 1 [json_name = "id"]; + * + * @return The id. + */ + public java.lang.String getId() { + java.lang.Object ref = id_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + id_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string id = 1 [json_name = "id"]; + * + * @return The bytes for id. + */ + public com.google.protobuf.ByteString getIdBytes() { + java.lang.Object ref = id_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8((java.lang.String) ref); + id_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string id = 1 [json_name = "id"]; + * + * @param value The id to set. + * @return This builder for chaining. + */ + public Builder setId(java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + id_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + /** + * string id = 1 [json_name = "id"]; + * + * @return This builder for chaining. + */ + public Builder clearId() { + id_ = getDefaultInstance().getId(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + /** + * string id = 1 [json_name = "id"]; + * + * @param value The bytes for id to set. + * @return This builder for chaining. + */ + public Builder setIdBytes(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + id_ = value; + bitField0_ |= 0x00000001; + onChanged(); + return this; + } + + private com.google.protobuf.ByteString payload_ = com.google.protobuf.ByteString.EMPTY; + /** + * bytes payload = 2 [json_name = "payload"]; + * + * @return The payload. + */ + @java.lang.Override + public com.google.protobuf.ByteString getPayload() { + return payload_; + } + /** + * bytes payload = 2 [json_name = "payload"]; + * + * @param value The payload to set. + * @return This builder for chaining. + */ + public Builder setPayload(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + payload_ = value; + bitField0_ |= 0x00000002; + onChanged(); + return this; + } + /** + * bytes payload = 2 [json_name = "payload"]; + * + * @return This builder for chaining. + */ + public Builder clearPayload() { + bitField0_ = (bitField0_ & ~0x00000002); + payload_ = getDefaultInstance().getPayload(); + onChanged(); + return this; + } + + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + // @@protoc_insertion_point(builder_scope:proto.echo.v1.EchoResponse) + } + + // @@protoc_insertion_point(class_scope:proto.echo.v1.EchoResponse) + private static final org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse + DEFAULT_INSTANCE; + + static { + DEFAULT_INSTANCE = new org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse(); + } + + public static org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse + getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + @java.lang.Override + public EchoResponse parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + Builder builder = newBuilder(); + try { + builder.mergeFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(builder.buildPartial()); + } catch (com.google.protobuf.UninitializedMessageException e) { + throw e.asInvalidProtocolBufferException() + .setUnfinishedMessage(builder.buildPartial()); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(builder.buildPartial()); + } + return builder.buildPartial(); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse + getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + } + + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_proto_echo_v1_EchoRequest_descriptor; + private static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_proto_echo_v1_EchoRequest_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_proto_echo_v1_EchoResponse_descriptor; + private static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_proto_echo_v1_EchoResponse_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + return descriptor; + } + + private static com.google.protobuf.Descriptors.FileDescriptor descriptor; + + static { + java.lang.String[] descriptorData = { + "\n\030proto/echo/v1/echo.proto\022\rproto.echo.v" + + "1\"7\n\013EchoRequest\022\016\n\002id\030\001 \001(\tR\002id\022\030\n\007payl" + + "oad\030\002 \001(\014R\007payload\"8\n\014EchoResponse\022\016\n\002id" + + "\030\001 \001(\tR\002id\022\030\n\007payload\030\002 \001(\014R\007payload2P\n\013" + + "EchoService\022A\n\004Echo\022\032.proto.echo.v1.Echo" + + "Request\032\033.proto.echo.v1.EchoResponse\"\000B;" + + "\n*org.apache.beam.testinfra.mockapis.ech" + + "o.v1Z\rproto/echo/v1b\006proto3" + }; + descriptor = + com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom( + descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[] {}); + internal_static_proto_echo_v1_EchoRequest_descriptor = getDescriptor().getMessageTypes().get(0); + internal_static_proto_echo_v1_EchoRequest_fieldAccessorTable = + new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_proto_echo_v1_EchoRequest_descriptor, + new java.lang.String[] { + "Id", "Payload", + }); + internal_static_proto_echo_v1_EchoResponse_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_proto_echo_v1_EchoResponse_fieldAccessorTable = + new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_proto_echo_v1_EchoResponse_descriptor, + new java.lang.String[] { + "Id", "Payload", + }); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/.test-infra/mock-apis/src/main/java/org/apache/beam/testinfra/mockapis/echo/v1/EchoServiceGrpc.java b/.test-infra/mock-apis/src/main/java/org/apache/beam/testinfra/mockapis/echo/v1/EchoServiceGrpc.java new file mode 100644 index 000000000000..14437899b69c --- /dev/null +++ b/.test-infra/mock-apis/src/main/java/org/apache/beam/testinfra/mockapis/echo/v1/EchoServiceGrpc.java @@ -0,0 +1,393 @@ +/* + * 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.testinfra.mockapis.echo.v1; + +import static io.grpc.MethodDescriptor.generateFullMethodName; + +/** + * + * + *
+ * EchoService simulates a mock API that echos a request.
+ * 
+ */ +@SuppressWarnings({ + "argument", + "assignment", + "initialization.fields.uninitialized", + "initialization.static.field.uninitialized", + "override.param", + "ClassTypeParameterName", + "ForbidNonVendoredGuava", + "JavadocStyle", + "LocalVariableName", + "MemberName", + "NeedBraces", + "MissingOverride", + "RedundantModifier", + "ReferenceEquality", + "UnusedVariable", +}) +@javax.annotation.Generated( + value = "by gRPC proto compiler (version 1.58.0)", + comments = "Source: proto/echo/v1/echo.proto") +@io.grpc.stub.annotations.GrpcGenerated +public final class EchoServiceGrpc { + + private EchoServiceGrpc() {} + + public static final java.lang.String SERVICE_NAME = "proto.echo.v1.EchoService"; + + // Static method descriptors that strictly reflect the proto. + private static volatile io.grpc.MethodDescriptor< + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest, + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse> + getEchoMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "Echo", + requestType = org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest.class, + responseType = org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor< + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest, + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse> + getEchoMethod() { + io.grpc.MethodDescriptor< + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest, + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse> + getEchoMethod; + if ((getEchoMethod = EchoServiceGrpc.getEchoMethod) == null) { + synchronized (EchoServiceGrpc.class) { + if ((getEchoMethod = EchoServiceGrpc.getEchoMethod) == null) { + EchoServiceGrpc.getEchoMethod = + getEchoMethod = + io.grpc.MethodDescriptor + . + newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "Echo")) + .setSampledToLocalTracing(true) + .setRequestMarshaller( + io.grpc.protobuf.ProtoUtils.marshaller( + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest + .getDefaultInstance())) + .setResponseMarshaller( + io.grpc.protobuf.ProtoUtils.marshaller( + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse + .getDefaultInstance())) + .setSchemaDescriptor(new EchoServiceMethodDescriptorSupplier("Echo")) + .build(); + } + } + } + return getEchoMethod; + } + + /** Creates a new async stub that supports all call types for the service */ + public static EchoServiceStub newStub(io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public EchoServiceStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new EchoServiceStub(channel, callOptions); + } + }; + return EchoServiceStub.newStub(factory, channel); + } + + /** + * Creates a new blocking-style stub that supports unary and streaming output calls on the service + */ + public static EchoServiceBlockingStub newBlockingStub(io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public EchoServiceBlockingStub newStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new EchoServiceBlockingStub(channel, callOptions); + } + }; + return EchoServiceBlockingStub.newStub(factory, channel); + } + + /** Creates a new ListenableFuture-style stub that supports unary calls on the service */ + public static EchoServiceFutureStub newFutureStub(io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public EchoServiceFutureStub newStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new EchoServiceFutureStub(channel, callOptions); + } + }; + return EchoServiceFutureStub.newStub(factory, channel); + } + + /** + * + * + *
+   * EchoService simulates a mock API that echos a request.
+   * 
+ */ + public interface AsyncService { + + /** + * + * + *
+     * Echo an EchoRequest payload in an EchoResponse.
+     * 
+ */ + default void echo( + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest request, + io.grpc.stub.StreamObserver + responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getEchoMethod(), responseObserver); + } + } + + /** + * Base class for the server implementation of the service EchoService. + * + *
+   * EchoService simulates a mock API that echos a request.
+   * 
+ */ + public abstract static class EchoServiceImplBase + implements io.grpc.BindableService, AsyncService { + + @java.lang.Override + public final io.grpc.ServerServiceDefinition bindService() { + return EchoServiceGrpc.bindService(this); + } + } + + /** + * A stub to allow clients to do asynchronous rpc calls to service EchoService. + * + *
+   * EchoService simulates a mock API that echos a request.
+   * 
+ */ + public static final class EchoServiceStub + extends io.grpc.stub.AbstractAsyncStub { + private EchoServiceStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected EchoServiceStub build(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new EchoServiceStub(channel, callOptions); + } + + /** + * + * + *
+     * Echo an EchoRequest payload in an EchoResponse.
+     * 
+ */ + public void echo( + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest request, + io.grpc.stub.StreamObserver + responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getEchoMethod(), getCallOptions()), request, responseObserver); + } + } + + /** + * A stub to allow clients to do synchronous rpc calls to service EchoService. + * + *
+   * EchoService simulates a mock API that echos a request.
+   * 
+ */ + public static final class EchoServiceBlockingStub + extends io.grpc.stub.AbstractBlockingStub { + private EchoServiceBlockingStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected EchoServiceBlockingStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new EchoServiceBlockingStub(channel, callOptions); + } + + /** + * + * + *
+     * Echo an EchoRequest payload in an EchoResponse.
+     * 
+ */ + public org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse echo( + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getEchoMethod(), getCallOptions(), request); + } + } + + /** + * A stub to allow clients to do ListenableFuture-style rpc calls to service EchoService. + * + *
+   * EchoService simulates a mock API that echos a request.
+   * 
+ */ + public static final class EchoServiceFutureStub + extends io.grpc.stub.AbstractFutureStub { + private EchoServiceFutureStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected EchoServiceFutureStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new EchoServiceFutureStub(channel, callOptions); + } + + /** + * + * + *
+     * Echo an EchoRequest payload in an EchoResponse.
+     * 
+ */ + public com.google.common.util.concurrent.ListenableFuture< + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse> + echo(org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getEchoMethod(), getCallOptions()), request); + } + } + + private static final int METHODID_ECHO = 0; + + private static final class MethodHandlers + implements io.grpc.stub.ServerCalls.UnaryMethod, + io.grpc.stub.ServerCalls.ServerStreamingMethod, + io.grpc.stub.ServerCalls.ClientStreamingMethod, + io.grpc.stub.ServerCalls.BidiStreamingMethod { + private final AsyncService serviceImpl; + private final int methodId; + + MethodHandlers(AsyncService serviceImpl, int methodId) { + this.serviceImpl = serviceImpl; + this.methodId = methodId; + } + + @java.lang.Override + @java.lang.SuppressWarnings("unchecked") + public void invoke(Req request, io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + case METHODID_ECHO: + serviceImpl.echo( + (org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest) request, + (io.grpc.stub.StreamObserver< + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse>) + responseObserver); + break; + default: + throw new AssertionError(); + } + } + + @java.lang.Override + @java.lang.SuppressWarnings("unchecked") + public io.grpc.stub.StreamObserver invoke( + io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + default: + throw new AssertionError(); + } + } + } + + public static final io.grpc.ServerServiceDefinition bindService(AsyncService service) { + return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor()) + .addMethod( + getEchoMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers< + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoRequest, + org.apache.beam.testinfra.mockapis.echo.v1.Echo.EchoResponse>( + service, METHODID_ECHO))) + .build(); + } + + private abstract static class EchoServiceBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoFileDescriptorSupplier, + io.grpc.protobuf.ProtoServiceDescriptorSupplier { + EchoServiceBaseDescriptorSupplier() {} + + @java.lang.Override + public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() { + return org.apache.beam.testinfra.mockapis.echo.v1.Echo.getDescriptor(); + } + + @java.lang.Override + public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() { + return getFileDescriptor().findServiceByName("EchoService"); + } + } + + private static final class EchoServiceFileDescriptorSupplier + extends EchoServiceBaseDescriptorSupplier { + EchoServiceFileDescriptorSupplier() {} + } + + private static final class EchoServiceMethodDescriptorSupplier + extends EchoServiceBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoMethodDescriptorSupplier { + private final java.lang.String methodName; + + EchoServiceMethodDescriptorSupplier(java.lang.String methodName) { + this.methodName = methodName; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() { + return getServiceDescriptor().findMethodByName(methodName); + } + } + + private static volatile io.grpc.ServiceDescriptor serviceDescriptor; + + public static io.grpc.ServiceDescriptor getServiceDescriptor() { + io.grpc.ServiceDescriptor result = serviceDescriptor; + if (result == null) { + synchronized (EchoServiceGrpc.class) { + result = serviceDescriptor; + if (result == null) { + serviceDescriptor = + result = + io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME) + .setSchemaDescriptor(new EchoServiceFileDescriptorSupplier()) + .addMethod(getEchoMethod()) + .build(); + } + } + } + return result; + } +} diff --git a/.test-infra/mock-apis/src/main/java/org/apache/beam/testinfra/mockapis/echo/v1/package-info.java b/.test-infra/mock-apis/src/main/java/org/apache/beam/testinfra/mockapis/echo/v1/package-info.java new file mode 100644 index 000000000000..00b7fa2f70b7 --- /dev/null +++ b/.test-infra/mock-apis/src/main/java/org/apache/beam/testinfra/mockapis/echo/v1/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** Autogenerated code supporting a quota aware gRPC endpoint client. */ +package org.apache.beam.testinfra.mockapis.echo.v1; diff --git a/build.gradle.kts b/build.gradle.kts index a97b73757249..b330bd07861e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -54,6 +54,7 @@ tasks.rat { // Proto/grpc generated wrappers "**/apache_beam/portability/api/**/*_pb2*.py", "**/go/pkg/beam/**/*.pb.go", + "**/mock-apis/**/*.pb.go", // Ignore go.sum files, which don't permit headers "**/go.sum", @@ -198,6 +199,9 @@ tasks.rat { // Ignore typesciript package management. "sdks/typescript/package-lock.json", "sdks/typescript/node_modules/**/*", + + // Ignore buf autogenerated files. + "**/buf.lock", ) // Add .gitignore excludes to the Apache Rat exclusion list. We re-create the behavior diff --git a/sdks/java/build-tools/src/main/resources/beam/checkstyle/suppressions.xml b/sdks/java/build-tools/src/main/resources/beam/checkstyle/suppressions.xml index 7037f0543f4f..3f052508f2dd 100644 --- a/sdks/java/build-tools/src/main/resources/beam/checkstyle/suppressions.xml +++ b/sdks/java/build-tools/src/main/resources/beam/checkstyle/suppressions.xml @@ -87,6 +87,7 @@ + diff --git a/settings.gradle.kts b/settings.gradle.kts index e8e374eb6e19..de83c11715c8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -326,6 +326,8 @@ include(":runners:google-cloud-dataflow-java:worker:windmill") // no dots allowed for project paths include("beam-test-infra-metrics") project(":beam-test-infra-metrics").projectDir = file(".test-infra/metrics") +include("beam-test-infra-mock-apis") +project(":beam-test-infra-mock-apis").projectDir = file(".test-infra/mock-apis") include("beam-test-infra-pipelines") project(":beam-test-infra-pipelines").projectDir = file(".test-infra/pipelines") include("beam-test-tools")