Skip to content

Commit fd4450b

Browse files
[CLOUDP-338093] Use goreleaser to build kubectl-mongodb plugin for dev/staging workflows (#320)
# Summary Per current implementation when we release the binary we build it via goreleaser but in our dev and staging workflows we used `scripts/evergreen/build_multi_cluster_kubeconfig_creator.sh` (which eventually run `go build`) to build the binary. We needed to change this to make sure that we are using the same mechanism to build the binary in dev/staging workflows that we use while releasing. In order to achieve that, this PR adds functionality to build the `kubectl-mongodb` plugin using goreleaser in dev/staging workflows. As part of the new workflow, `build_multi_cluster_binary` evergreen function has been changed to run `goreleaser build` and upload the artifacts to a S3 location. After that the linux/amd64 distribution of the binary is downloaded locally at the path where tests image expects it. Having the binary in the S3 bucket would eventually help us to just promote the binaries while the release process but we are not able to achieve that for now, because for dev/staging workflows we are not able to notarize the generated binaries. After this PR, potentially we can even consider just getting rid of the script `scripts/evergreen/build_multi_cluster_kubeconfig_creator.sh`. ## Proof of Work Run the local patch using ``` evergreen patch --path .evergreen.yml -p mongodb-kubernetes -v init_test_run -t build_test_image -f -y -u --browse -d "Testing build of kubectl plugin using build scenario" ``` and make sure that test image is built successfully and the goreleaser artifacts are uploaded to the S3 bucket (`mongodb-kubernetes-dev`) correctly. CI passing on this PR also proves that the binary is successfully built and copied to the test image. ## Checklist - [x] Have you linked a jira ticket and/or is the ticket in the title? - [x] Have you checked whether your jira ticket required DOCSP changes? - [x] Have you added changelog file? - use `skip-changelog` label if not needed - refer to [Changelog files and Release Notes](https://github.com/mongodb/mongodb-kubernetes/blob/master/CONTRIBUTING.md#changelog-files-and-release-notes) section in CONTRIBUTING.md for more details
1 parent 06aea16 commit fd4450b

File tree

6 files changed

+172
-13
lines changed

6 files changed

+172
-13
lines changed

.evergreen-functions.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -507,14 +507,13 @@ functions:
507507
params:
508508
working_dir: src/github.com/mongodb/mongodb-kubernetes
509509
binary: scripts/release/publish_helm_chart.sh
510-
510+
511511
build_multi_cluster_binary:
512512
- command: subprocess.exec
513-
type: setup
514513
params:
515514
working_dir: src/github.com/mongodb/mongodb-kubernetes
516-
binary: scripts/evergreen/build_multi_cluster_kubeconfig_creator.sh
517-
515+
binary: scripts/dev/run_python.sh scripts/release/kubectl-mongodb/python/build_kubectl_plugin.py
516+
518517
build_and_push_appdb_database:
519518
- command: subprocess.exec
520519
params:
@@ -854,6 +853,7 @@ functions:
854853
curl -fL "${goreleaser_pro_tar_gz}" --output goreleaser_Linux_x86_64.tar.gz
855854
tar -xf goreleaser_Linux_x86_64.tar.gz
856855
chmod 755 ./goreleaser
856+
sudo cp goreleaser /usr/local/bin/
857857
858858
install_macos_notarization_service:
859859
- command: shell.exec

.evergreen.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ variables:
8282
- func: clone
8383
- func: switch_context
8484
- func: setup_building_host_minikube
85+
- func: install_goreleaser
8586
- func: build_multi_cluster_binary
8687

8788
- &setup_group_multi_cluster
@@ -91,6 +92,7 @@ variables:
9192
- func: download_kube_tools
9293
- func: switch_context
9394
- func: setup_building_host
95+
- func: install_goreleaser
9496
- func: build_multi_cluster_binary
9597

9698
- &setup_and_teardown_task_cloudqa
@@ -346,6 +348,7 @@ tasks:
346348
commands:
347349
- func: clone
348350
- func: setup_building_host
351+
- func: install_goreleaser
349352
- func: build_multi_cluster_binary
350353
- func: pipeline
351354
vars:
@@ -355,6 +358,7 @@ tasks:
355358
commands:
356359
- func: clone
357360
- func: setup_building_host
361+
- func: install_goreleaser
358362
- func: build_multi_cluster_binary
359363
- func: pipeline
360364
vars:
@@ -373,6 +377,7 @@ tasks:
373377
add_to_path:
374378
- ${workdir}/bin
375379
command: scripts/evergreen/setup_minikube_host.sh
380+
- func: install_goreleaser
376381
- func: build_multi_cluster_binary
377382
- func: build_test_image_ibm
378383

.goreleaser.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ builds:
1414
goarch:
1515
- amd64
1616
- arm64
17+
- s390x
18+
- ppc64le
1719
hooks:
1820
# This will notarize Apple binaries and replace goreleaser bins with the notarized ones
1921
post:

build_info.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -349,14 +349,14 @@
349349
"binaries": {
350350
"kubectl-mongodb": {
351351
"patch": {
352-
"s3-store": "s3://kubectl-mongodb/dev",
352+
"s3-store": "mongodb-kubernetes-dev",
353353
"platforms": [
354354
"linux/amd64"
355355
]
356356
},
357357
"staging": {
358-
"sign": true,
359-
"s3-store": "s3://kubectl-mongodb/staging",
358+
"sign": false,
359+
"s3-store": "mongodb-kubernetes-staging",
360360
"platforms": [
361361
"darwin/amd64",
362362
"darwin/arm64",
@@ -366,7 +366,7 @@
366366
},
367367
"release": {
368368
"sign": true,
369-
"s3-store": "s3://kubectl-mongodb/prod",
369+
"s3-store": "mongodb-kubernetes-release",
370370
"platforms": [
371371
"darwin/amd64",
372372
"darwin/arm64",
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import os
2+
import subprocess
3+
import sys
4+
5+
import boto3
6+
from botocore.exceptions import ClientError, NoCredentialsError, PartialCredentialsError
7+
8+
from lib.base_logger import logger
9+
from scripts.release.build.build_info import (
10+
load_build_info,
11+
)
12+
13+
AWS_REGION = "eu-north-1"
14+
KUBECTL_PLUGIN_BINARY_NAME = "kubectl-mongodb"
15+
S3_BUCKET_KUBECTL_PLUGIN_SUBPATH = KUBECTL_PLUGIN_BINARY_NAME
16+
17+
GORELEASER_DIST_DIR = "dist"
18+
19+
20+
def run_goreleaser():
21+
try:
22+
command = ["goreleaser", "build", "--snapshot", "--clean", "--skip", "post-hooks"]
23+
24+
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)
25+
26+
for log in iter(process.stdout.readline, ""):
27+
print(log, end="")
28+
29+
process.stdout.close()
30+
exit_code = process.wait()
31+
32+
if exit_code != 0:
33+
logger.debug(f"GoReleaser command failed with exit code {exit_code}.")
34+
sys.exit(1)
35+
36+
logger.info("GoReleaser build completed successfully!")
37+
38+
except FileNotFoundError:
39+
logger.debug(
40+
"ERROR: 'goreleaser' command not found. Please ensure goreleaser is installed and in your system's PATH."
41+
)
42+
sys.exit(1)
43+
except Exception as e:
44+
logger.debug(f"An unexpected error occurred while running `goreleaser build`: {e}")
45+
sys.exit(1)
46+
47+
48+
# upload_artifacts_to_s3 uploads the artifacts that are generated by goreleaser to S3 bucket at a specific path.
49+
# The S3 bucket and version are figured out and passed to this function based on BuildScenario.
50+
def upload_artifacts_to_s3(s3_bucket: str, version: str):
51+
if not os.path.isdir(GORELEASER_DIST_DIR):
52+
logger.info(f"ERROR: GoReleaser dist directory '{GORELEASER_DIST_DIR}' not found.")
53+
sys.exit(1)
54+
55+
try:
56+
s3_client = boto3.client("s3", region_name=AWS_REGION)
57+
except (NoCredentialsError, PartialCredentialsError):
58+
logger.debug("ERROR: Failed to create S3 client. AWS credentials not found.")
59+
sys.exit(1)
60+
except Exception as e:
61+
logger.debug(f"An error occurred connecting to S3: {e}")
62+
sys.exit(1)
63+
64+
uploaded_files = 0
65+
# iterate over all the files generated by goreleaser in the dist directory and upload them to S3
66+
for subdir in os.listdir(GORELEASER_DIST_DIR):
67+
subdir_path = os.path.join(GORELEASER_DIST_DIR, subdir)
68+
if not os.path.isdir(subdir_path):
69+
continue # not a directory
70+
71+
for filename in os.listdir(subdir_path):
72+
local_file_path = os.path.join(subdir_path, filename)
73+
if not os.path.isfile(local_file_path):
74+
continue
75+
76+
s3_key = s3_path(local_file_path, version)
77+
logger.info(f"Uploading artifact {local_file_path} to s3://{s3_bucket}/{s3_key}")
78+
try:
79+
s3_client.upload_file(local_file_path, s3_bucket, s3_key)
80+
logger.info(f"Successfully uploaded the artifact {filename}")
81+
uploaded_files += 1
82+
except Exception as e:
83+
logger.debug(f"ERROR: Failed to upload file {filename}: {e}")
84+
sys.exit(1)
85+
86+
if uploaded_files > 0:
87+
logger.info(f"Successfully uploaded {uploaded_files} kubectl-mongodb plugin artifacts to S3.")
88+
89+
90+
# s3_path returns the path where the artifacts should be uploaded to in S3 object store.
91+
# For dev workflows it's going to be `kubectl-mongodb/{evg-patch-id}/{goreleaser-artifact}`,
92+
# for staging workflows it would be `kubectl-mongodb/{commit-sha}/{goreleaser-artifact}`.
93+
# The `version` string has the correct version (either patch id or commit sha), based on the BuildScenario.
94+
def s3_path(local_path: str, version: str):
95+
return f"{S3_BUCKET_KUBECTL_PLUGIN_SUBPATH}/{version}/{local_path}"
96+
97+
98+
def s3_and_local_plugin_path(version: str) -> dict[str, str]:
99+
s3_common_path = f"{S3_BUCKET_KUBECTL_PLUGIN_SUBPATH}/{version}/dist"
100+
local_common_path = "docker/mongodb-kubernetes-tests"
101+
# path in s3 : local path where tests image expects the binary
102+
return {
103+
f"{s3_common_path}/kubectl-mongodb_linux_amd64_v1/kubectl-mongodb": f"{local_common_path}/multi-cluster-kube-config-creator_amd64",
104+
f"{s3_common_path}/kubectl-mongodb_linux_arm64/kubectl-mongodb": f"{local_common_path}/multi-cluster-kube-config-creator_arm64",
105+
f"{s3_common_path}/kubectl-mongodb_linux_ppc64le/kubectl-mongodb": f"{local_common_path}/multi-cluster-kube-config-creator_ppc64le",
106+
f"{s3_common_path}/kubectl-mongodb_linux_s390x/kubectl-mongodb": f"{local_common_path}/multi-cluster-kube-config-creator_s390x",
107+
}
108+
109+
110+
# download_plugin_for_tests_image downloads the plugin for all the architectures and places them to the paths configured in
111+
# s3_and_local_plugin_path
112+
def download_plugin_for_tests_image(s3_bucket: str, version: str):
113+
try:
114+
s3_client = boto3.client("s3", region_name=AWS_REGION)
115+
except Exception as e:
116+
logger.debug(f"An error occurred connecting to S3 to download kubectl plugin for tests image: {e}")
117+
sys.exit(1)
118+
119+
plugin_path = f"{S3_BUCKET_KUBECTL_PLUGIN_SUBPATH}/{version}/dist/kubectl-mongodb_linux_amd64_v1/kubectl-mongodb"
120+
for plugin_path, local_path in s3_and_local_plugin_path(version).items():
121+
logger.info(f"Downloading s3://{s3_bucket}/{plugin_path} to {local_path}")
122+
try:
123+
s3_client.download_file(s3_bucket, plugin_path, local_path)
124+
# change the file's permissions to make file executable
125+
os.chmod(local_path, 0o755)
126+
127+
logger.info(f"Successfully downloaded artifact to {local_path}")
128+
except ClientError as e:
129+
if e.response["Error"]["Code"] == "404":
130+
logger.debug(f"ERROR: Artifact not found at s3://{s3_bucket}/{plugin_path} ")
131+
else:
132+
logger.debug(f"ERROR: Failed to download artifact. S3 Client Error: {e}")
133+
sys.exit(1)
134+
except Exception as e:
135+
logger.debug(f"An unexpected error occurred during download: {e}")
136+
sys.exit(1)
137+
138+
139+
def main():
140+
build_scenario = os.environ.get("BUILD_SCENARIO")
141+
kubectl_plugin_build_info = load_build_info(build_scenario).binaries[KUBECTL_PLUGIN_BINARY_NAME]
142+
143+
run_goreleaser()
144+
145+
version = os.environ.get("OPERATOR_VERSION")
146+
upload_artifacts_to_s3(kubectl_plugin_build_info.s3_store, version)
147+
148+
download_plugin_for_tests_image(kubectl_plugin_build_info.s3_store, version)
149+
150+
151+
if __name__ == "__main__":
152+
main()

scripts/release/tests/build_info_test.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def test_load_build_info_development():
8585
},
8686
binaries={
8787
"kubectl-mongodb": BinaryInfo(
88-
s3_store="s3://kubectl-mongodb/dev",
88+
s3_store="mongodb-kubernetes-dev",
8989
platforms=["linux/amd64"],
9090
)
9191
},
@@ -179,7 +179,7 @@ def test_load_build_info_patch():
179179
},
180180
binaries={
181181
"kubectl-mongodb": BinaryInfo(
182-
s3_store="s3://kubectl-mongodb/dev",
182+
s3_store="mongodb-kubernetes-dev",
183183
platforms=["linux/amd64"],
184184
)
185185
},
@@ -293,9 +293,9 @@ def test_load_build_info_staging():
293293
},
294294
binaries={
295295
"kubectl-mongodb": BinaryInfo(
296-
s3_store="s3://kubectl-mongodb/staging",
296+
s3_store="mongodb-kubernetes-staging",
297297
platforms=["darwin/amd64", "darwin/arm64", "linux/amd64", "linux/arm64"],
298-
sign=True,
298+
sign=False,
299299
)
300300
},
301301
helm_charts={
@@ -397,7 +397,7 @@ def test_load_build_info_release():
397397
},
398398
binaries={
399399
"kubectl-mongodb": BinaryInfo(
400-
s3_store="s3://kubectl-mongodb/prod",
400+
s3_store="mongodb-kubernetes-release",
401401
platforms=["darwin/amd64", "darwin/arm64", "linux/amd64", "linux/arm64"],
402402
sign=True,
403403
)

0 commit comments

Comments
 (0)