-
Notifications
You must be signed in to change notification settings - Fork 1
/
log.go
118 lines (101 loc) · 3.46 KB
/
log.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package api
import (
"fmt"
"log"
"net/http"
"strconv"
"time"
"github.com/galleybytes/terraform-operator-api/pkg/common/models"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func (h APIHandler) AddTaskPod(c *gin.Context) {
now := time.Now().UTC()
token, err := taskJWT(c.Request.Header["Token"][0])
if err != nil {
c.JSON(http.StatusUnprocessableEntity, response(http.StatusUnprocessableEntity, err.Error(), nil))
return
}
claims := taskJWTClaims(token)
resourceUUID := claims["resourceUUID"]
generation := claims["generation"]
jsonData := struct {
RerunID string `json:"rerun_id"`
TaskName string `json:"task_name"`
UUID string `json:"uuid"`
InClusterGeneration string `json:"generation"`
Content string `json:"content"`
}{}
err = c.BindJSON(&jsonData)
if err != nil {
log.Println(err)
c.JSON(http.StatusUnprocessableEntity, response(http.StatusUnprocessableEntity, err.Error(), nil))
return
}
if jsonData.UUID == "" {
c.JSON(http.StatusUnprocessableEntity, response(http.StatusUnprocessableEntity, "missing request data", nil))
return
}
rerunID, err := strconv.Atoi(jsonData.RerunID)
if err != nil {
c.JSON(http.StatusUnprocessableEntity, response(http.StatusUnprocessableEntity, fmt.Sprintf("rerun is must be an int, got %s", jsonData.RerunID), nil))
return
}
taskPod := models.TaskPod{
UUID: jsonData.UUID,
TaskType: jsonData.TaskName,
Generation: generation,
Rerun: rerunID,
TFOResourceUUID: resourceUUID,
InClusterGeneration: jsonData.InClusterGeneration,
CreatedAt: now,
UpdatedAt: now,
}
result := h.DB.Where("uuid = ?", &jsonData.UUID).FirstOrCreate(&taskPod)
if result.Error != nil {
c.JSON(http.StatusUnprocessableEntity, response(http.StatusUnprocessableEntity, result.Error.Error(), nil))
return
}
if jsonData.Content == "" {
c.JSON(http.StatusOK, response(http.StatusOK, "", []models.TaskPod{taskPod}))
return
}
// Task calls will generally contain content. Save the message to the database.
err = saveTaskLog(h.DB, taskPod.UUID, jsonData.Content)
if err != nil {
c.JSON(http.StatusUnprocessableEntity, response(http.StatusUnprocessableEntity, err.Error(), nil))
return
}
h.Cache.Set(taskPod.TFOResourceUUID, "", 20*time.Second)
c.JSON(http.StatusNoContent, nil)
}
// Write or update logs in database
func saveTaskLog(db *gorm.DB, taskUUID, content string) error {
now := time.Now().UTC()
taskLog := models.TFOTaskLog{
Model: gorm.Model{
CreatedAt: now,
UpdatedAt: now,
},
TaskPodUUID: taskUUID,
Message: content,
Size: uint64(len([]byte(content))),
}
if result := db.Where("task_pod_uuid = ?", &taskLog.TaskPodUUID).FirstOrCreate(&taskLog); result.Error != nil {
return fmt.Errorf("failed to save task log: %+v, %+v", taskLog, result.Error)
}
if taskLog.Size != uint64(len([]byte(content))) {
if taskLog.Size > uint64(len([]byte(content))) {
return fmt.Errorf("sent log's size was smaller than earlier recorded log")
}
// The content has been updated. Read the bytes after what has already been written to preserve the
// original content. We don't want to allow logs in the database to be changed once they are written.
taskLog.UpdatedAt = now
taskLog.Message += string([]byte(content)[taskLog.Size:])
taskLog.Size = uint64(len([]byte(taskLog.Message)))
if result := db.Save(&taskLog); result.Error != nil {
return result.Error
}
}
return nil
}