Skip to content

Commit ef29393

Browse files
diegomarquezpJoeWang1127
authored andcommitted
chore: add docker image for hermetic build scripts (#2493)
Similar changes to those of #2298 by @mpeddada1 This setup uses two triggers: - The first one is [library-generation-presubmit-sdk-platform-java](https://pantheon.corp.google.com/cloud-build/triggers;region=global/edit/5783744f-0820-419f-bc5e-abbbece4be0b?e=13803378&mods=monitoring_api_prod&project=cloud-devrel-kokoro-resources), which runs on each pull request. It builds a docker image with the contents of `library_generation` at HEAD and runs `library_generation/integration_tests.py` with such image - The second one is [library-generation-sdk-platform-java](https://pantheon.corp.google.com/cloud-build/triggers;region=global/edit/e3373892-82a2-4eac-a1f2-95523966df70?e=13803378&mods=monitoring_api_prod&project=cloud-devrel-kokoro-resources), which: - is triggered upon a commit pushed to the `main` branch, then - builds a docker image with two tags - `latest`, which will be constantly updated to match the latest build - a tag based on the branch `${COMMIT_SHA}` - then the image is pushed with both tags ### tasks - [x] create dockerfile - [x] create cloudbuild.yaml for testing - [x] create cloudbuild test infra - [x] create cloudbuild.yaml for releasing the image - [x] create cloudbuild release infra --------- Co-authored-by: Joe Wang <[email protected]>
1 parent 12084f0 commit ef29393

9 files changed

+213
-13
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Copyright 2024 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
timeout: 7200s # 2 hours
16+
substitutions:
17+
_IMAGE_NAME: "gcr.io/cloud-devrel-public-resources/java-library-generation"
18+
_SHA_IMAGE_ID: "${_IMAGE_NAME}:${COMMIT_SHA}"
19+
_LATEST_IMAGE_ID: "${_IMAGE_NAME}:latest"
20+
steps:
21+
# Library generation build
22+
- name: gcr.io/cloud-builders/docker
23+
args: [
24+
"build",
25+
"-t", "${_SHA_IMAGE_ID}",
26+
"-t", "${_LATEST_IMAGE_ID}",
27+
"--file", ".cloudbuild/library_generation.Dockerfile", "."]
28+
id: library-generation-build
29+
waitFor: ["-"]
30+
31+
images:
32+
- ${_SHA_IMAGE_ID}
33+
- ${_LATEST_IMAGE_ID}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Copyright 2024 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
timeout: 7200s # 2 hours
16+
substitutions:
17+
_TEST_IMAGE_ID: 'gcr.io/cloud-devrel-public-resources/java-library-generation:${COMMIT_SHA}'
18+
19+
steps:
20+
# Library generation build
21+
- name: gcr.io/cloud-builders/docker
22+
args: ["build", "-t", "${_TEST_IMAGE_ID}", "--file", ".cloudbuild/library_generation.Dockerfile", "."]
23+
id: library-generation-build
24+
waitFor: ["-"]
25+
- name: ${_TEST_IMAGE_ID}
26+
entrypoint: bash
27+
args: [ './library_generation/test/container_integration_tests.sh' ]
28+
waitFor: [ "library-generation-build" ]
29+
env:
30+
- 'TEST_IMAGE_ID=${_TEST_IMAGE_ID}'
31+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Copyright 2024 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# build from the root of this repo:
16+
FROM gcr.io/cloud-devrel-public-resources/python
17+
18+
# install tools
19+
RUN apt-get update && apt-get install -y \
20+
unzip openjdk-17-jdk rsync maven \
21+
&& apt-get clean
22+
23+
COPY library_generation /src
24+
25+
RUN rm $(which python3)
26+
RUN ln -s $(which python3.11) /usr/local/bin/python
27+
RUN ln -s $(which python3.11) /usr/local/bin/python3
28+
RUN python -m pip install --upgrade pip
29+
RUN cd /src && python -m pip install -r requirements.in
30+
RUN cd /src && python -m pip install .
31+
32+
# set dummy git credentials for empty commit used in postprocessing
33+
RUN git config --global user.email "[email protected]"
34+
RUN git config --global user.name "Cloud Java Bot"
35+
36+
WORKDIR /workspace
37+
RUN chmod 750 /workspace
38+
RUN chmod 750 /src/generate_repo.py
39+
40+
CMD [ "/src/generate_repo.py" ]

library_generation/dockerignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
README.md
2+
**/__pycache__/
3+
**/*.egg-info/
4+
**/output/
5+
**/build/
6+
**/google-cloud-java/

library_generation/generate_repo.py

100644100755
File mode changed.

library_generation/postprocess_library.sh

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,22 +61,48 @@ fi
6161

6262
# we determine the location of the .OwlBot.yaml file by checking if the target
6363
# folder is a monorepo folder or not
64-
if [[ "${postprocessing_target}" == *google-cloud-java* ]]; then
64+
if [[ "${is_monorepo}" == "true" ]]; then
6565
owlbot_yaml_relative_path=".OwlBot.yaml"
6666
else
6767
owlbot_yaml_relative_path=".github/.OwlBot.yaml"
6868
fi
6969

70+
# Default values for running copy-code directly from host
71+
repo_binding="${postprocessing_target}"
72+
repo_workspace="/repo"
73+
preprocessed_libraries_binding="${owlbot_cli_source_folder}"
74+
75+
# When running docker inside docker, we run into the issue of volume bindings
76+
# being mapped from the host machine to the child container (instead of the
77+
# parent container to child container) because we bind the `docker.sock` socket
78+
# to the parent container (i.e. docker calls use the host's filesystem context)
79+
# see https://serverfault.com/a/819371
80+
# We solve this by referencing environment variables that will be
81+
# set to produce the correct volume mapping.
82+
#
83+
# The workflow is: to check if we are in a docker container (via passed env var)
84+
# and use managed volumes (docker volume create) instead of bindings
85+
# (-v /path:/other-path). The volume names are also received as env vars.
86+
87+
if [[ -n "${RUNNING_IN_DOCKER}" ]]; then
88+
set -u # temporarily fail on unset variables
89+
repo_binding="${REPO_BINDING_VOLUME}"
90+
set +u
91+
if [[ "${is_monorepo}" == "true" ]]; then
92+
repo_workspace="/repo/$(echo "${postprocessing_target}" | rev | cut -d'/' -f1 | rev)"
93+
fi
94+
fi
95+
7096
docker run --rm \
7197
--user "$(id -u)":"$(id -g)" \
72-
-v "${postprocessing_target}:/repo" \
73-
-v "${owlbot_cli_source_folder}:/pre-processed-libraries" \
74-
-w /repo \
98+
-v "${repo_binding}:/repo" \
99+
-v "/tmp:/tmp" \
100+
-w "${repo_workspace}" \
75101
--env HOME=/tmp \
76102
gcr.io/cloud-devrel-public-resources/owlbot-cli@"${owlbot_cli_image_sha}" \
77103
copy-code \
78104
--source-repo-commit-hash=none \
79-
--source-repo=/pre-processed-libraries \
105+
--source-repo="${preprocessed_libraries_binding}" \
80106
--config-file="${owlbot_yaml_relative_path}"
81107

82108
# we clone the synthtool library and manually build it
@@ -86,6 +112,7 @@ pushd /tmp/synthtool
86112
if [ ! -d "synthtool" ]; then
87113
git clone https://github.com/googleapis/synthtool.git
88114
fi
115+
git config --global --add safe.directory /tmp/synthtool/synthtool
89116
pushd "synthtool"
90117

91118
git reset --hard "${synthtool_commitish}"

library_generation/setup.py

100644100755
File mode changed.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/bin/bash
2+
# This is a wrapper of integration_tests.py that sets up the environment to run
3+
# the script in a docker container
4+
5+
set -xe
6+
if [[ -z "${TEST_IMAGE_ID}" ]]; then
7+
echo "required environemnt variable TEST_IMAGE_ID is not set"
8+
exit 1
9+
fi
10+
11+
if [[ ! -d google-cloud-java ]]; then
12+
git clone https://github.com/googleapis/google-cloud-java
13+
fi
14+
pushd google-cloud-java
15+
git reset --hard main
16+
popd
17+
18+
# We use a volume to hold the google-cloud-java repository used in the
19+
# integration tests. This is because the test container creates a child
20+
# container using the host machine's docker socket, meaning that we can only
21+
# reference volumes created from within the host machine (i.e. the machine
22+
# running this script)
23+
#
24+
# To summarize, we create a special volume that can be referenced both in the
25+
# main container and in any child containers created by this one.
26+
if [[ $(docker volume inspect repo) != '[]' ]]; then
27+
docker volume rm repo
28+
fi
29+
docker volume create --name "repo" --opt "type=none" --opt "device=$(pwd)/google-cloud-java" --opt "o=bind"
30+
31+
docker run --rm \
32+
-v repo:/workspace \
33+
-v /tmp:/tmp \
34+
-v /var/run/docker.sock:/var/run/docker.sock \
35+
-e "RUNNING_IN_DOCKER=true" \
36+
-e "REPO_BINDING_VOLUME=repo" \
37+
-w "/src" \
38+
"${TEST_IMAGE_ID}" \
39+
python -m unittest /src/test/integration_tests.py

library_generation/test/integration_tests.py

100644100755
Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@
2626
from library_generation.generate_repo import generate_from_yaml
2727
from library_generation.model.generation_config import from_yaml
2828
from library_generation.test.compare_poms import compare_xml
29-
from library_generation.utilities import get_library_name
30-
from library_generation.utilities import sh_util as shell_call
29+
from library_generation.utilities import (
30+
get_library_name,
31+
sh_util as shell_call,
32+
run_process_and_print_output,
33+
)
3134

3235
config_name = "generation_config.yaml"
3336
script_dir = os.path.dirname(os.path.realpath(__file__))
@@ -48,8 +51,9 @@ def test_generate_repo(self):
4851
config_files = self.__get_config_files(config_dir)
4952
i = 0
5053
for repo, config_file in config_files.items():
51-
repo_dest = f"{output_folder}/{repo}"
52-
self.__pull_repo_to(Path(repo_dest), repo, committish_list[i])
54+
repo_dest = self.__pull_repo_to(
55+
Path(f"{golden_dir}/{repo}"), repo, committish_list[i]
56+
)
5357
library_names = self.__get_library_names_from_config(config_file)
5458
# prepare golden files
5559
for library_name in library_names:
@@ -109,11 +113,31 @@ def test_generate_repo(self):
109113
i += 1
110114

111115
@classmethod
112-
def __pull_repo_to(cls, dest: Path, repo: str, committish: str):
113-
repo_url = f"{repo_prefix}/{repo}"
114-
print(f"Cloning repository {repo_url}")
115-
repo = Repo.clone_from(repo_url, dest)
116+
def __pull_repo_to(cls, default_dest: Path, repo: str, committish: str) -> str:
117+
if "RUNNING_IN_DOCKER" in os.environ:
118+
# the docker image expects the repo to be in /workspace
119+
dest_in_docker = "/workspace"
120+
run_process_and_print_output(
121+
[
122+
"git",
123+
"config",
124+
"--global",
125+
"--add",
126+
"safe.directory",
127+
dest_in_docker,
128+
],
129+
"Add /workspace to safe directories",
130+
)
131+
dest = Path(dest_in_docker)
132+
repo = Repo(dest)
133+
else:
134+
dest = default_dest
135+
repo_dest = f"{golden_dir}/{repo}"
136+
repo_url = f"{repo_prefix}/{repo}"
137+
print(f"Cloning repository {repo_url}")
138+
repo = Repo.clone_from(repo_url, dest)
116139
repo.git.checkout(committish)
140+
return str(dest)
117141

118142
@classmethod
119143
def __get_library_names_from_config(cls, config_path: str) -> List[str]:

0 commit comments

Comments
 (0)