hooks.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. package stack
  2. import (
  3. "context"
  4. "encoding/base64"
  5. "fmt"
  6. "strings"
  7. "github.com/fatih/color"
  8. api "github.com/porter-dev/porter/api/client"
  9. "github.com/porter-dev/porter/api/types"
  10. "github.com/porter-dev/porter/cli/cmd/config"
  11. )
  12. type DeployAppHook struct {
  13. Client *api.Client
  14. ApplicationName string
  15. ProjectID, ClusterID uint
  16. BuildImageDriverName string
  17. PorterYAML []byte
  18. Builder string
  19. BuildEventID string
  20. }
  21. func (t *DeployAppHook) PreApply() error {
  22. err := config.ValidateCLIEnvironment()
  23. if err != nil {
  24. errMsg := composePreviewMessage("porter CLI is not configured correctly", Error)
  25. return fmt.Errorf("%s: %w", errMsg, err)
  26. }
  27. buildEventId, err := createAppEvent(t.Client, t.ApplicationName, t.ProjectID, t.ClusterID)
  28. if err != nil {
  29. return err
  30. }
  31. t.BuildEventID = buildEventId
  32. return nil
  33. }
  34. func (t *DeployAppHook) DataQueries() map[string]interface{} {
  35. res := map[string]interface{}{
  36. "image": fmt.Sprintf("{$.%s.image}", t.BuildImageDriverName),
  37. }
  38. return res
  39. }
  40. // deploy the app
  41. func (t *DeployAppHook) PostApply(driverOutput map[string]interface{}) error {
  42. namespace := fmt.Sprintf("porter-stack-%s", t.ApplicationName)
  43. _, err := t.Client.GetRelease(
  44. context.Background(),
  45. t.ProjectID,
  46. t.ClusterID,
  47. namespace,
  48. t.ApplicationName,
  49. )
  50. shouldCreate := err != nil
  51. if err != nil {
  52. color.New(color.FgYellow).Printf("Could not read release for app %s (%s): attempting creation\n", t.ApplicationName, err.Error())
  53. } else {
  54. color.New(color.FgGreen).Printf("Found release for app %s: attempting update\n", t.ApplicationName)
  55. }
  56. err = t.createOrUpdateApplication(shouldCreate, driverOutput)
  57. if err != nil {
  58. return err
  59. }
  60. eventRequest := types.CreateOrUpdatePorterAppEventRequest{
  61. Status: "SUCCESS",
  62. Type: types.PorterAppEventType_Build,
  63. Metadata: map[string]any{},
  64. ID: t.BuildEventID,
  65. }
  66. _, _ = t.Client.CreateOrUpdatePorterAppEvent(context.Background(), t.ProjectID, t.ClusterID, t.ApplicationName, &eventRequest)
  67. return nil
  68. }
  69. func (t *DeployAppHook) createOrUpdateApplication(shouldCreate bool, driverOutput map[string]interface{}) error {
  70. var imageInfo types.ImageInfo
  71. image, ok := driverOutput["image"].(string)
  72. // if it contains a $, then it means the query didn't resolve to anything
  73. if ok && !strings.Contains(image, "$") {
  74. imageSpl := strings.Split(image, ":")
  75. if len(imageSpl) == 2 {
  76. imageInfo = types.ImageInfo{
  77. Repository: imageSpl[0],
  78. Tag: imageSpl[1],
  79. }
  80. } else {
  81. return fmt.Errorf("could not parse image info %s", image)
  82. }
  83. }
  84. _, err := t.Client.CreatePorterApp(
  85. context.Background(),
  86. t.ProjectID,
  87. t.ClusterID,
  88. t.ApplicationName,
  89. &types.CreatePorterAppRequest{
  90. ClusterID: t.ClusterID,
  91. ProjectID: t.ProjectID,
  92. PorterYAMLBase64: base64.StdEncoding.EncodeToString(t.PorterYAML),
  93. ImageInfo: imageInfo,
  94. OverrideRelease: false, // deploying from the cli will never delete release resources, only append or override
  95. Builder: t.Builder,
  96. },
  97. )
  98. if err != nil {
  99. if shouldCreate {
  100. return fmt.Errorf("error creating app %s: %w", t.ApplicationName, err)
  101. }
  102. return fmt.Errorf("error updating app %s: %w", t.ApplicationName, err)
  103. }
  104. return nil
  105. }
  106. func (t *DeployAppHook) OnConsolidatedErrors(errors map[string]error) {
  107. errorStringMap := make(map[string]string)
  108. for k, v := range errors {
  109. errorStringMap[k] = fmt.Sprintf("%+v", v)
  110. }
  111. eventRequest := types.CreateOrUpdatePorterAppEventRequest{
  112. Status: "FAILED",
  113. Type: types.PorterAppEventType_Build,
  114. Metadata: map[string]any{
  115. "errors": errorStringMap,
  116. },
  117. ID: t.BuildEventID,
  118. }
  119. _, _ = t.Client.CreateOrUpdatePorterAppEvent(context.Background(), t.ProjectID, t.ClusterID, t.ApplicationName, &eventRequest)
  120. }
  121. func (t *DeployAppHook) OnError(err error) {
  122. t.OnConsolidatedErrors(map[string]error{
  123. "pre-apply": err,
  124. })
  125. }