...

Text file src/github.com/emissary-ingress/emissary/v3/python/ambassador/ir/irtracing.py

Documentation: github.com/emissary-ingress/emissary/v3/python/ambassador/ir

     1from typing import TYPE_CHECKING, Optional
     2
     3from ..config import Config
     4from ..utils import RichStatus
     5from .ircluster import IRCluster
     6from .irresource import IRResource
     7
     8if TYPE_CHECKING:
     9    from .ir import IR  # pragma: no cover
    10
    11
    12class IRTracing(IRResource):
    13    cluster: Optional[IRCluster]
    14    service: str
    15    driver: str
    16    driver_config: dict
    17    # TODO: tag_headers is deprecated and should be removed once migrated to CRD v3
    18    tag_headers: list
    19    custom_tags: list
    20    host_rewrite: Optional[str]
    21    sampling: dict
    22
    23    def __init__(
    24        self,
    25        ir: "IR",
    26        aconf: Config,
    27        rkey: str = "ir.tracing",
    28        kind: str = "ir.tracing",
    29        name: str = "tracing",
    30        namespace: Optional[str] = None,
    31        **kwargs
    32    ) -> None:
    33        del kwargs  # silence unused-variable warning
    34
    35        super().__init__(ir=ir, aconf=aconf, rkey=rkey, kind=kind, name=name, namespace=namespace)
    36        self.cluster = None
    37
    38    def setup(self, ir: "IR", aconf: Config) -> bool:
    39        # Some of the validations might go away if JSON Schema is doing the validations, but need to check on that
    40
    41        config_info = aconf.get_config("tracing_configs")
    42
    43        if not config_info:
    44            ir.logger.debug("IRTracing: no tracing config, bailing")
    45            # No tracing info. Be done.
    46            return False
    47
    48        configs = config_info.values()
    49        number_configs = len(configs)
    50        if number_configs != 1:
    51            self.post_error(
    52                RichStatus.fromError(
    53                    "exactly one TracingService is supported, got {}".format(number_configs),
    54                    module=aconf,
    55                )
    56            )
    57            return False
    58
    59        config = list(configs)[0]
    60
    61        service = config.get("service")
    62        if not service:
    63            self.post_error(RichStatus.fromError("service field is required in TracingService"))
    64            return False
    65
    66        driver = config.get("driver")
    67        if not driver:
    68            self.post_error(RichStatus.fromError("driver field is required in TracingService"))
    69            return False
    70
    71        self.namespace = config.get("namespace", self.namespace)
    72
    73        grpc = False
    74
    75        if driver == "lightstep":
    76            self.post_error(
    77                RichStatus.fromError(
    78                    "as of v3.4+ the 'lightstep' driver is no longer supported in the TracingService, please see docs for migration options"
    79                )
    80            )
    81            return False
    82        if driver == "opentelemetry":
    83            ir.logger.warning(
    84                "The OpenTelemetry tracing driver is work-in-progress. Functionality is incomplete and it is not intended for production use. This extension has an unknown security posture and should only be used in deployments where both the downstream and upstream are trusted."
    85            )
    86            grpc = True
    87
    88        if driver == "datadog":
    89            driver = "envoy.tracers.datadog"
    90
    91        # This "config" is a field on the aconf for the TracingService, not to be confused with the
    92        # envoyv2 untyped "config" field. We actually use a "typed_config" in the final Envoy
    93        # config, see envoy/v2/v2tracer.py.
    94        driver_config = config.get("config", {})
    95
    96        if driver == "zipkin":
    97            # fill zipkin defaults
    98            if not driver_config.get("collector_endpoint"):
    99                driver_config["collector_endpoint"] = "/api/v2/spans"
   100            if not driver_config.get("collector_endpoint_version"):
   101                driver_config["collector_endpoint_version"] = "HTTP_JSON"
   102            if not "trace_id_128bit" in driver_config:
   103                # Make 128-bit traceid the default
   104                driver_config["trace_id_128bit"] = True
   105            # validate
   106            if driver_config["collector_endpoint_version"] not in ["HTTP_JSON", "HTTP_PROTO"]:
   107                self.post_error(
   108                    RichStatus.fromError(
   109                        "collector_endpoint_version must be one of HTTP_JSON, HTTP_PROTO'"
   110                    )
   111                )
   112                return False
   113
   114        # OK, we have a valid config.
   115        self.sourced_by(config)
   116
   117        self.service = service
   118        self.driver = driver
   119        self.grpc = grpc
   120        self.cluster = None
   121        self.driver_config = driver_config
   122        self.tag_headers = config.get("tag_headers", [])
   123        self.custom_tags = config.get("custom_tags", [])
   124        self.sampling = config.get("sampling", {})
   125
   126        self.stats_name = config.get("stats_name", None)
   127
   128        # XXX host_rewrite actually isn't in the schema right now.
   129        self.host_rewrite = config.get("host_rewrite", None)
   130
   131        # Remember that the config references us.
   132        self.referenced_by(config)
   133
   134        return True
   135
   136    def add_mappings(self, ir: "IR", aconf: Config):
   137        cluster = ir.add_cluster(
   138            IRCluster(
   139                ir=ir,
   140                aconf=aconf,
   141                parent_ir_resource=self,
   142                location=self.location,
   143                service=self.service,
   144                host_rewrite=self.get("host_rewrite", None),
   145                marker="tracing",
   146                grpc=self.grpc,
   147                stats_name=self.get("stats_name", None),
   148            )
   149        )
   150
   151        cluster.referenced_by(self)
   152        self.cluster = cluster
   153
   154    def finalize(self):
   155        assert self.cluster
   156        self.ir.logger.debug("tracing cluster envoy name: %s" % self.cluster.envoy_name)
   157        # Opentelemetry is the only one that does not use collector_cluster
   158        if self.driver == "opentelemetry":
   159            self.driver_config["grpc_service"] = {
   160                "envoy_grpc": {"cluster_name": self.cluster.envoy_name}
   161            }
   162        else:
   163            self.driver_config["collector_cluster"] = self.cluster.envoy_name

View as plain text