update_config_driver.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. package preview
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "github.com/cli/cli/git"
  7. "github.com/fatih/color"
  8. "github.com/mitchellh/mapstructure"
  9. api "github.com/porter-dev/porter/api/client"
  10. "github.com/porter-dev/porter/api/types"
  11. "github.com/porter-dev/porter/cli/cmd/config"
  12. "github.com/porter-dev/porter/cli/cmd/deploy"
  13. "github.com/porter-dev/porter/cli/cmd/deploy/wait"
  14. "github.com/porter-dev/porter/internal/templater/utils"
  15. "github.com/porter-dev/switchboard/pkg/drivers"
  16. "github.com/porter-dev/switchboard/pkg/models"
  17. )
  18. type UpdateConfigDriverConfig struct {
  19. WaitForJob bool
  20. // If set to true, this does not run an update, it only creates the initial application and job,
  21. // skipping subsequent updates
  22. OnlyCreate bool
  23. UpdateConfig struct {
  24. Image string
  25. } `mapstructure:"update_config"`
  26. EnvGroups []types.EnvGroupMeta `mapstructure:"env_groups"`
  27. Values map[string]interface{}
  28. }
  29. type UpdateConfigDriver struct {
  30. source *Source
  31. target *Target
  32. config *UpdateConfigDriverConfig
  33. lookupTable *map[string]drivers.Driver
  34. output map[string]interface{}
  35. }
  36. func NewUpdateConfigDriver(resource *models.Resource, opts *drivers.SharedDriverOpts) (drivers.Driver, error) {
  37. driver := &UpdateConfigDriver{
  38. lookupTable: opts.DriverLookupTable,
  39. output: make(map[string]interface{}),
  40. }
  41. source, err := GetSource(resource.Source)
  42. if err != nil {
  43. return nil, err
  44. }
  45. driver.source = source
  46. target, err := GetTarget(resource.Target)
  47. if err != nil {
  48. return nil, err
  49. }
  50. if target.AppName == "" {
  51. return nil, fmt.Errorf("target app_name is missing")
  52. }
  53. driver.target = target
  54. return driver, nil
  55. }
  56. func (d *UpdateConfigDriver) ShouldApply(resource *models.Resource) bool {
  57. return true
  58. }
  59. func (d *UpdateConfigDriver) Apply(resource *models.Resource) (*models.Resource, error) {
  60. updateConfigDriverConfig, err := d.getConfig(resource)
  61. if err != nil {
  62. return nil, err
  63. }
  64. d.config = updateConfigDriverConfig
  65. client := config.GetAPIClient()
  66. _, err = client.GetRelease(
  67. context.Background(),
  68. d.target.Project,
  69. d.target.Cluster,
  70. d.target.Namespace,
  71. d.target.AppName,
  72. )
  73. shouldCreate := err != nil
  74. // FIXME: give tag option in config build, but override if PORTER_TAG is present
  75. tag := os.Getenv("PORTER_TAG")
  76. if tag == "" {
  77. commit, err := git.LastCommit()
  78. if err != nil {
  79. return nil, err
  80. }
  81. tag = commit.Sha[:7]
  82. }
  83. sharedOpts := &deploy.SharedOpts{
  84. ProjectID: d.target.Project,
  85. ClusterID: d.target.Cluster,
  86. OverrideTag: tag,
  87. Namespace: d.target.Namespace,
  88. Method: "registry",
  89. EnvGroups: d.config.EnvGroups,
  90. }
  91. if shouldCreate {
  92. color.New(color.FgYellow).Printf("Could not read release %s/%s (%s): attempting creation\n", d.target.Namespace, d.target.AppName, err.Error())
  93. createAgent := &deploy.CreateAgent{
  94. Client: client,
  95. CreateOpts: &deploy.CreateOpts{
  96. SharedOpts: sharedOpts,
  97. Kind: d.source.Name,
  98. ReleaseName: d.target.AppName,
  99. },
  100. }
  101. _, err := createAgent.CreateFromRegistry(d.config.UpdateConfig.Image, d.config.Values)
  102. if err != nil {
  103. return nil, err
  104. }
  105. } else {
  106. updateAgent, err := deploy.NewDeployAgent(client, d.target.AppName, &deploy.DeployOpts{
  107. SharedOpts: sharedOpts,
  108. Local: false,
  109. })
  110. if err != nil {
  111. return nil, err
  112. }
  113. err = updateAgent.UpdateImageAndValues(d.config.Values)
  114. if err != nil {
  115. return nil, err
  116. }
  117. }
  118. if d.source.Name == "job" && updateConfigDriverConfig.WaitForJob && (shouldCreate || !updateConfigDriverConfig.OnlyCreate) {
  119. color.New(color.FgYellow).Printf("Waiting for job '%s' to finish\n", resource.Name)
  120. err = wait.WaitForJob(client, &wait.WaitOpts{
  121. ProjectID: d.target.Project,
  122. ClusterID: d.target.Cluster,
  123. Namespace: d.target.Namespace,
  124. Name: d.target.AppName,
  125. })
  126. if err != nil {
  127. return nil, err
  128. }
  129. }
  130. err = d.assignOutput(resource, client)
  131. if err != nil {
  132. return nil, err
  133. }
  134. return resource, nil
  135. }
  136. func (d *UpdateConfigDriver) Output() (map[string]interface{}, error) {
  137. return d.output, nil
  138. }
  139. func (d *UpdateConfigDriver) getConfig(resource *models.Resource) (*UpdateConfigDriverConfig, error) {
  140. populatedConf, err := drivers.ConstructConfig(&drivers.ConstructConfigOpts{
  141. RawConf: resource.Config,
  142. LookupTable: *d.lookupTable,
  143. Dependencies: resource.Dependencies,
  144. })
  145. if err != nil {
  146. return nil, err
  147. }
  148. config := &UpdateConfigDriverConfig{}
  149. err = mapstructure.Decode(populatedConf, config)
  150. if err != nil {
  151. return nil, err
  152. }
  153. return config, nil
  154. }
  155. func (d *UpdateConfigDriver) assignOutput(resource *models.Resource, client *api.Client) error {
  156. release, err := client.GetRelease(
  157. context.Background(),
  158. d.target.Project,
  159. d.target.Cluster,
  160. d.target.Namespace,
  161. d.target.AppName,
  162. )
  163. if err != nil {
  164. return err
  165. }
  166. d.output = utils.CoalesceValues(d.source.SourceValues, release.Config)
  167. return nil
  168. }