update_config_driver.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. package preview
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "strings"
  7. "github.com/cli/cli/git"
  8. "github.com/fatih/color"
  9. "github.com/mitchellh/mapstructure"
  10. api "github.com/porter-dev/porter/api/client"
  11. "github.com/porter-dev/porter/api/types"
  12. "github.com/porter-dev/porter/cli/cmd/config"
  13. "github.com/porter-dev/porter/cli/cmd/deploy"
  14. "github.com/porter-dev/porter/cli/cmd/deploy/wait"
  15. "github.com/porter-dev/porter/internal/integrations/preview"
  16. "github.com/porter-dev/porter/internal/templater/utils"
  17. "github.com/porter-dev/switchboard/pkg/drivers"
  18. "github.com/porter-dev/switchboard/pkg/models"
  19. )
  20. type UpdateConfigDriver struct {
  21. source *preview.Source
  22. target *preview.Target
  23. config *preview.UpdateConfigDriverConfig
  24. lookupTable *map[string]drivers.Driver
  25. output map[string]interface{}
  26. }
  27. func NewUpdateConfigDriver(resource *models.Resource, opts *drivers.SharedDriverOpts) (drivers.Driver, error) {
  28. driver := &UpdateConfigDriver{
  29. lookupTable: opts.DriverLookupTable,
  30. output: make(map[string]interface{}),
  31. }
  32. target, err := GetTarget(resource.Name, resource.Target)
  33. if err != nil {
  34. return nil, err
  35. }
  36. driver.target = target
  37. source, err := GetSource(driver.target.Project, resource.Name, resource.Source)
  38. if err != nil {
  39. return nil, err
  40. }
  41. driver.source = source
  42. return driver, nil
  43. }
  44. func (d *UpdateConfigDriver) ShouldApply(resource *models.Resource) bool {
  45. return true
  46. }
  47. func (d *UpdateConfigDriver) Apply(resource *models.Resource) (*models.Resource, error) {
  48. ctx := context.Background()
  49. updateConfigDriverConfig, err := d.getConfig(resource)
  50. if err != nil {
  51. return nil, err
  52. }
  53. d.config = updateConfigDriverConfig
  54. client := config.GetAPIClient()
  55. _, err = client.GetRelease(
  56. ctx,
  57. d.target.Project,
  58. d.target.Cluster,
  59. d.target.Namespace,
  60. d.target.AppName,
  61. )
  62. shouldCreate := err != nil
  63. color.New(color.FgBlue).Println("checking for the existence of PORTER_TAG environment variable for the image tag")
  64. tag := os.Getenv("PORTER_TAG")
  65. if tag == "" {
  66. color.New(color.FgBlue).Println("PORTER_TAG environment variable not found, checking for update_config.tag in porter.yaml for the image tag")
  67. tag = d.config.UpdateConfig.Tag
  68. }
  69. if tag == "" {
  70. color.New(color.FgBlue).Println("update_config.tag not found in porter.yaml, falling back to the latest git commit SHA as the image tag")
  71. commit, err := git.LastCommit()
  72. if err != nil {
  73. return nil, err
  74. }
  75. tag = commit.Sha[:7]
  76. }
  77. regList, err := client.ListRegistries(context.Background(), d.target.Project)
  78. if err != nil {
  79. return nil, err
  80. }
  81. var registryURL string
  82. if len(*regList) == 0 {
  83. return nil, fmt.Errorf("no registry found")
  84. } else {
  85. registryURL = (*regList)[0].URL
  86. }
  87. var repoSuffix string
  88. if repoName := os.Getenv("PORTER_REPO_NAME"); repoName != "" {
  89. if repoOwner := os.Getenv("PORTER_REPO_OWNER"); repoOwner != "" {
  90. repoSuffix = strings.ToLower(strings.ReplaceAll(fmt.Sprintf("%s-%s", repoOwner, repoName), "_", "-"))
  91. }
  92. }
  93. sharedOpts := &deploy.SharedOpts{
  94. ProjectID: d.target.Project,
  95. ClusterID: d.target.Cluster,
  96. OverrideTag: tag,
  97. Namespace: d.target.Namespace,
  98. Method: "registry",
  99. EnvGroups: d.config.EnvGroups,
  100. }
  101. if shouldCreate {
  102. color.New(color.FgYellow).Printf("Could not read release %s/%s: attempting creation\n", d.target.Namespace, d.target.AppName)
  103. createAgent := &deploy.CreateAgent{
  104. Client: client,
  105. CreateOpts: &deploy.CreateOpts{
  106. SharedOpts: sharedOpts,
  107. Kind: d.source.Name,
  108. ReleaseName: d.target.AppName,
  109. RegistryURL: registryURL,
  110. RepoSuffix: repoSuffix,
  111. },
  112. }
  113. regID, imageURL, err := createAgent.GetImageRepoURL(d.target.AppName, sharedOpts.Namespace)
  114. if err != nil {
  115. return nil, err
  116. }
  117. err = client.CreateRepository(
  118. ctx,
  119. sharedOpts.ProjectID,
  120. regID,
  121. &types.CreateRegistryRepositoryRequest{
  122. ImageRepoURI: imageURL,
  123. },
  124. )
  125. if err != nil {
  126. return nil, err
  127. }
  128. image := fmt.Sprintf("%s:%s", strings.Split(d.config.UpdateConfig.Image, ":")[0], tag)
  129. _, err = createAgent.CreateFromRegistry(image, d.config.Values)
  130. if err != nil {
  131. return nil, err
  132. }
  133. } else if !updateConfigDriverConfig.OnlyCreate {
  134. updateAgent, err := deploy.NewDeployAgent(client, d.target.AppName, &deploy.DeployOpts{
  135. SharedOpts: sharedOpts,
  136. Local: false,
  137. })
  138. if err != nil {
  139. return nil, err
  140. }
  141. err = updateAgent.UpdateImageAndValues(d.config.Values)
  142. if err != nil {
  143. return nil, err
  144. }
  145. }
  146. if d.source.Name == "job" && updateConfigDriverConfig.WaitForJob && (shouldCreate || !updateConfigDriverConfig.OnlyCreate) {
  147. color.New(color.FgYellow).Printf("Waiting for job '%s' to finish\n", resource.Name)
  148. err = wait.WaitForJob(client, &wait.WaitOpts{
  149. ProjectID: d.target.Project,
  150. ClusterID: d.target.Cluster,
  151. Namespace: d.target.Namespace,
  152. Name: d.target.AppName,
  153. })
  154. if err != nil {
  155. return nil, err
  156. }
  157. }
  158. err = d.assignOutput(resource, client)
  159. if err != nil {
  160. return nil, err
  161. }
  162. return resource, nil
  163. }
  164. func (d *UpdateConfigDriver) Output() (map[string]interface{}, error) {
  165. return d.output, nil
  166. }
  167. func (d *UpdateConfigDriver) getConfig(resource *models.Resource) (*preview.UpdateConfigDriverConfig, error) {
  168. populatedConf, err := drivers.ConstructConfig(&drivers.ConstructConfigOpts{
  169. RawConf: resource.Config,
  170. LookupTable: *d.lookupTable,
  171. Dependencies: resource.Dependencies,
  172. })
  173. if err != nil {
  174. return nil, err
  175. }
  176. config := &preview.UpdateConfigDriverConfig{}
  177. err = mapstructure.Decode(populatedConf, config)
  178. if err != nil {
  179. return nil, err
  180. }
  181. return config, nil
  182. }
  183. func (d *UpdateConfigDriver) assignOutput(resource *models.Resource, client *api.Client) error {
  184. release, err := client.GetRelease(
  185. context.Background(),
  186. d.target.Project,
  187. d.target.Cluster,
  188. d.target.Namespace,
  189. d.target.AppName,
  190. )
  191. if err != nil {
  192. return err
  193. }
  194. d.output = utils.CoalesceValues(d.source.SourceValues, release.Config)
  195. return nil
  196. }