environment.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. package gorm
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/porter-dev/porter/internal/models"
  6. "github.com/porter-dev/porter/internal/repository"
  7. "gorm.io/gorm"
  8. )
  9. // EnvironmentRepository uses gorm.DB for querying the database
  10. type EnvironmentRepository struct {
  11. db *gorm.DB
  12. }
  13. // NewEnvironmentRepository returns a DefaultEnvironmentRepository which uses
  14. // gorm.DB for querying the database
  15. func NewEnvironmentRepository(db *gorm.DB) repository.EnvironmentRepository {
  16. return &EnvironmentRepository{db}
  17. }
  18. func (repo *EnvironmentRepository) CreateEnvironment(env *models.Environment) (*models.Environment, error) {
  19. if err := repo.db.Create(env).Error; err != nil {
  20. return nil, err
  21. }
  22. return env, nil
  23. }
  24. func (repo *EnvironmentRepository) ReadEnvironment(projectID, clusterID, gitInstallationID uint, gitRepoOwner, gitRepoName string) (*models.Environment, error) {
  25. env := &models.Environment{}
  26. switch repo.db.Dialector.Name() {
  27. case "sqlite":
  28. if err := repo.db.Order("id desc").Where(
  29. "project_id = ? AND cluster_id = ? AND git_installation_id = ? AND git_repo_owner LIKE ? AND git_repo_name LIKE ?",
  30. projectID, clusterID, gitInstallationID, gitRepoOwner, gitRepoName,
  31. ).First(&env).Error; err != nil {
  32. return nil, err
  33. }
  34. case "postgres":
  35. if err := repo.db.Order("id desc").Where(
  36. "project_id = ? AND cluster_id = ? AND git_installation_id = ? AND git_repo_owner iLIKE ? AND git_repo_name iLIKE ?",
  37. projectID, clusterID, gitInstallationID, gitRepoOwner, gitRepoName,
  38. ).First(&env).Error; err != nil {
  39. return nil, err
  40. }
  41. }
  42. return env, nil
  43. }
  44. func (repo *EnvironmentRepository) ReadEnvironmentByID(projectID, clusterID, envID uint) (*models.Environment, error) {
  45. env := &models.Environment{}
  46. if err := repo.db.Order("id desc").Where(
  47. "project_id = ? AND cluster_id = ? AND id = ?",
  48. projectID, clusterID, envID,
  49. ).First(&env).Error; err != nil {
  50. return nil, err
  51. }
  52. return env, nil
  53. }
  54. func (repo *EnvironmentRepository) ReadEnvironmentByOwnerRepoName(
  55. projectID, clusterID uint,
  56. gitRepoOwner, gitRepoName string,
  57. ) (*models.Environment, error) {
  58. env := &models.Environment{}
  59. switch repo.db.Dialector.Name() {
  60. case "sqlite":
  61. if err := repo.db.Order("id desc").Where("project_id = ? AND cluster_id = ? AND git_repo_owner LIKE ? AND git_repo_name LIKE ?",
  62. projectID, clusterID, gitRepoOwner, gitRepoName,
  63. ).First(&env).Error; err != nil {
  64. return nil, err
  65. }
  66. case "postgres":
  67. if err := repo.db.Order("id desc").Where("project_id = ? AND cluster_id = ? AND git_repo_owner iLIKE ? AND git_repo_name iLIKE ?",
  68. projectID, clusterID, gitRepoOwner, gitRepoName,
  69. ).First(&env).Error; err != nil {
  70. return nil, err
  71. }
  72. }
  73. return env, nil
  74. }
  75. func (repo *EnvironmentRepository) ReadEnvironmentByWebhookIDOwnerRepoName(
  76. webhookID, gitRepoOwner, gitRepoName string,
  77. ) (*models.Environment, error) {
  78. env := &models.Environment{}
  79. switch repo.db.Dialector.Name() {
  80. case "sqlite":
  81. if err := repo.db.Order("id desc").Where("webhook_id = ? AND git_repo_owner LIKE ? AND git_repo_name LIKE ?",
  82. webhookID, gitRepoOwner, gitRepoName,
  83. ).First(&env).Error; err != nil {
  84. return nil, err
  85. }
  86. case "postgres":
  87. if err := repo.db.Order("id desc").Where("webhook_id = ? AND git_repo_owner iLIKE ? AND git_repo_name iLIKE ?",
  88. webhookID, gitRepoOwner, gitRepoName,
  89. ).First(&env).Error; err != nil {
  90. return nil, err
  91. }
  92. }
  93. return env, nil
  94. }
  95. func (repo *EnvironmentRepository) ListEnvironments(projectID, clusterID uint) ([]*models.Environment, error) {
  96. envs := make([]*models.Environment, 0)
  97. if err := repo.db.Order("id asc").Where("project_id = ? AND cluster_id = ?", projectID, clusterID).Find(&envs).Error; err != nil {
  98. return nil, err
  99. }
  100. return envs, nil
  101. }
  102. func (repo *EnvironmentRepository) UpdateEnvironment(environment *models.Environment) (*models.Environment, error) {
  103. if err := repo.db.Save(environment).Error; err != nil {
  104. return nil, err
  105. }
  106. return environment, nil
  107. }
  108. func (repo *EnvironmentRepository) DeleteEnvironment(env *models.Environment) (*models.Environment, error) {
  109. if err := repo.db.Delete(&env).Error; err != nil {
  110. return nil, err
  111. }
  112. return env, nil
  113. }
  114. func (repo *EnvironmentRepository) CreateDeployment(deployment *models.Deployment) (*models.Deployment, error) {
  115. if err := repo.db.Create(deployment).Error; err != nil {
  116. return nil, err
  117. }
  118. return deployment, nil
  119. }
  120. func (repo *EnvironmentRepository) UpdateDeployment(deployment *models.Deployment) (*models.Deployment, error) {
  121. if err := repo.db.Save(deployment).Error; err != nil {
  122. return nil, err
  123. }
  124. return deployment, nil
  125. }
  126. func (repo *EnvironmentRepository) ReadDeployment(environmentID uint, namespace string) (*models.Deployment, error) {
  127. depl := &models.Deployment{}
  128. if err := repo.db.Order("id desc").Where("environment_id = ? AND namespace = ?", environmentID, namespace).First(&depl).Error; err != nil {
  129. return nil, err
  130. }
  131. return depl, nil
  132. }
  133. func (repo *EnvironmentRepository) ReadDeploymentByID(projectID, clusterID, id uint) (*models.Deployment, error) {
  134. depl := &models.Deployment{}
  135. if err := repo.db.
  136. Order("deployments.updated_at desc").
  137. Joins("INNER JOIN environments ON environments.id = deployments.environment_id").
  138. Where("environments.project_id = ? AND environments.cluster_id = ? AND deployments.id = ?", projectID, clusterID, id).First(&depl).Error; err != nil {
  139. return nil, err
  140. }
  141. return depl, nil
  142. }
  143. func (repo *EnvironmentRepository) ReadDeploymentByGitDetails(
  144. environmentID uint, gitRepoOwner, gitRepoName string, prNumber uint,
  145. ) (*models.Deployment, error) {
  146. depl := &models.Deployment{}
  147. switch repo.db.Dialector.Name() {
  148. case "sqlite":
  149. if err := repo.db.Order("id asc").
  150. Where("environment_id = ? AND repo_owner LIKE ? AND repo_name LIKE ? AND pull_request_id = ?",
  151. environmentID, gitRepoOwner, gitRepoName, prNumber).
  152. First(&depl).Error; err != nil {
  153. return nil, err
  154. }
  155. case "postgres":
  156. if err := repo.db.Order("id asc").
  157. Where("environment_id = ? AND repo_owner iLIKE ? AND repo_name iLIKE ? AND pull_request_id = ?",
  158. environmentID, gitRepoOwner, gitRepoName, prNumber).
  159. First(&depl).Error; err != nil {
  160. return nil, err
  161. }
  162. }
  163. return depl, nil
  164. }
  165. func (repo *EnvironmentRepository) ListDeploymentsByCluster(projectID, clusterID uint, states ...string) ([]*models.Deployment, error) {
  166. query := repo.db.
  167. Order("deployments.updated_at desc").
  168. Joins("INNER JOIN environments ON environments.id = deployments.environment_id").
  169. Where("environments.project_id = ? AND environments.cluster_id = ? AND environments.deleted_at IS NULL", projectID, clusterID)
  170. if len(states) > 0 {
  171. queryArr := make([]string, len(states))
  172. stateInterArr := make([]interface{}, len(states))
  173. for i, state := range states {
  174. queryArr[i] = "deployments.status = ?"
  175. stateInterArr[i] = state
  176. }
  177. query = query.Where(strings.Join(queryArr, " OR "), stateInterArr...)
  178. }
  179. depls := make([]*models.Deployment, 0)
  180. if err := query.Find(&depls).Error; err != nil {
  181. return nil, err
  182. }
  183. return depls, nil
  184. }
  185. func (repo *EnvironmentRepository) ListDeployments(environmentID uint, states ...string) ([]*models.Deployment, error) {
  186. query := repo.db.Debug().Order("deployments.updated_at desc").Where("environment_id = ?", environmentID)
  187. if len(states) > 0 {
  188. queryArr := make([]string, len(states))
  189. stateInterArr := make([]interface{}, len(states))
  190. for i, state := range states {
  191. queryArr[i] = "deployments.status = ?"
  192. stateInterArr[i] = state
  193. }
  194. query = query.Where(strings.Join(queryArr, " OR "), stateInterArr...)
  195. }
  196. depls := make([]*models.Deployment, 0)
  197. if err := query.Find(&depls).Error; err != nil {
  198. return nil, err
  199. }
  200. return depls, nil
  201. }
  202. func (repo *EnvironmentRepository) DeleteDeployment(deployment *models.Deployment) (*models.Deployment, error) {
  203. depl := &models.Deployment{}
  204. if err := repo.db.Where("id = ?", deployment.ID).First(&depl).Error; err != nil {
  205. return nil, fmt.Errorf("error fetching deployment ID %d: %w", deployment.ID, err)
  206. }
  207. revisions, err := repo.ListDeploymentRevisions(depl.ID)
  208. if err != nil {
  209. return nil, fmt.Errorf("error fetching revisions for deployment ID %d: %w", depl.ID, err)
  210. }
  211. err = repo.db.Model(&depl).Association("Revisions").Clear()
  212. if err != nil {
  213. return nil, fmt.Errorf("error clearing revision associations for deployment ID %d: %w", depl.ID, err)
  214. }
  215. for _, rev := range revisions {
  216. err := repo.db.Model(&rev).Association("Resources").Clear()
  217. if err != nil {
  218. return nil, fmt.Errorf("error clearing resource associations for revision ID %d: %w", rev.ID, err)
  219. }
  220. for _, res := range rev.Resources {
  221. if err := repo.db.Delete(&res).Error; err != nil {
  222. return nil, fmt.Errorf("error deleting resource ID %d: %w", res.ID, err)
  223. }
  224. }
  225. if err := repo.db.Delete(&rev).Error; err != nil {
  226. return nil, fmt.Errorf("error deleting revision ID %d: %w", rev.ID, err)
  227. }
  228. }
  229. if err := repo.db.Delete(deployment).Error; err != nil {
  230. return nil, fmt.Errorf("error deleting deployment ID %d: %w", deployment.ID, err)
  231. }
  232. return deployment, nil
  233. }
  234. func (repo *EnvironmentRepository) AddNewDeploymentRevision(deploymentID uint, revision *models.DeploymentRevision) (*models.DeploymentRevision, error) {
  235. if deploymentID == 0 {
  236. return nil, fmt.Errorf("deployment ID cannot be set to 0")
  237. }
  238. depl := &models.Deployment{}
  239. if err := repo.db.Where("id = ?", deploymentID).First(&depl).Error; err != nil {
  240. return nil, fmt.Errorf("error fetching deployment ID %d: %w", deploymentID, err)
  241. }
  242. assoc := repo.db.Model(&depl).Association("Revisions")
  243. if assoc.Error != nil {
  244. return nil, fmt.Errorf("error fetting association Revisions for deployment ID %d: %w", deploymentID, assoc.Error)
  245. }
  246. revision.RevisionNumber = uint(assoc.Count() + 1)
  247. if err := assoc.Append(revision); err != nil {
  248. return nil, fmt.Errorf("error appending new revision for deployment ID %d: %w", deploymentID, err)
  249. }
  250. return revision, nil
  251. }
  252. func (repo *EnvironmentRepository) ListDeploymentRevisions(deploymentID uint) ([]*models.DeploymentRevision, error) {
  253. depl := &models.Deployment{}
  254. if err := repo.db.Where("id = ?", deploymentID).First(&depl).Error; err != nil {
  255. return nil, fmt.Errorf("error fetching deployment ID %d: %w", deploymentID, err)
  256. }
  257. var revisions []*models.DeploymentRevision
  258. // FIXME: use proper pagination
  259. if err := repo.db.Preload("Resources").Where("deployment_id = ?", deploymentID).Order("revision_number desc").Limit(25).Find(&revisions).Error; err != nil {
  260. return nil, fmt.Errorf("error fetching revisions for deployment ID %d: %w", deploymentID, err)
  261. }
  262. return revisions, nil
  263. }
  264. func (repo *EnvironmentRepository) ReadDeploymentRevision(deploymentID, revisionNumber uint) (*models.DeploymentRevision, error) {
  265. depl := &models.Deployment{}
  266. if err := repo.db.Where("id = ?", deploymentID).First(&depl).Error; err != nil {
  267. return nil, fmt.Errorf("error fetching deployment ID %d: %w", deploymentID, err)
  268. }
  269. revision := &models.DeploymentRevision{}
  270. if err := repo.db.Preload("Resources").Where("deployment_id = ? AND revision_number = ?", deploymentID, revisionNumber).First(&revision).Error; err != nil {
  271. return nil, fmt.Errorf("error fetching revision number %d for deployment ID %d: %w", revisionNumber, deploymentID, err)
  272. }
  273. return revision, nil
  274. }