blob: 9556112e6b12cc253ae64d61434585b9d376f92c [file] [log] [blame] [edit]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package karte
import (
"math"
"strconv"
"time"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
"go.chromium.org/luci/common/errors"
kartepb "go.chromium.org/infra/cros/karte/api"
"go.chromium.org/infra/cros/recovery/logger/metrics"
)
// ConvertActionStatusToKarteActionStatus takes a metrics action status and converts it to a Karte action status.
func convertActionStatusToKarteActionStatus(status metrics.ActionStatus) kartepb.Action_Status {
// TODO(gregorynisbet): Add support for skipped actions to Karte.
switch status {
case metrics.ActionStatusSuccess:
return kartepb.Action_SUCCESS
case metrics.ActionStatusFail:
return kartepb.Action_FAIL
case metrics.ActionStatusSkip:
return kartepb.Action_SKIP
default:
return kartepb.Action_STATUS_UNSPECIFIED
}
}
// ConvertKarteActionStatusToActionStatus takes a Karte action status and converts it to a metrics action status.
func convertKarteActionStatusToActionStatus(status kartepb.Action_Status) metrics.ActionStatus {
switch status {
case kartepb.Action_SUCCESS:
return metrics.ActionStatusSuccess
case kartepb.Action_FAIL:
return metrics.ActionStatusFail
case kartepb.Action_SKIP:
return metrics.ActionStatusSkip
default:
return metrics.ActionStatusUnspecified
}
}
// convertAllowFailToKarteAllowFail converts an allow fail value to a Karte allow fail value.
func convertAllowFailToKarteAllowFail(allowFail metrics.AllowFail) kartepb.Action_AllowFail {
switch allowFail {
case metrics.YesAllowFail:
return kartepb.Action_ALLOW_FAIL
case metrics.NoAllowFail:
return kartepb.Action_NO_ALLOW_FAIL
default:
return kartepb.Action_ALLOW_FAIL_UNSPECIFIED
}
}
// convertKarteAllowFailToAllowFail converts a Karte allow fail value to a Boolean.
func convertKarteAllowFailToAllowFail(allowFail kartepb.Action_AllowFail) metrics.AllowFail {
switch allowFail {
case kartepb.Action_ALLOW_FAIL:
return metrics.YesAllowFail
case kartepb.Action_NO_ALLOW_FAIL:
return metrics.NoAllowFail
default:
return metrics.AllowFailUnspecified
}
}
func convertKarteActionTypeToActionType(actionType kartepb.Action_ActionType) metrics.ActionType {
switch actionType {
case kartepb.Action_ACTION_TYPE_UNSPECIFIED:
return metrics.ActionTypeUnspecified
case kartepb.Action_ACTION_TYPE_VERIFIER:
return metrics.ActionTypeVerifier
case kartepb.Action_ACTION_TYPE_CONDITION:
return metrics.ActionTypeCondition
case kartepb.Action_ACTION_TYPE_RECOVERY:
return metrics.ActionTypeRecovery
}
return metrics.ActionTypeUnspecified
}
func convertActionTypeToKarteActionType(actionType metrics.ActionType) kartepb.Action_ActionType {
switch actionType {
case metrics.ActionTypeUnspecified:
return kartepb.Action_ACTION_TYPE_UNSPECIFIED
case metrics.ActionTypeVerifier:
return kartepb.Action_ACTION_TYPE_VERIFIER
case metrics.ActionTypeCondition:
return kartepb.Action_ACTION_TYPE_CONDITION
case metrics.ActionTypeRecovery:
return kartepb.Action_ACTION_TYPE_RECOVERY
}
return kartepb.Action_ACTION_TYPE_UNSPECIFIED
}
// ConvertTimeToProtobufTimestamp takes a time and converts it to a pointer to a protobuf timestamp.
// This method sends the zero time to a nil pointer.
func convertTimeToProtobufTimestamp(t time.Time) *timestamppb.Timestamp {
if t.IsZero() {
return nil
}
return timestamppb.New(t)
}
// ConvertProtobufTimestampToTime takes a protobuf timestamp and converts it
// to a Go time.Time. We can't just use thing.AsTime() because the protobuf timestamp and time.Time do not agree on what the "zero time" is.
//
// This is the error message that we get if we instead use thing.AsTime().
//
// -  StartTime: s"0001-01-01 00:00:00 +0000 UTC",
// +  StartTime: s"1970-01-01 00:00:00 +0000 UTC",
// -  StopTime: s"0001-01-01 00:00:00 +0000 UTC",
// +  StopTime: s"1970-01-01 00:00:00 +0000 UTC",
func convertProtobufTimestampToTime(t *timestamppb.Timestamp) time.Time {
var zero time.Time
if t == nil {
return zero
}
return t.AsTime()
}
// ConvertActionToKarteAction takes an action and converts it to a Karte action.
func convertActionToKarteAction(action *metrics.Action) *kartepb.Action {
if action == nil {
return nil
}
return &kartepb.Action{
Name: action.Name,
Kind: action.ActionKind,
SwarmingTaskId: action.SwarmingTaskID,
BuildbucketId: action.BuildbucketID,
Model: action.Model,
Board: action.Board,
AssetTag: action.AssetTag,
StartTime: convertTimeToProtobufTimestamp(action.StartTime),
StopTime: convertTimeToProtobufTimestamp(action.StopTime),
Status: convertActionStatusToKarteActionStatus(action.Status),
FailReason: action.FailReason,
Hostname: action.Hostname,
RecoveredBy: action.RecoveredBy,
Restarts: action.Restarts,
AllowFail: convertAllowFailToKarteAllowFail(action.AllowFail),
PlanName: action.PlanName,
ActionType: convertActionTypeToKarteActionType(action.Type),
}
}
// ConvertKarteActionToAction takes a Karte action and converts it to an action.
func convertKarteActionToAction(action *kartepb.Action) *metrics.Action {
if action == nil {
return nil
}
return &metrics.Action{
Name: action.GetName(),
ActionKind: action.GetKind(),
SwarmingTaskID: action.GetSwarmingTaskId(),
BuildbucketID: action.GetBuildbucketId(),
Model: action.GetModel(),
Board: action.GetBoard(),
AssetTag: action.GetAssetTag(),
StartTime: convertProtobufTimestampToTime(action.GetStartTime()),
StopTime: convertProtobufTimestampToTime(action.GetStopTime()),
Status: convertKarteActionStatusToActionStatus(action.GetStatus()),
FailReason: action.GetFailReason(),
Hostname: action.GetHostname(),
RecoveredBy: action.GetRecoveredBy(),
Restarts: action.GetRestarts(),
AllowFail: convertKarteAllowFailToAllowFail(action.GetAllowFail()),
PlanName: action.GetPlanName(),
Type: convertKarteActionTypeToActionType(action.GetActionType()),
}
}
// makeKarteObservation takes an action name and an observation and creates a Karte observation.
func makeKarteObservation(actionName string, observation *metrics.Observation) (*kartepb.Observation, error) {
if actionName == "" {
return nil, errors.Reason(`action name is ""`).Err()
}
if observation == nil {
return nil, errors.Reason("observation is nil").Err()
}
out := &kartepb.Observation{
ActionName: actionName,
MetricKind: observation.MetricKind,
}
switch observation.ValueType {
case metrics.ValueTypeNumber:
f, err := strconv.ParseFloat(observation.Value, 64)
if err != nil {
f = math.NaN()
}
out.Value = &kartepb.Observation_ValueNumber{
ValueNumber: f,
}
default:
out.Value = &kartepb.Observation_ValueString{
ValueString: observation.Value,
}
}
return out, nil
}
// convertFailureToKarteFailure converts a metrics failure to a karte failure.
func convertFailureToKarteFailure(failure *metrics.Failure) *kartepb.Failure {
if failure == nil {
return nil
}
return &kartepb.Failure{
SwarmingTaskId: failure.SwarmingTaskID,
BuildbucketId: failure.BuildbucketID,
DeviceModel: failure.DeviceModel,
DeviceBoard: failure.DeviceBoard,
DeviceOsType: failure.DeviceOsType,
DeviceOsBuild: failure.DeviceOsBuild,
DeviceState: failure.DeviceState,
CreatedTime: convertTimeToProtobufTimestamp(failure.FailureTime),
FailureType: convertFailureTypeToKarteFailureType(failure.FailureType),
}
}
func convertFailureTypeToKarteFailureType(failureType metrics.FailureType) kartepb.Failure_FailureType {
switch failureType {
case metrics.FailureTypeUnspecified:
return kartepb.Failure_FAILURE_TYPE_UNSPECIFIED
case metrics.FailureTypeProvisioning:
return kartepb.Failure_FAILURE_TYPE_PROVISIONING
case metrics.FailureTypeRepair:
return kartepb.Failure_FAILURE_TYPE_REPAIR
}
return kartepb.Failure_FAILURE_TYPE_UNSPECIFIED
}