Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New package static quality gates #33878

Merged
merged 11 commits into from
Feb 13, 2025
4 changes: 2 additions & 2 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,8 @@ variables:
# Build images versions
# To use images from datadog-agent-buildimages dev branches, set the corresponding
# SUFFIX variable to _test_only
DATADOG_AGENT_BUILDIMAGES_SUFFIX: ""
DATADOG_AGENT_BUILDIMAGES: v54965839-ff6db30b
DATADOG_AGENT_BUILDIMAGES_SUFFIX: "_test_only"
DATADOG_AGENT_BUILDIMAGES: v55508864-5558bd15
Pythyu marked this conversation as resolved.
Show resolved Hide resolved
DATADOG_AGENT_WINBUILDIMAGES_SUFFIX: ""
DATADOG_AGENT_WINBUILDIMAGES: v54965839-ff6db30b
DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX: ""
Expand Down
32 changes: 32 additions & 0 deletions .gitlab/functional_test/static_quality_gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ static_quality_gates:
needs:
- job: agent_deb-x64-a7
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: artifacts: true` is the default so we could have omitted it everywhere

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch 👍 I'll remove them in my follow-up PR with other quality gates

artifacts: true
- job: agent_deb-arm64-a7
artifacts: true
- job: agent_rpm-x64-a7
artifacts: true
- job: agent_rpm-arm64-a7
artifacts: true
- job: agent_suse-x64-a7
artifacts: true
- job: agent_suse-arm64-a7
artifacts: true
- job: docker_build_agent7
artifacts: true
- job: docker_build_agent7_arm64
Expand All @@ -21,6 +31,28 @@ static_quality_gates:
artifacts: true
- job: docker_build_cluster_agent_arm64
artifacts: true
- job: docker_build_dogstatsd_amd64
artifacts: true
- job: docker_build_dogstatsd_arm64
artifacts: true
Comment on lines +34 to +37
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the static_quality_gates_docker*.py files for these two in the PR, I guess they were already there before?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea I forgot to add the dependency on the previous PR. To be fair, the explicit dependency isn't needed because dogstatsd is way smaller than other flavors - even without waiting for it specifically it will be merged before the job start being executed.

- job: dogstatsd_deb-x64
artifacts: true
- job: dogstatsd_deb-arm64
artifacts: true
- job: dogstatsd_rpm-x64
artifacts: true
- job: dogstatsd_suse-x64
artifacts: true
- job: iot_agent_deb-x64
artifacts: true
- job: iot_agent_deb-arm64
artifacts: true
- job: iot_agent_rpm-x64
artifacts: true
- job: iot_agent_rpm-arm64
artifacts: true
- job: iot_agent_suse-x64
artifacts: true
# Static Quality Gates aren't enforced until Q1
allow_failure: true
script:
Expand Down
3 changes: 2 additions & 1 deletion tasks/quality_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def display_pr_comment(
:return:
"""
title = f"Static quality checks {SUCCESS_CHAR if final_state else FAIL_CHAR}"
body_info = body_pattern.format("Info")
body_info = "<details>\n<summary>Successful checks</summary>\n" + body_pattern.format("Info")
body_error = body_pattern.format("Error")
body_error_footer = body_error_footer_pattern

Expand All @@ -64,6 +64,7 @@ def getMetric(metric_name, gate_name=gate['name']):
with_error = True

body_error_footer += "\n</details>\n"
body_info += "\n</details>\n"
body = f"Please find below the results from static quality gates\n{body_error+body_error_footer if with_error else ''}\n\n{body_info if with_info else ''}"

pr_commenter(ctx, title=title, body=body)
Expand Down
71 changes: 31 additions & 40 deletions tasks/static_quality_gates/lib/gates_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ def find_package_path(flavor, package_os, arch):


class GateMetricHandler:
# All metric_name -> metric_key
METRICS_DICT = {
"datadog.agent.static_quality_gate.on_wire_size": "current_on_wire_size",
"datadog.agent.static_quality_gate.on_disk_size": "current_on_disk_size",
"datadog.agent.static_quality_gate.max_allowed_on_wire_size": "max_on_wire_size",
"datadog.agent.static_quality_gate.max_allowed_on_disk_size": "max_on_disk_size",
}

def __init__(self, git_ref, bucket_branch):
self.metrics = {}
self.metadata = {}
Expand All @@ -101,6 +109,18 @@ def register_gate_tags(self, gate, **kwargs):
for key in kwargs:
self.metadata[gate][key] = kwargs[key]

def _add_gauge(self, timestamp, common_tags, gate, metric_name, metric_key):
if self.metrics[gate].get(metric_key, None) is None:
Pythyu marked this conversation as resolved.
Show resolved Hide resolved
return None
return create_gauge(
metric_name,
timestamp,
self.metrics[gate][metric_key],
tags=common_tags,
metric_origin=get_metric_origin(ORIGIN_PRODUCT, ORIGIN_CATEGORY, ORIGIN_SERVICE),
unit="byte",
)

def _generate_series(self):
if not self.git_ref or not self.bucket_branch:
return None
Expand All @@ -120,46 +140,17 @@ def _generate_series(self):
for tag in self.metadata[gate]:
common_tags.append(f"{tag}:{self.metadata[gate][tag]}")

series.append(
create_gauge(
"datadog.agent.static_quality_gate.on_wire_size",
timestamp,
self.metrics[gate]["current_on_wire_size"],
tags=common_tags,
metric_origin=get_metric_origin(ORIGIN_PRODUCT, ORIGIN_CATEGORY, ORIGIN_SERVICE),
unit="byte",
),
)
series.append(
create_gauge(
"datadog.agent.static_quality_gate.on_disk_size",
timestamp,
self.metrics[gate]["current_on_disk_size"],
tags=common_tags,
metric_origin=get_metric_origin(ORIGIN_PRODUCT, ORIGIN_CATEGORY, ORIGIN_SERVICE),
unit="byte",
),
)
series.append(
create_gauge(
"datadog.agent.static_quality_gate.max_allowed_on_wire_size",
timestamp,
self.metrics[gate]["max_on_wire_size"],
tags=common_tags,
metric_origin=get_metric_origin(ORIGIN_PRODUCT, ORIGIN_CATEGORY, ORIGIN_SERVICE),
unit="byte",
),
)
series.append(
create_gauge(
"datadog.agent.static_quality_gate.max_allowed_on_disk_size",
timestamp,
self.metrics[gate]["max_on_disk_size"],
tags=common_tags,
metric_origin=get_metric_origin(ORIGIN_PRODUCT, ORIGIN_CATEGORY, ORIGIN_SERVICE),
unit="byte",
),
)
for metric_name, metric_key in self.METRICS_DICT.items():
gauge = self._add_gauge(timestamp, common_tags, gate, metric_name, metric_key)
if gauge:
series.append(gauge)
else:
print(
color_message(
f"[WARN] gate {gate} doesn't have the {metric_name} metric registered ! skipping metric...",
"orange",
)
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks way better that way!

return series

def send_metrics_to_datadog(self):
Expand Down
72 changes: 72 additions & 0 deletions tasks/static_quality_gates/lib/package_agent_lib.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import tempfile

from tasks.libs.common.color import color_message
from tasks.libs.package.size import directory_size, extract_package, file_size
from tasks.static_quality_gates.lib.gates_lib import argument_extractor, find_package_path, read_byte_input


def calculate_package_size(ctx, package_os, package_path, gate_name, metric_handler):
with tempfile.TemporaryDirectory() as extract_dir:
extract_package(ctx=ctx, package_os=package_os, package_path=package_path, extract_dir=extract_dir)
package_on_wire_size = file_size(path=package_path)
package_on_disk_size = directory_size(ctx, path=extract_dir)

metric_handler.register_metric(gate_name, "current_on_wire_size", package_on_wire_size)
metric_handler.register_metric(gate_name, "current_on_disk_size", package_on_disk_size)
return package_on_wire_size, package_on_disk_size


def check_package_size(package_on_wire_size, package_on_disk_size, max_on_wire_size, max_on_disk_size):
error_message = ""
if package_on_wire_size > max_on_wire_size:
err_msg = f"Package size on wire (compressed package size) {package_on_wire_size} is higher than the maximum allowed {max_on_wire_size} by the gate !\n"
print(color_message(err_msg, "red"))
error_message += err_msg
else:
print(
color_message(
f"package_on_wire_size <= max_on_wire_size, ({package_on_wire_size}) <= ({max_on_wire_size})",
"green",
)
)
if package_on_disk_size > max_on_disk_size:
err_msg = f"Package size on disk (uncompressed package size) {package_on_disk_size} is higher than the maximum allowed {max_on_disk_size} by the gate !\n"
print(color_message(err_msg, "red"))
error_message += err_msg
else:
print(
color_message(
f"package_on_disk_size <= max_on_disk_size, ({package_on_disk_size}) <= ({max_on_disk_size})",
"green",
)
)
if error_message != "":
raise AssertionError(error_message)


def generic_package_agent_quality_gate(gate_name, arch, os, flavor, **kwargs):
arguments = argument_extractor(
kwargs, max_on_wire_size=read_byte_input, max_on_disk_size=read_byte_input, ctx=None, metricHandler=None
)
ctx = arguments.ctx
metric_handler = arguments.metricHandler
max_on_wire_size = arguments.max_on_wire_size
max_on_disk_size = arguments.max_on_disk_size

metric_handler.register_gate_tags(gate_name, gate_name=gate_name, arch=arch, os=os)

metric_handler.register_metric(gate_name, "max_on_wire_size", max_on_wire_size)
metric_handler.register_metric(gate_name, "max_on_disk_size", max_on_disk_size)
package_arm = arch
if os == "centos" or os == "suse":
if arch == "arm64":
package_arm = "aarch64"
elif arch == "amd64":
package_arm = "x86_64"

package_path = find_package_path(flavor, os, package_arm)

package_on_wire_size, package_on_disk_size = calculate_package_size(
ctx, os, package_path, gate_name, metric_handler
)
check_package_size(package_on_wire_size, package_on_disk_size, max_on_wire_size, max_on_disk_size)
Original file line number Diff line number Diff line change
@@ -1,63 +1,7 @@
import tempfile

from tasks.libs.common.color import color_message
from tasks.libs.package.size import directory_size, extract_package, file_size
from tasks.static_quality_gates.lib.gates_lib import argument_extractor, find_package_path, read_byte_input
from tasks.static_quality_gates.lib.package_agent_lib import generic_package_agent_quality_gate


def entrypoint(**kwargs):
arguments = argument_extractor(
kwargs, max_on_wire_size=read_byte_input, max_on_disk_size=read_byte_input, ctx=None, metricHandler=None
)
ctx = arguments.ctx
metric_handler = arguments.metricHandler
max_on_wire_size = arguments.max_on_wire_size
max_on_disk_size = arguments.max_on_disk_size

metric_handler.register_gate_tags(
"static_quality_gate_agent_deb_amd64", gate_name="static_quality_gate_agent_deb_amd64", arch="x64", os="debian"
generic_package_agent_quality_gate(
"static_quality_gate_agent_deb_amd64", "amd64", "debian", "datadog-agent", **kwargs
)

metric_handler.register_metric("static_quality_gate_agent_deb_amd64", "max_on_wire_size", max_on_wire_size)
metric_handler.register_metric("static_quality_gate_agent_deb_amd64", "max_on_disk_size", max_on_disk_size)

package_os = "debian"
package_path = find_package_path("datadog-agent", package_os, "amd64")

with tempfile.TemporaryDirectory() as extract_dir:
extract_package(ctx=ctx, package_os=package_os, package_path=package_path, extract_dir=extract_dir)
package_on_wire_size = file_size(path=package_path)
package_on_disk_size = directory_size(ctx, path=extract_dir)

metric_handler.register_metric(
"static_quality_gate_agent_deb_amd64", "current_on_wire_size", package_on_wire_size
)
metric_handler.register_metric(
"static_quality_gate_agent_deb_amd64", "current_on_disk_size", package_on_disk_size
)

error_message = ""
if package_on_wire_size > max_on_wire_size:
err_msg = f"Package size on wire (compressed package size) {package_on_wire_size} is higher than the maximum allowed {max_on_wire_size} by the gate !\n"
print(color_message(err_msg, "red"))
error_message += err_msg
else:
print(
color_message(
f"package_on_wire_size <= max_on_wire_size, ({package_on_wire_size}) <= ({max_on_wire_size})",
"green",
)
)
if package_on_disk_size > max_on_disk_size:
err_msg = f"Package size on disk (uncompressed package size) {package_on_disk_size} is higher than the maximum allowed {max_on_disk_size} by the gate !\n"
print(color_message(err_msg, "red"))
error_message += err_msg
else:
print(
color_message(
f"package_on_disk_size <= max_on_disk_size, ({package_on_disk_size}) <= ({max_on_disk_size})",
"green",
)
)
if error_message != "":
raise AssertionError(error_message)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from tasks.static_quality_gates.lib.package_agent_lib import generic_package_agent_quality_gate


def entrypoint(**kwargs):
generic_package_agent_quality_gate(
"static_quality_gate_agent_deb_arm64", "arm64", "debian", "datadog-agent", **kwargs
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from tasks.static_quality_gates.lib.package_agent_lib import generic_package_agent_quality_gate


def entrypoint(**kwargs):
generic_package_agent_quality_gate(
"static_quality_gate_agent_rpm_amd64", "amd64", "centos", "datadog-agent", **kwargs
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from tasks.static_quality_gates.lib.package_agent_lib import generic_package_agent_quality_gate


def entrypoint(**kwargs):
generic_package_agent_quality_gate(
"static_quality_gate_agent_rpm_arm64", "arm64", "centos", "datadog-agent", **kwargs
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from tasks.static_quality_gates.lib.package_agent_lib import generic_package_agent_quality_gate


def entrypoint(**kwargs):
generic_package_agent_quality_gate(
"static_quality_gate_agent_suse_amd64", "amd64", "suse", "datadog-agent", **kwargs
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from tasks.static_quality_gates.lib.package_agent_lib import generic_package_agent_quality_gate


def entrypoint(**kwargs):
generic_package_agent_quality_gate(
"static_quality_gate_agent_suse_arm64", "arm64", "suse", "datadog-agent", **kwargs
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from tasks.static_quality_gates.lib.package_agent_lib import generic_package_agent_quality_gate


def entrypoint(**kwargs):
generic_package_agent_quality_gate(
"static_quality_gate_dogstatsd_deb_amd64", "amd64", "debian", "datadog-dogstatsd", **kwargs
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from tasks.static_quality_gates.lib.package_agent_lib import generic_package_agent_quality_gate


def entrypoint(**kwargs):
generic_package_agent_quality_gate(
"static_quality_gate_dogstatsd_deb_arm64", "arm64", "debian", "datadog-dogstatsd", **kwargs
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from tasks.static_quality_gates.lib.package_agent_lib import generic_package_agent_quality_gate


def entrypoint(**kwargs):
generic_package_agent_quality_gate(
"static_quality_gate_dogstatsd_rpm_amd64", "amd64", "centos", "datadog-dogstatsd", **kwargs
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from tasks.static_quality_gates.lib.package_agent_lib import generic_package_agent_quality_gate


def entrypoint(**kwargs):
generic_package_agent_quality_gate(
"static_quality_gate_dogstatsd_suse_amd64", "amd64", "suse", "datadog-dogstatsd", **kwargs
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from tasks.static_quality_gates.lib.package_agent_lib import generic_package_agent_quality_gate


def entrypoint(**kwargs):
generic_package_agent_quality_gate(
"static_quality_gate_iot_agent_deb_amd64", "amd64", "debian", "datadog-iot-agent", **kwargs
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from tasks.static_quality_gates.lib.package_agent_lib import generic_package_agent_quality_gate


def entrypoint(**kwargs):
generic_package_agent_quality_gate(
"static_quality_gate_iot_agent_deb_arm64", "arm64", "debian", "datadog-iot-agent", **kwargs
)
Loading
Loading