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,264 @@
package handlers
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"otaupdate/services"
"sort"
"strconv"
"strings"
"github.com/fiskerinc/cloud-services/pkg/cache"
"github.com/fiskerinc/cloud-services/pkg/common"
"github.com/fiskerinc/cloud-services/pkg/logger"
"github.com/fiskerinc/cloud-services/pkg/utils"
"github.com/fiskerinc/cloud-services/pkg/utils/envtool"
"github.com/fiskerinc/cloud-services/pkg/validator"
"github.com/julienschmidt/httprouter"
"github.com/fiskerinc/cloud-services/pkg/loggerdataresp"
)
type ManifestMigrateVersion struct {
Version int
}
var ERR_MANIFEST_MIGRATE_BAD_VERSION = errors.New("received version not recognized")
// TargetURLS is multiple as stage pushes to Prod and Euro Prod
var apiToken string = envtool.GetEnv("MANIFEST_MIGRATE_TOKEN", "MISSING API TOKEN")
var targetURLS []string = strings.Split(envtool.GetEnv("MANIFEST_MIGRATE_URLS", "MISSING_TARGET_URL"), ",")
// So we are going to take the information from our lower env and push it into a higher one.
// Need to decide how we want to get the information from the lower level to this higher level
// HandleUpdateManifestMigrate godoc
// @Summary Push update manifest from this environment to a higher one
// @Accept json
// @Produce json
// @Param Authorization header string false "Bearer <ID token>"
// @Param Api-Key header string false "<API token>"
// @Param manifest_id path string true "Manifest ID"
// @Success 200 {object} common.JSONMessage
// @Failure 400 {object} common.JSONError "Bad Request"
// @Failure 401 {object} common.JSONError "Unauthorized"
// @Failure 503 {object} common.JSONError "Service unavailable"
// @Router /manifestmigrate/{manifest_id} [post]
func HandleUpdateManifestMigrate(w http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context())
manifestIdString := params.ByName("manifest_id")
// Gather the right information
manifestId, err := strconv.Atoi(manifestIdString)
if loggerdataresp.BadDataErrorResp(w, err, http.StatusBadRequest) {
return
}
var manifest common.UpdateManifest
manifest.ID = int64(manifestId)
err = validator.ValidateIDField(manifest.ID)
if loggerdataresp.BadDataErrorResp(w, err, http.StatusBadRequest) {
return
}
um := services.GetDB().GetUpdateManifests()
err = um.Load(&manifest)
if loggerdataresp.BadDataErrorResp(w, err, http.StatusServiceUnavailable, loggerdataresp.PostgresNoRowsErrorCheck) {
return
}
manifest.SUMS = ""
manifest.Env = ""
manifest.MigratePrepareHardwareVersion()
fileKeys, err := getFileKeys(manifest)
if loggerdataresp.BadDataErrorResp(w, err, http.StatusServiceUnavailable, loggerdataresp.PostgresNoRowsErrorCheck) {
return
}
sendStruct := ManifestMigrateBody{}
sendStruct.MigratedManifest = manifest
sendStruct.FileKeys = fileKeys
// Send message to the other API's
sendBody, err := PrepareMigrateBodyForSending(&sendStruct)
if loggerdataresp.BadDataErrorResp(w, err, http.StatusBadRequest) {
return
}
for index, targetURL := range targetURLS {
turl, err := url.JoinPath(targetURL, "manifestmigrate")
if loggerdataresp.BadDataErrorResp(w, err, http.StatusBadRequest) {
return
}
req, err := http.NewRequest("POST", turl, bytes.NewBuffer(sendBody))
if loggerdataresp.BadDataErrorResp(w, err, http.StatusBadRequest) {
return
}
req.Header.Set("Content-Type", "application/json")
req.Header.Add("Api-Key", apiToken)
resp, err := http.DefaultClient.Do(req)
if loggerdataresp.BadDataErrorResp(w, err, http.StatusBadRequest) {
deleteBecauseFailedMigrate(targetURLS[:index], int(manifest.ID))
return
}
defer resp.Body.Close()
b, _ := io.ReadAll(resp.Body)
// Forgot to check the status code
if resp.StatusCode >= 300 {
logger.Error().Msgf("Failed to send manifest migrate: %s \n Status: %s\n Resp Body: %s\n", string(sendBody), resp.Status, string(b))
var response common.JSONError
err := json.Unmarshal(b, &response)
if loggerdataresp.BadDataErrorResp(w, err, http.StatusBadRequest) {
return
}
utils.RespJSON(w, http.StatusBadRequest, response)
return
}
}
utils.RespJSON(w, http.StatusOK, common.JSONMessage{
Message: "Migration Sent",
})
}
// If we fail to upload a manifest to one of the urls, delete from the other environments we are uploading to
func deleteBecauseFailedMigrate(urls []string, manifestID int) {
for _, targetURL := range urls {
turl, err := url.JoinPath(targetURL, "manifestraw")
if err != nil {
logger.Err(err).Msgf("Failed to delete manifest %d from %s after failing an upload", manifestID, targetURL)
continue
}
turl += fmt.Sprintf("?id=%d", manifestID)
req, err := http.NewRequest("DELETE", turl, nil)
if err != nil {
logger.Err(err).Msgf("Failed to delete manifest %d from %s after failing an upload", manifestID, targetURL)
continue
}
req.Header.Set("Content-Type", "application/json")
req.Header.Add("Api-Key", apiToken)
resp, err := http.DefaultClient.Do(req)
if err != nil {
logger.Err(err).Msgf("Failed to delete manifest %d from %s after failing an upload %s", manifestID, targetURL, resp.Status)
continue
}
defer resp.Body.Close()
if resp.StatusCode >= 300 {
if err != nil {
logger.Err(err).Msgf("Failed to delete manifest %d from %s after failing an upload %s", manifestID, targetURL, resp.Status)
continue
}
}
}
}
func getFileKeys(manifest common.UpdateManifest) ([]common.FileKeyResponse, error) {
conn := services.RedisClientPool().GetFromPool()
dbFK := services.GetDB().GetFileKeys()
defer conn.Close()
fileIDs := make([]string, 0)
for _, ecu := range manifest.ECUs {
for _, file := range ecu.Files {
fileIDs = append(fileIDs, file.FileID)
}
}
return cache.RetrieveFileEncryptionParams(conn, dbFK, fileIDs)
}
// In case we need a completely different struct, we will just return the bytes
func PrepareMigrateBodyForSending(manifestMigrate *ManifestMigrateBody) (jsonBody []byte, err error) {
v, err := getVersionToSend()
if err != nil {
return
}
err = transformMigrateBody(manifestMigrate, v)
if err != nil {
return
}
return json.Marshal(manifestMigrate)
}
// Get the version that the env we are pushing to is going to run, then we can transform is needed or not send at all
// If we do not get a version, it defaults to 0
func getVersionToSend() (version int, err error) {
version = -1
if len(targetURLS) == 0 {
return version, errors.New("missing target url")
}
targetURL := targetURLS[0]
// The eu prod and normal prod should always be the same version, so just going to act like they are
turl, err := url.JoinPath(targetURL, "manifestmigrate-version")
if err != nil {
return
}
req, err := http.NewRequest("GET", turl, nil)
if err != nil {
return
}
req.Header.Add("Api-Key", apiToken)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return
}
defer resp.Body.Close()
// If they don't have this route, we can assume they are on version 0
if resp.StatusCode >= 300 {
logger.Warn().Msgf("manifest migrate version returned status code %s", resp.Status)
version = 0
return
}
var mmv ManifestMigrateVersion
err = json.NewDecoder(resp.Body).Decode(&mmv)
if err != nil {
return
}
version = mmv.Version
return
}
const MIGRATION_VERSION = 1
// If we do not get a version, it defaults to 0
// If we cannot send the manifest due to some major change, then return an error
func transformMigrateBody(manifestMigrate *ManifestMigrateBody, version int) (err error) {
// We have to go from our version down to their version
// EX we are on version 5, they are on version 3
// So do the conversion of 5 -> 4, then 4 -> 3
// For each version that is added on, add to this switch statement
// This switch statement is more of a way to organize the flow, can probably be improved
switch MIGRATION_VERSION {
case 1: // The conversion needed to go from VERSION 1 to VERSION 0
if version == 1 { // If we are at the desired version, then we can break
break
}
swapECUInstallPriority(manifestMigrate)
fallthrough
case 0:
// THis is just an example row to show the fallthrough from case of 1 -> 0
if version == 0 {
break
}
default:
err = ERR_MANIFEST_MIGRATE_BAD_VERSION
}
return
}
func swapECUInstallPriority(manifestMigrate *ManifestMigrateBody) {
ecuList := manifestMigrate.MigratedManifest.ECUs
// Order the array so we can begin to swap easily
sort.Slice(ecuList, func(i, j int) bool { return ecuList[i].InstallPriority < ecuList[j].InstallPriority })
rightIndex := len(ecuList) - 1
for leftIndex := 0; leftIndex < rightIndex; leftIndex++ {
ecuList[leftIndex].InstallPriority, ecuList[rightIndex].InstallPriority = ecuList[rightIndex].InstallPriority, ecuList[leftIndex].InstallPriority
rightIndex--
}
}