google_guest_agent/telemetry/telemetry.go (103 lines of code) (raw):
// Copyright 2023 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
// https://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 telemetry
import (
"context"
"encoding/base64"
"runtime"
"time"
"github.com/GoogleCloudPlatform/guest-agent/metadata"
"github.com/GoogleCloudPlatform/guest-logging-go/logger"
"google.golang.org/protobuf/proto"
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/osinfo"
tpb "github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/telemetry/proto"
)
var (
telemetryJobID = "telemetryJobID"
telemetryInterval = 24 * time.Hour
)
// Data is telemetry data on the current agent and OS.
type Data struct {
// Name of the agent.
AgentName string
// Version of the Agent.
AgentVersion string
// Architecture of the Agent.
AgentArch string
// OS name.
OS string
// The name the OS uses to fully describe itself.
LongName string
// OS name in short form (aka distro name).
ShortName string
// Version of the OS.
Version string
// Kernel Release.
KernelRelease string
// Kernel Version.
KernelVersion string
}
func formatGuestAgent(d Data) string {
data, err := proto.Marshal(&tpb.AgentInfo{
Name: &d.AgentName,
Version: &d.AgentVersion,
Architecture: &d.AgentArch,
})
if err != nil {
logger.Warningf("Error marshalling AgentInfo: %v", err)
}
return base64.StdEncoding.EncodeToString(data)
}
func formatGuestOS(d Data) string {
data, err := proto.Marshal(&tpb.OSInfo{
OsType: &d.OS,
LongName: &d.LongName,
ShortName: &d.ShortName,
Version: &d.Version,
KernelVersion: &d.KernelVersion,
KernelRelease: &d.KernelRelease,
})
if err != nil {
logger.Warningf("Error marshalling AgentInfo: %v", err)
}
return base64.StdEncoding.EncodeToString(data)
}
// Record records telemetry data.
func Record(ctx context.Context, client metadata.MDSClientInterface, d Data) error {
headers := map[string]string{
"X-Google-Guest-Agent": formatGuestAgent(d),
"X-Google-Guest-OS": formatGuestOS(d),
}
// This is the simplest metadata call we can make, and we dont care about any return value,
// all we need to do is make some call with the telemetry headers.
_, err := client.GetKey(ctx, "", headers)
return err
}
// Job implements job scheduler interface for recording telemetry.
type Job struct {
client metadata.MDSClientInterface
programName string
agentVersion string
}
// New initializes a new TelemetryJob.
func New(client metadata.MDSClientInterface, programName, agentVersion string) *Job {
return &Job{
client: client,
programName: programName,
agentVersion: agentVersion,
}
}
// ID returns the ID for this job.
func (j *Job) ID() string {
return telemetryJobID
}
// Run records telemetry data.
func (j *Job) Run(ctx context.Context) (bool, error) {
osInfo := osinfo.Get()
d := Data{
AgentName: j.programName,
AgentVersion: j.agentVersion,
AgentArch: runtime.GOARCH,
OS: runtime.GOOS,
LongName: osInfo.PrettyName,
ShortName: osInfo.OS,
Version: osInfo.VersionID,
KernelRelease: osInfo.KernelRelease,
KernelVersion: osInfo.KernelVersion,
}
if err := Record(ctx, j.client, d); err != nil {
// Log this here in Debug mode as telemetry is best effort.
logger.Debugf("Error recording telemetry: %v", err)
}
return j.ShouldEnable(ctx), nil
}
// Interval returns the interval at which job is executed.
func (j *Job) Interval() (time.Duration, bool) {
return telemetryInterval, true
}
// ShouldEnable returns true as long as DisableTelemetry is not set in metadata.
func (j *Job) ShouldEnable(ctx context.Context) bool {
md, err := j.client.Get(ctx)
if err != nil {
return false
}
return !md.Instance.Attributes.DisableTelemetry && !md.Project.Attributes.DisableTelemetry
}