Add depot, attendant, jetfire, optimus, ota services with kustomize overlays

This commit is contained in:
Chris Rai
2026-01-31 15:35:07 -05:00
parent a0ec642ca1
commit 9a5cb2f547
404 changed files with 38817 additions and 16 deletions

View File

@@ -0,0 +1,403 @@
package controllers
import (
"encoding/json"
"fmt"
"github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc"
"github.com/fiskerinc/cloud-services/services/attendant/services"
"github.com/fiskerinc/cloud-services/pkg/carcommand"
"github.com/fiskerinc/cloud-services/pkg/common"
"github.com/fiskerinc/cloud-services/pkg/common/carupdatestatus"
"github.com/fiskerinc/cloud-services/pkg/common/manifestfingerprintparams"
"github.com/fiskerinc/cloud-services/pkg/grpc/sms"
"github.com/fiskerinc/cloud-services/pkg/hwversion"
"github.com/fiskerinc/cloud-services/pkg/kafka"
"github.com/fiskerinc/cloud-services/pkg/logger"
"github.com/fiskerinc/cloud-services/pkg/manifestsender"
"github.com/fiskerinc/cloud-services/pkg/redis"
"github.com/fiskerinc/cloud-services/pkg/tmobile"
uhelpers "github.com/fiskerinc/cloud-services/pkg/usecase_helpers"
"github.com/fiskerinc/cloud-services/pkg/utils/envtool"
"github.com/fiskerinc/cloud-services/pkg/utils/randomvalues"
"github.com/fiskerinc/cloud-services/pkg/utils/whereami"
"github.com/fiskerinc/cloud-services/pkg/validator"
vconfig "github.com/fiskerinc/cloud-services/pkg/vehicleconfig"
"google.golang.org/protobuf/proto"
"sync"
"time"
"github.com/pkg/errors"
)
// 0100084000000101012200010101010001010101000000000000000000ff7eff7f000101010101000101010100010001010101000101010100000000000000000000000000010101000100000100010101000201010101000101020101000101010200010101010101010101000101000100010001010101010101010101010000000000000100010101020101010101010000000000000000ffffff00010102010102020001010000000000000000000000000000000000000000000000000000000000000000000100202310010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f6
var defaultVOD string = envtool.GetEnv("DEFAULT_VOD", "00FF111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111114A")
var fileKeyBypass map[int64]string
var FileKeyBypassManifest_US map[int64]string = map[int64]string{
1189: "fe4f19fbc940ed58",
1380: "6a44fd71c940716d",
}
var FileKeyBypassManifest_EU map[int64]string = map[int64]string{
893: "fe4f19fbc940ed58",
1002: "6a44fd71c940716d",
1012: "d70e1d32c940a221",
}
func init() {
if whereami.Environment == whereami.PRODUCTION_EU {
fileKeyBypass = FileKeyBypassManifest_EU
} else {
fileKeyBypass = FileKeyBypassManifest_US
}
}
func NewManifestSender(
r redis.ClientPoolInterface,
db *services.DB,
conf vconfig.ConfigServiceInterface,
device common.Device,
ka *services.KeepAwake,
seed int64, //This seed is used only for testing
) *ManifestSender {
randomGenerator := randomvalues.NewNonCryptoGenerator("ABCDEFGHIJKLMNOPQRSTUVWXYZ", seed)
return &ManifestSender{
Redis: r,
db: db,
conf: conf,
Device: device,
sms: services.GetSMSClient(),
ka: ka,
gen: randomGenerator,
}
}
type ManifestSender struct {
Redis redis.ClientPoolInterface
db *services.DB
conf vconfig.ConfigServiceInterface
sms sms.SMSServiceClient
Device common.Device
ka *services.KeepAwake
gen randomvalues.NonCryptoGenerator
}
func (m *ManifestSender) Process(id string, data []byte) error {
// id string parameter is unused except in the following log
// the data might not be used either, just for its single carUpdateID
u, err := m.parseRequest(data)
if err != nil {
logger.Err(err).Msgf("Manifest sender, unable to process update %s", id)
return err
}
carUpdate := common.CarUpdate{ID: u.CarUpdateID}
err = m.loadCarUpdate(&carUpdate)
if err != nil {
return err
}
logger.Info().Msgf("ManifestSender car_update_id %d", carUpdate.ID)
carUpdate.UpdateManifest.CarUpdateID = carUpdate.ID
helper := uhelpers.NewECUKeys(m.db.GetECCKeys())
err = helper.AddECUECCKeys(carUpdate.UpdateManifest)
if err != nil {
logger.Err(err).Msgf("Unable to decrypt keys for %d", carUpdate.ID)
return err //comment out for local debugging
}
// If we fail to deliver the sms, then we don't believe the car has awoken
// then set the manifest as failed
err = m.send(carUpdate.VIN, carUpdate.UpdateManifest)
if err != nil {
update := NewCarUpdateProgress(m.Redis, m.ka, m.db, common.TRex)
err = update.ProcessStatus(carUpdate.VIN, common.CarUpdateProgress{
CarUpdateID: u.CarUpdateID,
Status: carupdatestatus.ManifestCanceled,
Info: " Underlying error: " + err.Error(),
})
}
return err
}
func (m *ManifestSender) destination(manifest *common.UpdateManifest) string {
if manifest.HasSelfDownload() {
return "ICC"
} else {
return "ICC/TBOX"
}
}
func (m *ManifestSender) parseRequest(data []byte) (*common.UpdateManifest, error) {
var req common.UpdateManifest
err := json.Unmarshal(data, &req)
if err != nil {
return nil, errors.WithStack(err)
}
err = validator.ValidateIDField(req.CarUpdateID)
if err != nil {
return &req, errors.WithStack(err)
}
return &req, nil
}
func (m *ManifestSender) loadCarUpdate(cu *common.CarUpdate) error {
err := m.db.GetCarUpdates().Load(cu)
if err != nil {
return errors.WithStack(err)
}
if cu.UpdateManifest == nil {
return nil
}
return nil
}
func (m *ManifestSender) queueManifest(vin string, manifest *common.UpdateManifest) error {
loggedManifest := manifest.Copy()
loggedManifest.RemoveECCKeysFromECUs()
logger.At(logger.Info(), common.HMI.Key(vin), "update").Interface("manifest.json", loggedManifest).Msgf("HMI ManifestSender sent %v %s %d", common.HMI, vin, manifest.CarUpdateID)
client := m.Redis.GetFromPool()
defer client.Close()
err := client.SafeQueueMessage(common.HMI.Key(vin), common.Message{
Handler: "update_manifest",
Data: manifest,
})
if err != nil {
logger.Err(err).Msgf("failed to queue manifest in redis vin %s ", vin)
}
return err
}
func (m *ManifestSender) send(vin string, manifest *common.UpdateManifest) error {
updateManifestID := manifest.ID
manifest.TransformECUNames()
err := hwversion.SetHWVersion(manifest, vin, m.db.GetCars())
if err != nil {
logger.Err(err).Msgf("manifest sender failed at SetHwVersion for vin %s", vin)
return err //comment out for local debugging
}
manifest.SortECUs()
manifest.RemoveOriginalS19HexFiles()
manifest.RemoveOriginalS19HexFilesRollbacks()
manifest.FilterCompatibleECUs(vin)
err = uhelpers.PopulateECUsCurrentVersion(m.db.GetCars(), vin, manifest.ECUs)
if err != nil {
logger.Err(err).Msgf("manifest sender failed at PupulateECUsCurrentVersion for vin %s", vin)
return err
}
fpparams := manifestfingerprintparams.GetFPParams()
manifest.GenerateFingerprint(fpparams.CurTime(), fpparams.ManifestSerial())
hmiManifest := manifest.Copy()
cds, err := m.addVOD(hmiManifest, vin)
if err != nil {
logger.Err(err).Msgf("manifest sender failed at adding the vod for vin %s", vin)
return err // comment out for local debugging
}
err = validator.ValidateField(hmiManifest.SUMS, "sums_version")
if err == nil {
err = hmiManifest.AddSUMSToVOD()
if err != nil {
logger.Err(err).Msgf("manifest sender failed at AddSUMSToVOD for vin %s", vin)
return err
}
} else {
logger.Err(err).Msgf("manifest sender failed at Validation for vin %s", vin)
return err
}
hmiManifest.Scrub(common.HMI)
client := m.Redis.GetFromPool()
defer client.Close()
// If HasSelfDownload, we are not yet ready to send to t box, so we wake the car if the update is forced
if manifest.HasSelfDownload() && m.sms != nil {
// The function that calls this one handles canceling the manifest
var msgID string
res, err := carcommand.QueueSMSWakeUp(vin, true, client, m.db.GetCars(), m.sms)
if err != nil || res == nil || !res.SentSuccessful {
logger.Err(err).Msgf("Attendant:manifest sender failed at sendSMSWakeUp for vin %s", vin)
msgID = m.gen.GetString(10)
defer m.selfKafkaCallback(msgID)
} else {
msgID = res.SmsMsgID
}
err = m.ka.SendFirstKeepAwakeMessage(vin)
if err != nil {
logger.Err(err).Msgf("failed to SendFirstKeepAwakeMessage on msgID %s", msgID)
}
m.queueManifest(vin, hmiManifest)
fileID, ok := fileKeyBypass[updateManifestID]
if ok {
logger.Info().Str("VIN", vin).Msg("Queueing SendFileKeys")
time.AfterFunc(time.Second*3, func() { sendFileKeys(vin, fileID) })
}
// Do not send manifest to TBOX, wait until ICC has finished downloading
return err
}
trex := manifestsender.NewTBOXManifestSender(client, m.conf, m.db, services.GetSMSClient(), cds)
defer trex.Close()
//smsID, err := trex.ProcessSoftwareUpdate(vin, manifest)
_, err = trex.ProcessSoftwareUpdate(vin, manifest, services.GetDB().GetCarConfigData())
logger.Debug().Msgf("Send HMI manifest to %s ", vin)
m.queueManifest(vin, hmiManifest)
// We managed to send an sms, so we want to continue with an sms delivered check
// if smsID != "" {
// err = m.setManifestCache(smsID, ManifestCachedPoint{
// VIN: vin,
// ManifestID: manifest.ID,
// Destination: m.destination(manifest),
// })
// if err != nil {
// return err
// }
// }
return err
}
// So this flow of continue will be activated at two different times, possible multiple times.
// 1) When a new car_update comes through and the .hasdownload is true, and then again when the manifest is being sent to the tbox
func (m *ManifestSender) ContinueTBOXSend(id string, data []byte) (err error) {
// After a text has been succesfully delivered, we can now continue the update
// Check the status and make sure that is is success, otherwise fail and set the update status as failed
var msgStatus tmobile.MessageStatus
err = json.Unmarshal(data, &msgStatus)
if err != nil {
return
}
return nil
}
func (m *ManifestSender) setManifestCache(msgID string, cache ManifestCachedPoint) (err error) {
client := m.Redis.GetFromPool()
defer client.Close()
return client.Set(msgIDToRedisKey(msgID), cache)
}
type ManifestCachedPoint struct {
VIN string
ManifestID int64
Destination string
}
func msgIDToRedisKey(msgID string) (redisKey string) {
return fmt.Sprintf("manifest_tbox_send_cache:%s", msgID)
}
func (m *ManifestSender) addVOD(manifest *common.UpdateManifest, vin string) (map[string]string, error) {
cds, err := uhelpers.GetCDS(m.conf, services.GetDB().GetCarConfigData(), vin)
if err != nil {
return nil, err
}
if vod, ok := cds["VOD"]; ok && vod != "" {
manifest.VOD = vod
} else {
manifest.VOD = defaultVOD
}
return cds, nil
}
// When car is a virtual trex, we are going to produce to the kafka message queue as if the sms service did it
func (m *ManifestSender) selfKafkaCallback(messageID string) {
producer, err := services.GetKafkaProducer()
if err != nil {
logger.Err(err).Send()
return
}
messageStatus := &kafka_grpc.GRPC_AttendantPayload_MessageStatus{
MessageStatus: &kafka_grpc.MessageStatus{
MessageId: messageID,
Status: kafka_grpc.EmumStatus_DELIVERED,
},
}
kafkaMSG := kafka_grpc.GRPC_AttendantPayload{
Handler: "sms_delivery_status_manifest",
Data: messageStatus,
}
binaryPayload, _ := proto.Marshal(&kafkaMSG)
err = producer.ProduceBinary(kafka.AttendantServiceGRPCKafka, "4:Service", binaryPayload, nil)
if err != nil {
err = errors.WithStack(err)
logger.Err(err).Msgf("failed to produce kafka message for sms id %s", messageID)
}
}
func (m *ManifestSender) Release() {
m.Redis = nil
m.db = nil
m.conf = nil
}
var fpParams FingerprintParamer
var fpParamsOnce sync.Once
func SetFPParams(fpp FingerprintParamer) {
fpParams = fpp
}
func GetFPParams() FingerprintParamer {
fpParamsOnce.Do(func() {
if fpParams == nil {
fpParams = &fingerprintParams{
serialNum: envtool.GetEnv("OTA_MANIFEST_SERIAL", "00000000000000000"),
}
}
})
return fpParams
}
type fingerprintParams struct {
serialNum string
}
func (p *fingerprintParams) ManifestSerial() string {
return p.serialNum
}
func (p *fingerprintParams) CurTime() time.Time {
return time.Now().UTC()
}
type FingerprintParamer interface {
ManifestSerial() string
CurTime() time.Time
}
// Mega hack mode
func sendFileKeys(vin string, fileID string) {
logger.Info().Str("VIN", vin).Msg("Sending SendFileKeys")
db := services.GetDB()
err := GetFileKeys(db, common.HMI, vin, []byte(`{"file_ids": ["`+fileID+`"]}`))
if err != nil {
logger.Err(err).Str("VIN", vin).Str("file ID", fileID).Msg("Failed to SendFileKeys")
}
}