infra.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. package models
  2. import (
  3. "encoding/hex"
  4. "encoding/json"
  5. "fmt"
  6. "strconv"
  7. "strings"
  8. "gorm.io/gorm"
  9. "github.com/porter-dev/porter/api/types"
  10. "github.com/porter-dev/porter/internal/encryption"
  11. )
  12. // Infra represents the metadata for an infrastructure type provisioned on
  13. // Porter
  14. type Infra struct {
  15. gorm.Model
  16. // The type of infra that was provisioned
  17. Kind types.InfraKind
  18. // The infrastructure API version
  19. APIVersion string
  20. // The source link (only set on apiVersion >= v2)
  21. SourceLink string
  22. // The source version (only set on apiVersion >= v2)
  23. SourceVersion string
  24. // A random 6-byte suffix to ensure workspace/stream ids are unique
  25. Suffix string
  26. // The project that this infra belongs to
  27. ProjectID uint
  28. // The ID of the user that created this infra
  29. CreatedByUserID uint
  30. // If this infra was created under a cluster scope (for example, for RDS), the parent cluster
  31. // ID
  32. ParentClusterID uint
  33. // Status is the status of the infra
  34. Status types.InfraStatus
  35. Operations []Operation
  36. // The AWS integration that was used to create the infra
  37. AWSIntegrationID uint
  38. // The Azure integration that was used to create the infra
  39. AzureIntegrationID uint
  40. // The GCP integration that was used to create the infra
  41. GCPIntegrationID uint
  42. // The DO integration that was used to create the infra:
  43. // this points to an OAuthIntegrationID
  44. DOIntegrationID uint
  45. // The database id for the infra, if this infra provisioned a database
  46. DatabaseID uint
  47. Database Database
  48. // ------------------------------------------------------------------
  49. // All fields below this line are encrypted before storage
  50. // ------------------------------------------------------------------
  51. // The last-applied input variables to the provisioner
  52. LastApplied []byte
  53. }
  54. type Operation struct {
  55. gorm.Model
  56. UID string `gorm:"unique"`
  57. InfraID uint
  58. Type string
  59. Status string
  60. Errored bool
  61. Error string
  62. TemplateVersion string
  63. // ------------------------------------------------------------------
  64. // All fields below this line are encrypted before storage
  65. // ------------------------------------------------------------------
  66. // The last-applied input variables to the provisioner
  67. LastApplied []byte
  68. }
  69. func (o *Operation) ToOperationMetaType() *types.OperationMeta {
  70. return &types.OperationMeta{
  71. LastUpdated: o.UpdatedAt,
  72. UID: o.UID,
  73. InfraID: o.InfraID,
  74. Type: o.Type,
  75. Status: o.Status,
  76. Errored: o.Errored,
  77. Error: o.Error,
  78. }
  79. }
  80. func (o *Operation) ToOperationType() (*types.Operation, error) {
  81. // unmarshal last applied
  82. lastApplied := make(map[string]interface{})
  83. err := json.Unmarshal(o.LastApplied, &lastApplied)
  84. if err != nil {
  85. return nil, err
  86. }
  87. return &types.Operation{
  88. OperationMeta: o.ToOperationMetaType(),
  89. LastApplied: lastApplied,
  90. }, nil
  91. }
  92. func GetOperationID() (string, error) {
  93. return encryption.GenerateRandomBytes(10)
  94. }
  95. type getInfraName struct {
  96. Name string `json:"name"`
  97. ClusterName string `json:"cluster_name"`
  98. DBName string `json:"db_name"`
  99. BucketName string `json:"bucket_name"`
  100. DOCRName string `json:"docr_name"`
  101. ECRName string `json:"ecr_name"`
  102. ACRName string `json:"acr_name"`
  103. }
  104. // ToInfraType generates an external Infra to be shared over REST
  105. func (i *Infra) ToInfraType() *types.Infra {
  106. // perform best attempt to get infra name
  107. var name string
  108. infraName := &getInfraName{}
  109. if err := json.Unmarshal(i.LastApplied, infraName); err == nil {
  110. if infraName.DOCRName != "" {
  111. name = infraName.DOCRName
  112. }
  113. if infraName.ECRName != "" {
  114. name = infraName.ECRName
  115. }
  116. if infraName.ACRName != "" {
  117. name = infraName.ACRName
  118. }
  119. if infraName.DBName != "" {
  120. name = infraName.DBName
  121. }
  122. if infraName.BucketName != "" {
  123. name = infraName.BucketName
  124. }
  125. if infraName.ClusterName != "" {
  126. name = infraName.ClusterName
  127. }
  128. if infraName.Name != "" {
  129. name = infraName.Name
  130. }
  131. }
  132. return &types.Infra{
  133. ID: i.ID,
  134. Name: name,
  135. CreatedAt: i.CreatedAt,
  136. UpdatedAt: i.UpdatedAt,
  137. ProjectID: i.ProjectID,
  138. APIVersion: i.APIVersion,
  139. SourceLink: i.SourceLink,
  140. SourceVersion: i.SourceVersion,
  141. Kind: i.Kind,
  142. Status: i.Status,
  143. AWSIntegrationID: i.AWSIntegrationID,
  144. DOIntegrationID: i.DOIntegrationID,
  145. GCPIntegrationID: i.GCPIntegrationID,
  146. }
  147. }
  148. // GetID returns the unique id for this infra
  149. func (i *Infra) GetUniqueName() string {
  150. return fmt.Sprintf("%s-%d-%d-%s", i.Kind, i.ProjectID, i.ID, i.Suffix)
  151. }
  152. // ParseUniqueName returns the (kind, projectID, infraID, suffix)
  153. func ParseUniqueName(workspaceID string) (string, uint, uint, string, error) {
  154. strArr := strings.Split(workspaceID, "-")
  155. if len(strArr) != 4 {
  156. return "", 0, 0, "", fmt.Errorf("workspace id improperly formatted")
  157. }
  158. projID, err := strconv.ParseUint(strArr[1], 10, 64)
  159. if err != nil {
  160. return "", 0, 0, "", err
  161. }
  162. infraID, err := strconv.ParseUint(strArr[2], 10, 64)
  163. if err != nil {
  164. return "", 0, 0, "", err
  165. }
  166. return strArr[0], uint(projID), uint(infraID), strArr[3], nil
  167. }
  168. type UniqueNameWithOperation struct {
  169. Kind string
  170. ProjectID uint
  171. InfraID uint
  172. Suffix string
  173. OperationUID string
  174. }
  175. func ParseWorkspaceID(workspaceID string) (*UniqueNameWithOperation, error) {
  176. strArr := strings.Split(workspaceID, "-")
  177. if len(strArr) != 5 {
  178. return nil, fmt.Errorf("workspace id improperly formatted: %s", workspaceID)
  179. }
  180. projID, err := strconv.ParseUint(strArr[1], 10, 64)
  181. if err != nil {
  182. return nil, err
  183. }
  184. infraID, err := strconv.ParseUint(strArr[2], 10, 64)
  185. if err != nil {
  186. return nil, err
  187. }
  188. if len(strArr[4]) != hex.EncodedLen(10) {
  189. return nil, fmt.Errorf("operation uid does not have hex length 10")
  190. }
  191. return &UniqueNameWithOperation{
  192. Kind: strArr[0],
  193. ProjectID: uint(projID),
  194. InfraID: uint(infraID),
  195. Suffix: strArr[3],
  196. OperationUID: strArr[4],
  197. }, nil
  198. }
  199. func GetWorkspaceID(infra *Infra, operation *Operation) string {
  200. return fmt.Sprintf("%s-%d-%d-%s-%s", infra.Kind, infra.ProjectID, infra.ID, infra.Suffix, operation.UID)
  201. }