Skip to content
This repository was archived by the owner on Sep 17, 2025. It is now read-only.
Merged
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
Use `UnknownMetricFamily` for `SumData` instead of `UntypedMetricFamily`.
Check if label keys and values match before exporting.
- Remove min and max from Distribution.
- Replace stackdriver `gke_container` resources, see the [GKE migration
notes](https://cloud.google.com/monitoring/kubernetes-engine/migration#incompatible)
for details

## 0.2.0
Released 2019-01-18
Expand Down
101 changes: 25 additions & 76 deletions opencensus/common/monitored_resource/gcp_metadata_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,63 +13,32 @@
# limitations under the License.

from opencensus.common.http_handler import get_request
import os

_GCP_METADATA_URI = 'http://metadata/computeMetadata/v1/'
_GCP_METADATA_URI_HEADER = {'Metadata-Flavor': 'Google'}

# GCE common attributes
# See: https://cloud.google.com/appengine/docs/flexible/python/runtime#
# environment_variables
_GCE_ATTRIBUTES = {
# ProjectID is the identifier of the GCP project associated with this
# resource, such as "my-project".
'project_id': 'project/project-id',

# instance_id is the numeric VM instance identifier assigned by
# Compute Engine.
'instance_id': 'instance/id',

# zone is the Compute Engine zone in which the VM is running.
'zone': 'instance/zone'
}
# ID of the GCP project associated with this resource, such as "my-project"
PROJECT_ID_KEY = 'project_id'

_GKE_ATTRIBUTES = {
# ProjectID is the identifier of the GCP project associated with this
# resource, such as "my-project".
'project_id': 'project/project-id',

# instance_id is the numeric VM instance identifier assigned by
# Compute Engine.
'instance_id': 'instance/id',

# zone is the Compute Engine zone in which the VM is running.
'zone': 'instance/zone',

# cluster_name is the name for the cluster the container is running in.
'cluster_name': 'instance/attributes/cluster-name'
}
# Numeric VM instance identifier assigned by GCE
INSTANCE_ID_KEY = 'instance_id'

# Following attributes are derived from environment variables. They are
# configured via yaml file. For details refer to:
# https://cloud.google.com/kubernetes-engine/docs/tutorials/
# custom-metrics-autoscaling#exporting_metrics_from_the_application
_GKE_ENV_ATTRIBUTES = {
# ContainerName is the name of the container.
'container_name': 'CONTAINER_NAME',
# The GCE zone in which the VM is running
ZONE_KEY = 'zone'

# namespace_id is the identifier for the cluster namespace the container
# is running in
'namespace_id': 'NAMESPACE',
# GKE cluster name
CLUSTER_NAME_KEY = 'instance/attributes/cluster-name'

# pod_id is the identifier for the pod the container is running in.
'pod_id': 'HOSTNAME'
# GCE common attributes
# See: https://cloud.google.com/appengine/docs/flexible/python/runtime#environment_variables # noqa
_GCE_ATTRIBUTES = {
PROJECT_ID_KEY: 'project/project-id',
INSTANCE_ID_KEY: 'instance/id',
ZONE_KEY: 'instance/zone'
}

# Kubenertes environment variables
_KUBERNETES_SERVICE_HOST = 'KUBERNETES_SERVICE_HOST'

gcp_metadata_map = {}
_GCP_METADATA_MAP = {}


class GcpMetadataConfig(object):
Expand All @@ -90,23 +59,19 @@ def _initialize_metadata_service(cls):
if cls.inited:
return

instance_id = cls._get_attribute('instance_id')
instance_id = cls.get_attribute('instance/id')

if instance_id is not None:
cls.is_running = True

gcp_metadata_map['instance_id'] = instance_id

attributes = _GCE_ATTRIBUTES
if _KUBERNETES_SERVICE_HOST in os.environ:
attributes = _GKE_ATTRIBUTES
_GCP_METADATA_MAP['instance_id'] = instance_id

# fetch attributes from metadata request
for attribute_key, attribute_uri in attributes.items():
if attribute_key not in gcp_metadata_map:
attribute_value = cls._get_attribute(attribute_key)
if attribute_value is not None:
gcp_metadata_map[attribute_key] = attribute_value
for attribute_key, attribute_uri in _GCE_ATTRIBUTES.items():
if attribute_key not in _GCP_METADATA_MAP:
attribute_value = cls.get_attribute(attribute_uri)
if attribute_value is not None: # pragma: NO COVER
_GCP_METADATA_MAP[attribute_key] = attribute_value

cls.inited = True

Expand All @@ -118,35 +83,19 @@ def is_running_on_gcp(cls):
def get_gce_metadata(self):
"""for GCP GCE instance"""
if self.is_running_on_gcp():
return gcp_metadata_map
return _GCP_METADATA_MAP

return dict()

def get_gke_metadata(self):
"""for GCP GKE container."""
gke_metadata = {}

if self.is_running_on_gcp():
gke_metadata = gcp_metadata_map

# fetch attributes from Environment Variables
for attribute_key, attribute_env in _GKE_ENV_ATTRIBUTES.items():
attribute_value = os.environ.get(attribute_env)
if attribute_value is not None:
gke_metadata[attribute_key] = attribute_value

return gke_metadata

@staticmethod
def _get_attribute(attribute_key):
def get_attribute(attribute_uri):
"""
Fetch the requested instance metadata entry.
:param attribute_uri: attribute_uri: attribute name relative to the
computeMetadata/v1 prefix
:return: The value read from the metadata service or None
"""
attribute_value = get_request(_GCP_METADATA_URI +
_GKE_ATTRIBUTES[attribute_key],
attribute_value = get_request(_GCP_METADATA_URI + attribute_uri,
_GCP_METADATA_URI_HEADER)

if attribute_value is not None and isinstance(attribute_value, bytes):
Expand Down
64 changes: 64 additions & 0 deletions opencensus/common/monitored_resource/k8s_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Copyright 2019, OpenCensus Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os

from opencensus.common.monitored_resource import gcp_metadata_config

# Env var that signals that we're in a kubernetes container
_KUBERNETES_SERVICE_HOST = 'KUBERNETES_SERVICE_HOST'

# Name of the cluster the container is running in
CLUSTER_NAME_KEY = 'k8s.io/cluster/name'

# ID of the instance the container is running on
NAMESPACE_NAME_KEY = 'k8s.io/namespace/name'

# Container pod ID
POD_NAME_KEY = 'k8s.io/pod/name'

# Container name
CONTAINER_NAME_KEY = 'k8s.io/container/name'

# Attributes set from environment variables
_K8S_ENV_ATTRIBUTES = {
CONTAINER_NAME_KEY: 'CONTAINER_NAME',
NAMESPACE_NAME_KEY: 'NAMESPACE',
POD_NAME_KEY: 'HOSTNAME'
}


def is_k8s_environment():
"""Whether the environment is a kubernetes container.

The KUBERNETES_SERVICE_HOST environment variable must be set.
"""
return _KUBERNETES_SERVICE_HOST in os.environ


def get_k8s_metadata():
"""Get kubernetes container metadata, as on GCP GKE."""
k8s_metadata = {}

gcp_cluster = (gcp_metadata_config.GcpMetadataConfig
.get_attribute(gcp_metadata_config.CLUSTER_NAME_KEY))
if gcp_cluster is not None:
k8s_metadata[CLUSTER_NAME_KEY] = gcp_cluster

for attribute_key, attribute_env in _K8S_ENV_ATTRIBUTES.items():
attribute_value = os.environ.get(attribute_env)
if attribute_value is not None:
k8s_metadata[attribute_key] = attribute_value

return k8s_metadata
31 changes: 4 additions & 27 deletions opencensus/common/monitored_resource/monitored_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import os

from opencensus.common import resource
from opencensus.common.monitored_resource import aws_identity_doc_utils
from opencensus.common.monitored_resource import gcp_metadata_config
from opencensus.common.monitored_resource import k8s_utils


# Supported environments (resource types)
_GCE_INSTANCE = "gce_instance"
_GKE_CONTAINER = "gke_container"
_K8S_CONTAINER = "k8s_container"
_AWS_EC2_INSTANCE = "aws_ec2_instance"

# Kubenertes environment variables
_KUBERNETES_SERVICE_HOST = 'KUBERNETES_SERVICE_HOST'


def is_gke_environment():
"""Whether the environment is a GKE container instance.

The KUBERNETES_SERVICE_HOST environment variable must be set.
"""
return _KUBERNETES_SERVICE_HOST in os.environ


def is_gce_environment():
"""Whether the environment is a virtual machine on GCE."""
Expand All @@ -52,22 +40,11 @@ def get_instance():
Returns a `Resource` configured for the current environment, or None if the
environment is unknown or unsupported.

Supported environments include:

1. 'gke_container'
- https://cloud.google.com/monitoring/api/resources#tag_gke_container
2. 'gce_instance'
- https://cloud.google.com/monitoring/api/resources#tag_gce_instance
3. 'aws_ec2_instance'
- https://cloud.google.com/monitoring/api/resources#tag_aws_ec2_instance

:rtype: :class:`opencensus.common.resource.Resource` or None
:return: A `Resource` configured for the current environment.
"""
if is_gke_environment():
return resource.Resource(
_GKE_CONTAINER,
gcp_metadata_config.GcpMetadataConfig().get_gke_metadata())
if k8s_utils.is_k8s_environment():
return resource.Resource(_K8S_CONTAINER, k8s_utils.get_k8s_metadata())
if is_gce_environment():
return resource.Resource(
_GCE_INSTANCE,
Expand Down
Loading