confgenerator/resourcedetector/gce_detector.go (207 lines of code) (raw):

// Copyright 2022 Google LLC // // 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. package resourcedetector import ( "fmt" "strings" "github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp" "github.com/prometheus/prometheus/util/strutil" "google.golang.org/genproto/googleapis/api/monitoredres" ) type gceAttribute int const ( project gceAttribute = iota zone network subnetwork publicIP privateIP instanceID instanceName tags machineType metadata label interfaceIPv4 defaultScopes ) const ( ManagedInstanceGroupNameLabel = `compute.googleapis.com/instance_group_manager/name` ManagedInstanceGroupZoneLabel = `compute.googleapis.com/instance_group_manager/zone` ManagedInstanceGroupRegionLabel = `compute.googleapis.com/instance_group_manager/region` ) func GetGCEResource() (Resource, error) { provider := NewGCEMetadataProvider() dt := GCEResourceBuilder{provider: provider} return dt.GetResource() } // The data provider interface for GCE environment // Implementation of this provider can use either the metadata server on VM, // or the cloud API type gceDataProvider interface { getProject() (string, error) getZone() (string, error) getNetwork() (string, error) getSubnetwork() (string, error) getPublicIP() (string, error) getPrivateIP() (string, error) getInstanceID() (string, error) getInstanceName() (string, error) getTags() (string, error) getMachineType() (string, error) getDefaultScopes() ([]string, error) getLabels() (map[string]string, error) getMetadata() (map[string]string, error) getInterfaceIPv4s() (map[string]string, error) getMIG() (gcp.ManagedInstanceGroup, error) } // List of single-valued attributes (non-nested) var singleAttributeSpec = map[gceAttribute]func(gceDataProvider) (string, error){ project: gceDataProvider.getProject, zone: gceDataProvider.getZone, network: gceDataProvider.getNetwork, subnetwork: gceDataProvider.getSubnetwork, publicIP: gceDataProvider.getPublicIP, privateIP: gceDataProvider.getPrivateIP, instanceID: gceDataProvider.getInstanceID, instanceName: gceDataProvider.getInstanceName, tags: gceDataProvider.getTags, machineType: gceDataProvider.getMachineType, } // List of multi-valued attributes (non-nested) var multiAttributeSpec = map[gceAttribute]func(gceDataProvider) ([]string, error){ defaultScopes: gceDataProvider.getDefaultScopes, } // List of nested attributes var nestedAttributeSpec = map[gceAttribute]func(gceDataProvider) (map[string]string, error){ metadata: gceDataProvider.getMetadata, interfaceIPv4: gceDataProvider.getInterfaceIPv4s, label: gceDataProvider.getLabels, } // GCEResource implements the Resource interface and provide attributes of the VM when on GCE type GCEResource struct { Project string Zone string Network string Subnetwork string PublicIP string PrivateIP string InstanceID string InstanceName string Tags string MachineType string DefaultScopes []string Metadata map[string]string Label map[string]string InterfaceIPv4 map[string]string ManagedInstanceGroup gcp.ManagedInstanceGroup } func (r GCEResource) ProjectName() string { return r.Project } func (r GCEResource) OTelResourceAttributes() map[string]string { return map[string]string{ "cloud.platform": "gcp_compute_engine", "cloud.project": r.Project, "cloud.availability_zone": r.Zone, "cloud.region": r.Zone, "host.id": r.InstanceID, } } func (r GCEResource) MonitoredResource() *monitoredres.MonitoredResource { return &monitoredres.MonitoredResource{ Type: "gce_instance", Labels: map[string]string{ "instance_id": r.InstanceID, "zone": r.Zone, }, } } func (r GCEResource) PrometheusStyleMetadata() map[string]string { metaLabels := map[string]string{ "__meta_gce_instance_id": r.InstanceID, "__meta_gce_instance_name": r.InstanceName, "__meta_gce_project": r.Project, "__meta_gce_zone": r.Zone, "__meta_gce_network": r.Network, // TODO(b/b/246995894): Add support for subnetwork label. // "__meta_gce_subnetwork": r.Subnetwork, "__meta_gce_public_ip": r.PublicIP, "__meta_gce_private_ip": r.PrivateIP, "__meta_gce_tags": r.Tags, "__meta_gce_machine_type": r.MachineType, } prefix := "__meta_gce_" for k, v := range r.Metadata { sanitizedKey := "metadata_" + strutil.SanitizeLabelName(k) // Once https://github.com/open-telemetry/opentelemetry-collector/issues/9204 // is fixed, this will no longer be needed. sanitizedVal := strings.ReplaceAll(v, "${", "_{") sanitizedVal = strings.ReplaceAll(sanitizedVal, "$", "$$") metaLabels[prefix+sanitizedKey] = sanitizedVal } // Labels are not available using the GCE metadata API. // TODO(b/246995462): Add support for labels. // // for k, v := range r.Label { // metaLabels[prefix+"label_"+k] = v // } for k, v := range r.InterfaceIPv4 { sanitizedKey := "interface_ipv4_nic" + strutil.SanitizeLabelName(k) metaLabels[prefix+sanitizedKey] = v } // Set the location, namespace and cluster labels. metaLabels["location"] = r.Zone metaLabels["namespace"] = fmt.Sprintf("%s/%s", r.InstanceID, r.InstanceName) metaLabels["cluster"] = "__gce__" // Set some curated labels. metaLabels["instance_name"] = r.InstanceName metaLabels["machine_type"] = r.MachineType return metaLabels } func (r GCEResource) ExtraLogLabels() map[string]string { l := make(map[string]string) if r.ManagedInstanceGroup.Name != "" { l[ManagedInstanceGroupNameLabel] = r.ManagedInstanceGroup.Name } switch r.ManagedInstanceGroup.Type { case gcp.Zone: l[ManagedInstanceGroupZoneLabel] = r.ManagedInstanceGroup.Location case gcp.Region: l[ManagedInstanceGroupRegionLabel] = r.ManagedInstanceGroup.Location } return l } type GCEResourceBuilderInterface interface { GetResource() (Resource, error) } type GCEResourceBuilder struct { provider gceDataProvider } // Return a resource instance with all the attributes // based on the single and nested attributes spec func (gd *GCEResourceBuilder) GetResource() (Resource, error) { singleAttributes := map[gceAttribute]string{} for attrName, attrGetter := range singleAttributeSpec { attr, err := attrGetter(gd.provider) if err != nil { return nil, err } singleAttributes[attrName] = attr } multiAttributes := map[gceAttribute][]string{} for attrName, attrGetter := range multiAttributeSpec { attr, err := attrGetter(gd.provider) if err != nil { return nil, err } multiAttributes[attrName] = attr } nestedAttributes := map[gceAttribute]map[string]string{} for attrName, attrGetter := range nestedAttributeSpec { attr, err := attrGetter(gd.provider) if err != nil { return nil, err } nestedAttributes[attrName] = attr } mig, err := gd.provider.getMIG() if err != nil { return nil, err } res := GCEResource{ Project: singleAttributes[project], Zone: singleAttributes[zone], Network: singleAttributes[network], Subnetwork: singleAttributes[subnetwork], PublicIP: singleAttributes[publicIP], PrivateIP: singleAttributes[privateIP], InstanceID: singleAttributes[instanceID], InstanceName: singleAttributes[instanceName], Tags: singleAttributes[tags], MachineType: singleAttributes[machineType], DefaultScopes: multiAttributes[defaultScopes], Metadata: nestedAttributes[metadata], Label: nestedAttributes[label], InterfaceIPv4: nestedAttributes[interfaceIPv4], ManagedInstanceGroup: mig, } return res, nil }