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