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

Add option to fetch metadata and add it to resources #12

Merged
merged 3 commits into from
Dec 15, 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
60 changes: 51 additions & 9 deletions pkg/gcp/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"cloud.google.com/go/logging"
texporter "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
"github.com/otto-de/sherlock-microservice/pkg/gke"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"google.golang.org/genproto/googleapis/api/monitoredres"
)
Expand Down Expand Up @@ -38,10 +39,11 @@ func NewLoggingClient(project string) (*logging.Client, error) {
}

type discoveryOption struct {
clusterName string
namespace string
pod string
containerName string
clusterName string
containerName string
namespace string
pod string
gkeAutoDiscoverMetaData bool
}

func WithKubernetes(clusterName, namespace, pod, containerName string) discoveryOption {
Expand All @@ -53,9 +55,34 @@ func WithKubernetes(clusterName, namespace, pod, containerName string) discovery
}
}

func WithGKEAutoDiscoverMetaData() discoveryOption {
/*
This option will try to auto discover available metadata from the Google Cloud metadata service and environment variables.
The metadata will be used to create the monitored resource and trace resource.
For the container name it will use the environment variable CONTAINER_NAME.
For the pod name it will use the environment variable POD_NAME.
For the namespace it will use the environment variable NAMESPACE.
These variables cant be fetched from the metadata service.
Set the environment variables in the deployment manifest.

- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: CONTAINER_NAME
value: test-container
*/
return discoveryOption{
gkeAutoDiscoverMetaData: true,
}
}

// DiscoverServices builds clients for all Services that we use.
func DiscoverServices(project, serviceName string, tracerProviderOptions []sdktrace.TracerProviderOption, opts ...discoveryOption) (*Services, error) {

loggingClient, err := NewLoggingClient(project)
if err != nil {
return nil, err
Expand Down Expand Up @@ -83,19 +110,34 @@ func DiscoverServices(project, serviceName string, tracerProviderOptions []sdktr
panic(err)
}

tp := sdktrace.NewTracerProvider(append(tracerProviderOptions, sdktrace.WithBatcher(exporter))...)

s := &Services{
Logging: loggingClient,
ErrorReporting: errorClient,
TracerProvider: tp,
}

var traceResource *resource.Resource
for _, opt := range opts {
if opt.pod != "" {
if opt.gkeAutoDiscoverMetaData {
metadata, err := gke.GetMetaData()
if err != nil {
logger.Log(logging.Entry{
Severity: logging.Info,
Payload: fmt.Sprintf("Error getting MetaData: %s", err),
})
continue
}

s.MonitoredResource = gke.MonitoredResourceFromMetaData(metadata)
traceResource = gke.TraceResourceFromMetaData(serviceName, metadata)
} else if opt.pod != "" {
s.MonitoredResource = gke.MonitoredResource(s.Logging, project, opt.clusterName, opt.namespace, opt.pod, opt.containerName)
}
}
if traceResource != nil {
tracerProviderOptions = append(tracerProviderOptions, sdktrace.WithResource(traceResource))
}

s.TracerProvider = sdktrace.NewTracerProvider(append(tracerProviderOptions, sdktrace.WithBatcher(exporter))...)

return s, nil
}
Expand Down
85 changes: 85 additions & 0 deletions pkg/gke/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package gke

import (
"encoding/json"
"os"
"strings"

"cloud.google.com/go/compute/metadata"
)

type GKEMetaData struct {
ClusterLocation string
ClusterName string
ContainerName string
Namespace string
InstanceName string
InstanceID int64
NumericProjectID int64
PodName string
ProjectID string
Zone string
}

type instance struct {
Instance struct {
Attributes struct {
ClusterLocation string `json:"cluster-location"`
ClusterName string `json:"cluster-name"`
ClusterUID string `json:"cluster-uid"`
} `json:"attributes"`
Hostname string `json:"hostname"`
ID int64 `json:"id"`
Name string `json:"name"`
NetworkInterfaces map[string]struct {
Ipv6s string `json:"ipv6s"`
} `json:"networkInterfaces"`
ServiceAccounts map[string]struct {
Aliases []string `json:"aliases"`
Email string `json:"email"`
Scopes []string `json:"scopes"`
} `json:"serviceAccounts"`
Zone string `json:"zone"`
} `json:"instance"`
Project struct {
NumericProjectID int64 `json:"numericProjectId"`
ProjectID string `json:"projectId"`
} `json:"project"`
}

func parseInstanceJSON(jsonData []byte) (*instance, error) {
var instance instance
err := json.Unmarshal(jsonData, &instance)
if err != nil {
return nil, err
}
return &instance, nil
}

func GetMetaData() (*GKEMetaData, error) {
s, err := metadata.Get("/?recursive=true")
if err != nil {
return &GKEMetaData{}, err
}

i, err := parseInstanceJSON([]byte(s))
if err != nil {
return &GKEMetaData{}, err
}

zp := strings.Split(i.Instance.Zone, "/")
zoneName := zp[len(zp)-1]

return &GKEMetaData{
ClusterLocation: i.Instance.Attributes.ClusterLocation,
ClusterName: i.Instance.Attributes.ClusterName,
ContainerName: os.Getenv("CONTAINER_NAME"),
Namespace: os.Getenv("POD_NAMESPACE"),
InstanceName: i.Instance.Name,
InstanceID: i.Instance.ID,
NumericProjectID: i.Project.NumericProjectID,
PodName: os.Getenv("POD_NAME"),
ProjectID: i.Project.ProjectID,
Zone: zoneName,
}, nil
}
35 changes: 35 additions & 0 deletions pkg/gke/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import (

"cloud.google.com/go/compute/metadata"
"cloud.google.com/go/logging"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
"google.golang.org/genproto/googleapis/api/monitoredres"
)

Expand Down Expand Up @@ -53,3 +56,35 @@ func MonitoredResource(l *logging.Client, project, clusterName, namespace, pod,

return res
}

func MonitoredResourceFromMetaData(metadata *GKEMetaData) *monitoredres.MonitoredResource {
return &monitoredres.MonitoredResource{
Type: "gke_container",
Labels: map[string]string{
"project_id": metadata.ProjectID,
"cluster_name": metadata.ClusterName,
"namespace_id": metadata.Namespace,
"instance_id": string(metadata.InstanceID),
"pod_id": metadata.PodName,
"container_name": metadata.ContainerName,
"zone": metadata.Zone,
},
}
}

func TraceResourceFromMetaData(serviceName string, metadata *GKEMetaData) *resource.Resource {
return resource.NewWithAttributes(
"",
semconv.CloudProviderGCP,
semconv.CloudPlatformGCPKubernetesEngine,
semconv.ServiceNameKey.String(serviceName),
attribute.String("g.co/r/k8s_container/project_id", metadata.ProjectID),
attribute.String("g.co/r/k8s_container/cluster_name", metadata.ClusterName),
attribute.String("g.co/r/k8s_container/namespace", metadata.Namespace),
attribute.String("g.co/r/k8s_container/location", metadata.ClusterLocation),
attribute.String("g.co/r/k8s_container/node_name", metadata.InstanceName),
attribute.String("g.co/r/k8s_container/pod_name", metadata.PodName),
attribute.String("g.co/r/k8s_container/container_name", metadata.ContainerName),
attribute.String("g.co/r/k8s_container/zone", metadata.Zone),
)
}