event.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. package gorm
  2. import (
  3. "strings"
  4. "time"
  5. "github.com/porter-dev/porter/api/types"
  6. "github.com/porter-dev/porter/internal/models"
  7. "github.com/porter-dev/porter/internal/repository"
  8. "gorm.io/gorm"
  9. )
  10. // BuildEventRepository holds both EventContainer and SubEvent models
  11. type BuildEventRepository struct {
  12. db *gorm.DB
  13. }
  14. // NewBuildEventRepository returns a BuildEventRepository which uses
  15. // gorm.DB for querying the database
  16. func NewBuildEventRepository(db *gorm.DB) repository.BuildEventRepository {
  17. return &BuildEventRepository{db}
  18. }
  19. func (repo BuildEventRepository) CreateEventContainer(am *models.EventContainer) (*models.EventContainer, error) {
  20. if err := repo.db.Create(am).Error; err != nil {
  21. return nil, err
  22. }
  23. return am, nil
  24. }
  25. func (repo BuildEventRepository) CreateSubEvent(am *models.SubEvent) (*models.SubEvent, error) {
  26. if err := repo.db.Create(am).Error; err != nil {
  27. return nil, err
  28. }
  29. return am, nil
  30. }
  31. func (repo BuildEventRepository) ReadEventsByContainerID(id uint) ([]*models.SubEvent, error) {
  32. var events []*models.SubEvent
  33. if err := repo.db.Where("event_container_id = ?", id).Find(&events).Error; err != nil {
  34. return nil, err
  35. }
  36. return events, nil
  37. }
  38. func (repo BuildEventRepository) ReadEventContainer(id uint) (*models.EventContainer, error) {
  39. container := &models.EventContainer{}
  40. if err := repo.db.Where("id = ?", id).First(&container).Error; err != nil {
  41. return nil, err
  42. }
  43. return container, nil
  44. }
  45. func (repo BuildEventRepository) ReadSubEvent(id uint) (*models.SubEvent, error) {
  46. event := &models.SubEvent{}
  47. if err := repo.db.Where("id = ?", id).First(&event).Error; err != nil {
  48. return nil, err
  49. }
  50. return event, nil
  51. }
  52. // AppendEvent will check if subevent with same (id, index) already exists
  53. // if yes, overrite it, otherwise make a new subevent
  54. func (repo BuildEventRepository) AppendEvent(container *models.EventContainer, event *models.SubEvent) error {
  55. event.EventContainerID = container.ID
  56. return repo.db.Create(event).Error
  57. }
  58. // KubeEventRepository uses gorm.DB for querying the database
  59. type KubeEventRepository struct {
  60. db *gorm.DB
  61. key *[32]byte
  62. }
  63. // NewKubeEventRepository returns an KubeEventRepository which uses
  64. // gorm.DB for querying the database. It accepts an encryption key to encrypt
  65. // sensitive data
  66. func NewKubeEventRepository(db *gorm.DB, key *[32]byte) repository.KubeEventRepository {
  67. return &KubeEventRepository{db, key}
  68. }
  69. // CreateEvent creates a new kube auth mechanism
  70. func (repo *KubeEventRepository) CreateEvent(
  71. event *models.KubeEvent,
  72. ) (*models.KubeEvent, error) {
  73. // read the count of the events in the DB
  74. query := repo.db.Where("project_id = ? AND cluster_id = ?", event.ProjectID, event.ClusterID)
  75. var count int64
  76. if err := query.Model([]*models.KubeEvent{}).Count(&count).Error; err != nil {
  77. return nil, err
  78. }
  79. // if the count is greater than 500, remove the lowest-order event to implement a
  80. // basic fixed-length buffer
  81. if count >= 500 {
  82. // first, delete the matching sub events
  83. err := repo.db.Exec(`
  84. DELETE FROM kube_sub_events
  85. WHERE kube_event_id IN (
  86. SELECT id FROM kube_events k2 WHERE (k2.project_id = ? AND k2.cluster_id = ?) AND k2.id NOT IN (
  87. SELECT id FROM kube_events k3 WHERE (k3.project_id = ? AND k3.cluster_id = ?) ORDER BY k3.updated_at desc, k3.id desc LIMIT 499
  88. )
  89. )
  90. `, event.ProjectID, event.ClusterID, event.ProjectID, event.ClusterID).Error
  91. if err != nil {
  92. return nil, err
  93. }
  94. // then, delete the matching events
  95. err = repo.db.Exec(`
  96. DELETE FROM kube_events
  97. WHERE (project_id = ? AND cluster_id = ?) AND id NOT IN (
  98. SELECT id FROM kube_events k2 WHERE (k2.project_id = ? AND k2.cluster_id = ?) ORDER BY k2.updated_at desc, k2.id desc LIMIT 499
  99. )
  100. `, event.ProjectID, event.ClusterID, event.ProjectID, event.ClusterID).Error
  101. if err != nil {
  102. return nil, err
  103. }
  104. }
  105. if err := repo.db.Create(event).Error; err != nil {
  106. return nil, err
  107. }
  108. return event, nil
  109. }
  110. // ReadEvent finds an event by id
  111. func (repo *KubeEventRepository) ReadEvent(
  112. id, projID, clusterID uint,
  113. ) (*models.KubeEvent, error) {
  114. event := &models.KubeEvent{}
  115. if err := repo.db.Preload("SubEvents").Where(
  116. "id = ? AND project_id = ? AND cluster_id = ?",
  117. id,
  118. projID,
  119. clusterID,
  120. ).First(&event).Error; err != nil {
  121. return nil, err
  122. }
  123. return event, nil
  124. }
  125. // ReadEventByGroup finds an event by a set of options which group events together
  126. func (repo *KubeEventRepository) ReadEventByGroup(
  127. projID uint,
  128. clusterID uint,
  129. opts *types.GroupOptions,
  130. ) (*models.KubeEvent, error) {
  131. event := &models.KubeEvent{}
  132. query := repo.db.Preload("SubEvents").
  133. Where("project_id = ? AND cluster_id = ? AND name = ? AND LOWER(resource_type) = LOWER(?)", projID, clusterID, opts.Name, opts.ResourceType)
  134. // construct query for timestamp
  135. query = query.Where(
  136. "updated_at >= ?", opts.ThresholdTime,
  137. )
  138. if opts.Namespace != "" {
  139. query = query.Where(
  140. "namespace = ?",
  141. strings.ToLower(opts.Namespace),
  142. )
  143. }
  144. if err := query.First(&event).Error; err != nil {
  145. return nil, err
  146. }
  147. return event, nil
  148. }
  149. // ListEventsByProjectID finds all events for a given project id
  150. // with the given options
  151. func (repo *KubeEventRepository) ListEventsByProjectID(
  152. projectID uint,
  153. clusterID uint,
  154. opts *types.ListKubeEventRequest,
  155. ) ([]*models.KubeEvent, int64, error) {
  156. listOpts := opts
  157. if listOpts.Limit == 0 {
  158. listOpts.Limit = 50
  159. }
  160. events := []*models.KubeEvent{}
  161. // preload the subevents
  162. query := repo.db.Preload("SubEvents").Where("project_id = ? AND cluster_id = ?", projectID, clusterID)
  163. if listOpts.OwnerName != "" && listOpts.OwnerType != "" {
  164. query = query.Where(
  165. "LOWER(owner_name) = LOWER(?) AND LOWER(owner_type) = LOWER(?)",
  166. listOpts.OwnerName,
  167. listOpts.OwnerType,
  168. )
  169. }
  170. if listOpts.ResourceType != "" {
  171. query = query.Where(
  172. "LOWER(resource_type) = LOWER(?)",
  173. listOpts.ResourceType,
  174. )
  175. }
  176. if listOpts.Namespace != "" && listOpts.Namespace != "ALL" {
  177. query = query.Where(
  178. "LOWER(namespace) = LOWER(?)",
  179. listOpts.Namespace,
  180. )
  181. }
  182. // get the count before limit and offset
  183. var count int64
  184. if err := query.Model([]*models.KubeEvent{}).Count(&count).Error; err != nil {
  185. return nil, 0, err
  186. }
  187. query = query.Order("updated_at desc").Order("id desc").Limit(listOpts.Limit).Offset(listOpts.Skip)
  188. if err := query.Find(&events).Error; err != nil {
  189. return nil, 0, err
  190. }
  191. return events, count, nil
  192. }
  193. // AppendSubEvent will add a subevent to an existing event
  194. func (repo *KubeEventRepository) AppendSubEvent(event *models.KubeEvent, subEvent *models.KubeSubEvent) error {
  195. subEvent.KubeEventID = event.ID
  196. var count int64
  197. query := repo.db.Where("kube_event_id = ?", event.ID)
  198. if err := query.Model([]*models.KubeSubEvent{}).Count(&count).Error; err != nil {
  199. return err
  200. }
  201. // if the count is greater than 20, remove the lowest-order events to implement a
  202. // basic fixed-length buffer
  203. if count >= 20 {
  204. err := repo.db.Exec(`
  205. DELETE FROM kube_sub_events
  206. WHERE kube_event_id = ? AND
  207. id NOT IN (
  208. SELECT id FROM kube_sub_events k2 WHERE k2.kube_event_id = ? ORDER BY k2.updated_at desc, k2.id desc LIMIT 19
  209. )
  210. `, event.ID, event.ID).Error
  211. if err != nil {
  212. return err
  213. }
  214. }
  215. // we construct a shallow copy here that just populates the primary key, because otherwise gorm
  216. // attempts to write subevents that have already been written via the association.
  217. shallowCopy := &models.KubeEvent{
  218. Model: gorm.Model{
  219. ID: event.ID,
  220. },
  221. }
  222. if err := repo.db.Model(shallowCopy).Association("SubEvents").Append(subEvent); err != nil {
  223. return err
  224. }
  225. // only update the updated_at field for the event
  226. if err := repo.db.Model(shallowCopy).Update("updated_at", time.Now()).Error; err != nil {
  227. return err
  228. }
  229. event.SubEvents = append(event.SubEvents, shallowCopy.SubEvents...)
  230. event.UpdatedAt = shallowCopy.UpdatedAt
  231. return nil
  232. }
  233. // DeleteEvent deletes an event by ID
  234. func (repo *KubeEventRepository) DeleteEvent(
  235. id uint,
  236. ) error {
  237. return deleteEventPermanently(id, repo.db)
  238. }
  239. func deleteEventPermanently(id uint, db *gorm.DB) error {
  240. // delete all subevents first
  241. if err := db.Unscoped().Where("kube_event_id = ?", id).Delete(&models.KubeSubEvent{}).Error; err != nil {
  242. return err
  243. }
  244. // delete event
  245. return db.Preload("SubEvents").Unscoped().Where("id = ?", id).Delete(&models.KubeEvent{}).Error
  246. }