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

osbs opentelemetry #2083

Merged
merged 2 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions atomic_reactor/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
IMAGE_TYPE_OCI = 'oci'
IMAGE_TYPE_OCI_TAR = 'oci-tar'

OTEL_SERVICE_NAME = 'osbs'

PLUGIN_KOJI_PROMOTE_PLUGIN_KEY = 'koji_promote'
PLUGIN_KOJI_IMPORT_PLUGIN_KEY = 'koji_import'
PLUGIN_KOJI_IMPORT_SOURCE_CONTAINER_PLUGIN_KEY = 'koji_import_source_container'
Expand Down
8 changes: 8 additions & 0 deletions atomic_reactor/schemas/source_containers_user_params.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@
"kind": {"type": "string"},
"koji_target": {"type": "string"},
"koji_task_id": {"type": "integer"},
"opentelemetry_info": {
"type": "object",
"properties": {
"traceparent": {"type": ["string", "null"]},
"otel_url": {"type": ["string", "null"]}
},
"additionalProperties": false
},
"reactor_config_map": {"type": "string"},
"scratch": {"type": "boolean"},
"signing_intent": {"type": "string"},
Expand Down
8 changes: 8 additions & 0 deletions atomic_reactor/schemas/user_params.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@
"koji_task_id": {"type": "integer"},
"name": {"type": "string"},
"operator_csv_modifications_url": {"type": "string"},
"opentelemetry_info": {
"type": "object",
"properties": {
"traceparent": {"type": ["string", "null"]},
"otel_url": {"type": ["string", "null"]}
},
"additionalProperties": false
},
"platform": {"type": "string"},
"platforms": {
"type": "array",
Expand Down
49 changes: 31 additions & 18 deletions atomic_reactor/tasks/binary_container_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
from json import JSONDecodeError

from osbs.utils import ImageName
from otel_extensions import instrumented, get_tracer

from atomic_reactor import dirs
from atomic_reactor import util
from atomic_reactor.constants import REMOTE_HOST_MAX_RETRIES, REMOTE_HOST_RETRY_INTERVAL

from atomic_reactor.constants import (REMOTE_HOST_MAX_RETRIES, REMOTE_HOST_RETRY_INTERVAL,
OTEL_SERVICE_NAME)
from atomic_reactor.tasks.common import Task, TaskParams
from atomic_reactor.utils import retries
from atomic_reactor.utils import remote_host
Expand Down Expand Up @@ -103,23 +104,32 @@ def execute(self) -> Any:
podman_remote = PodmanRemote.setup_for(
remote_resource, registries_authfile=get_authfile_path(config.registry)
)
module_name = self.task_name + '_' + platform
tracer = get_tracer(module_name=module_name, service_name=OTEL_SERVICE_NAME)
with tracer.start_as_current_span("build_container") as span:
span.set_attribute('git_ref', self._params.user_params.get('git_ref'))
span.set_attribute('git_uri', self._params.user_params.get('git_uri'))
span.set_attribute('git_commit',
self._params.source.provider_params.get('git_commit', None))
koji_task_id = self._params.user_params.get('koji_task_id', None)
if koji_task_id:
span.set_attribute('koji_task_id', koji_task_id)
# log the image+host for auditing purposes
logger.info("Building image=%s on host=%s", dest_tag, remote_resource.host.hostname)

output_lines = podman_remote.build_container(
build_dir=build_dir,
build_args=self.workflow_data.buildargs,
dest_tag=dest_tag,
flatpak=flatpak,
memory_limit=config.remote_hosts.get("memory_limit"),
podman_capabilities=config.remote_hosts.get("podman_capabilities")
)
for line in output_lines:
logger.info(line.rstrip())
build_log_file.write(line)

# log the image+host for auditing purposes
logger.info("Building image=%s on host=%s", dest_tag, remote_resource.host.hostname)

output_lines = podman_remote.build_container(
build_dir=build_dir,
build_args=self.workflow_data.buildargs,
dest_tag=dest_tag,
flatpak=flatpak,
memory_limit=config.remote_hosts.get("memory_limit"),
podman_capabilities=config.remote_hosts.get("podman_capabilities")
)
for line in output_lines:
logger.info(line.rstrip())
build_log_file.write(line)

logger.info("Build finished successfully! Pushing image to %s", dest_tag)
logger.info("Build finished successfully! Pushing image to %s", dest_tag)

image_size_limit = config.image_size_limit['binary_image']
image_size = podman_remote.get_image_size(dest_tag)
Expand All @@ -135,6 +145,7 @@ def execute(self) -> Any:

return remote_resource.host.hostname

@instrumented
def acquire_remote_resource(self, remote_hosts_config: dict) -> remote_host.LockedResource:
"""Lock a build slot on a remote host."""
logger.info("Acquiring a build slot on a remote host")
Expand Down Expand Up @@ -310,6 +321,7 @@ def build_container(
else:
yield last_line

@instrumented
def get_image_size(self, dest_tag: ImageName) -> int:
inspect_cmd = [*self._podman_remote_cmd, 'image', 'inspect', str(dest_tag)]
try:
Expand All @@ -328,6 +340,7 @@ def get_image_size(self, dest_tag: ImageName) -> int:
f"{str(dest_tag)}") from e
return image_size

@instrumented
def push_container(self, dest_tag: ImageName, *, insecure: bool = False) -> None:
"""Push the built container (named dest_tag) to the registry (as dest_tag).

Expand Down
43 changes: 40 additions & 3 deletions atomic_reactor/tasks/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,28 @@
"""

import abc
import signal
import json
import logging
import os
import signal
from dataclasses import dataclass
from functools import cached_property
from pathlib import Path
from typing import Dict, Any, ClassVar, Generic, TypeVar, Optional
from functools import cached_property

from opentelemetry.instrumentation.requests import RequestsInstrumentor
from otel_extensions import TelemetryOptions, init_telemetry_provider, get_tracer

from atomic_reactor import config
from atomic_reactor import dirs
from atomic_reactor import inner
from atomic_reactor import source
from atomic_reactor import util
from atomic_reactor.constants import OTEL_SERVICE_NAME
from atomic_reactor.plugin import TaskCanceledException

logger = logging.getLogger(__name__)


def write_task_result(output_file, msg):
with open(output_file, 'w') as f:
Expand Down Expand Up @@ -88,6 +96,7 @@ class Task(abc.ABC, Generic[ParamsT]):
ignore_sigterm: ClassVar[bool] = False
# Automatically save context data before exiting? (Note: do not use for parallel tasks)
autosave_context_data: ClassVar[bool] = True
task_name = 'default'

def __init__(self, params: ParamsT):
"""Initialize a Task."""
Expand Down Expand Up @@ -122,7 +131,35 @@ def run(self, *args, **kwargs):
else:
signal.signal(signal.SIGTERM, self.throw_task_canceled_exception)

result = self.execute(*args, **kwargs)
opentelemetry_info = self._params.user_params.get('opentelemetry_info', {})
traceparent = opentelemetry_info.get('traceparent', None)
otel_url = opentelemetry_info.get('otel_url', None)

span_exporter = ''
otel_protocol = 'http/protobuf'
if not otel_url:
otel_protocol = 'custom'
span_exporter = '"opentelemetry.sdk.trace.export.ConsoleSpanExporter"'

if traceparent:
os.environ['TRACEPARENT'] = traceparent
hjmodi marked this conversation as resolved.
Show resolved Hide resolved
logger.info('traceparent is set to %s', traceparent)
otel_options = TelemetryOptions(
OTEL_SERVICE_NAME=OTEL_SERVICE_NAME,
OTEL_EXPORTER_CUSTOM_SPAN_EXPORTER_TYPE=span_exporter,
OTEL_EXPORTER_OTLP_ENDPOINT=otel_url,
OTEL_EXPORTER_OTLP_PROTOCOL=otel_protocol,
)
init_telemetry_provider(otel_options)

RequestsInstrumentor().instrument()

span_name = self.task_name
if hasattr(self._params, 'platform'):
span_name += '_' + self._params.platform
tracer = get_tracer(module_name=span_name, service_name=OTEL_SERVICE_NAME)
with tracer.start_as_current_span(span_name):
result = self.execute(*args, **kwargs)
if self._params.task_result:
write_task_result(self._params.task_result, json.dumps(result))

Expand Down
1 change: 0 additions & 1 deletion atomic_reactor/tasks/plugin_based.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ class PluginBasedTask(Task[ParamsT]):
# {"name": "add_filesystem", "args": {...}}
# Refer to plugins.json schema for the details.
plugins_conf: ClassVar[List[Dict[str, Any]]] = []
task_name = 'default'
rcerven marked this conversation as resolved.
Show resolved Hide resolved

def prepare_workflow(self) -> inner.DockerBuildWorkflow:
"""Fully initialize the workflow instance to be used for running the list of plugins."""
Expand Down
8 changes: 8 additions & 0 deletions otel-requirements.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
calver
hatchling
opentelemetry-api
opentelemetry-exporter-otlp
opentelemetry-instrumentation-requests
opentelemetry-sdk
otel-extensions
urllib3 < 2.0
112 changes: 112 additions & 0 deletions otel-requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#
# This file is autogenerated by pip-compile with Python 3.8
# by the following command:
#
# pip-compile --output-file=otel-requirements.txt otel-requirements.in
#
backoff==2.2.1
# via
# opentelemetry-exporter-otlp-proto-grpc
# opentelemetry-exporter-otlp-proto-http
calver==2022.6.26
# via -r otel-requirements.in
certifi==2023.7.22
# via requests
charset-normalizer==3.2.0
# via requests
deprecated==1.2.14
# via
# opentelemetry-api
# opentelemetry-exporter-otlp-proto-grpc
# opentelemetry-exporter-otlp-proto-http
editables==0.5
# via hatchling
googleapis-common-protos==1.60.0
# via
# opentelemetry-exporter-otlp-proto-grpc
# opentelemetry-exporter-otlp-proto-http
grpcio==1.57.0
# via opentelemetry-exporter-otlp-proto-grpc
hatchling==1.18.0
# via -r otel-requirements.in
idna==3.4
# via requests
importlib-metadata==6.8.0
# via opentelemetry-api
opentelemetry-api==1.19.0
# via
# -r otel-requirements.in
# opentelemetry-exporter-otlp-proto-grpc
# opentelemetry-exporter-otlp-proto-http
# opentelemetry-instrumentation
# opentelemetry-instrumentation-requests
# opentelemetry-sdk
# otel-extensions
opentelemetry-exporter-otlp==1.19.0
# via -r otel-requirements.in
opentelemetry-exporter-otlp-proto-common==1.19.0
# via
# opentelemetry-exporter-otlp-proto-grpc
# opentelemetry-exporter-otlp-proto-http
opentelemetry-exporter-otlp-proto-grpc==1.19.0
# via opentelemetry-exporter-otlp
opentelemetry-exporter-otlp-proto-http==1.19.0
# via opentelemetry-exporter-otlp
opentelemetry-instrumentation==0.40b0
# via opentelemetry-instrumentation-requests
opentelemetry-instrumentation-requests==0.40b0
# via -r otel-requirements.in
opentelemetry-proto==1.19.0
# via
# opentelemetry-exporter-otlp-proto-common
# opentelemetry-exporter-otlp-proto-grpc
# opentelemetry-exporter-otlp-proto-http
opentelemetry-sdk==1.19.0
# via
# -r otel-requirements.in
# opentelemetry-exporter-otlp-proto-grpc
# opentelemetry-exporter-otlp-proto-http
# otel-extensions
opentelemetry-semantic-conventions==0.40b0
# via
# opentelemetry-instrumentation-requests
# opentelemetry-sdk
opentelemetry-util-http==0.40b0
# via opentelemetry-instrumentation-requests
otel-extensions==0.2.5
# via -r otel-requirements.in
packaging==23.1
# via hatchling
pathspec==0.11.2
# via hatchling
pluggy==1.3.0
# via hatchling
protobuf==4.24.2
# via
# googleapis-common-protos
# opentelemetry-proto
pydantic==1.10.12
# via otel-extensions
requests==2.31.0
# via opentelemetry-exporter-otlp-proto-http
tomli==2.0.1
# via hatchling
trove-classifiers==2023.8.7
# via hatchling
typing-extensions==4.7.1
# via
# opentelemetry-sdk
# pydantic
urllib3==1.26.16
# via
# -r otel-requirements.in
# requests
wrapt==1.15.0
# via
# deprecated
# opentelemetry-instrumentation
zipp==3.16.2
# via importlib-metadata

# The following packages are considered to be unsafe in a requirements file:
# setuptools
Loading
Loading