Add depot, attendant, jetfire, optimus, ota services with kustomize overlays
This commit is contained in:
403
services/attendant/controllers/send_manifest.go
Normal file
403
services/attendant/controllers/send_manifest.go
Normal 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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user