default_driver.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. package v2
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "strings"
  7. "github.com/mitchellh/mapstructure"
  8. api "github.com/porter-dev/porter/api/client"
  9. apiTypes "github.com/porter-dev/porter/api/types"
  10. "github.com/porter-dev/porter/cli/cmd/config"
  11. "github.com/porter-dev/porter/cli/cmd/deploy"
  12. "github.com/porter-dev/switchboard/v2/pkg/types"
  13. )
  14. type DefaultDriver struct {
  15. Vars map[string]string
  16. Env map[string]string
  17. Builds []*types.Build
  18. APIClient *api.Client
  19. Namespace string
  20. allErrors []error
  21. }
  22. func (d *DefaultDriver) PreApply(resource *types.YAMLNode[*types.Resource]) error {
  23. return nil
  24. }
  25. func (d *DefaultDriver) Apply(resource *types.YAMLNode[*types.Resource]) error {
  26. if isPorterApp(resource) {
  27. return d.applyPorterApp(resource)
  28. }
  29. // everything else is an addon
  30. return d.applyAddon(resource)
  31. }
  32. func (d *DefaultDriver) PostApply(resource *types.YAMLNode[*types.Resource]) error {
  33. return nil
  34. }
  35. func (d *DefaultDriver) OnError(resource *types.YAMLNode[*types.Resource], err error) {
  36. }
  37. func isPorterApp(resource *types.YAMLNode[*types.Resource]) bool {
  38. if resource.GetValue().ChartURL.GetValue() == "https://charts.getporter.dev" &&
  39. (resource.GetValue().Type.GetValue() == "web" ||
  40. resource.GetValue().Type.GetValue() == "worker" ||
  41. resource.GetValue().Type.GetValue() == "job") {
  42. return true
  43. }
  44. return false
  45. }
  46. func (d *DefaultDriver) applyPorterApp(resource *types.YAMLNode[*types.Resource]) error {
  47. appBuild := &porterAppBuild{}
  48. appDeploy := &porterAppDeploy{}
  49. buildNode := resource.GetValue().Build.GetRawYAMLNode()
  50. deployNode := resource.GetValue().Deploy.GetRawYAMLNode()
  51. err := buildNode.Decode(appBuild)
  52. if err != nil {
  53. return err // FIXME: descriptive error
  54. }
  55. err = deployNode.Decode(appDeploy)
  56. if err != nil {
  57. return err // FIXME: descriptive error
  58. }
  59. var buildConfig *types.Build
  60. if appBuild.Ref != "" {
  61. for _, b := range d.Builds {
  62. if b.Name.GetValue() == appBuild.Ref {
  63. buildConfig = b
  64. break
  65. }
  66. }
  67. if buildConfig == nil {
  68. // this should not happen
  69. return fmt.Errorf("internal error: please let the Porter team know about this and quote the following "+
  70. "error:\n-----\nERROR: invalid build ref given for app '%s'", resource.GetValue().Name.GetValue())
  71. }
  72. } else {
  73. buildConfig = appBuild.Build
  74. }
  75. if buildConfig == nil {
  76. // this should not happen
  77. return fmt.Errorf("internal error: please let the Porter team know about this and quote the following "+
  78. "error:\n-----\nERROR: neither build ref nor build body given for app '%s'", resource.GetValue().Name.GetValue())
  79. }
  80. if resource.GetValue().Type.GetValue() == "job" {
  81. jobConfig := &porterJob{}
  82. jobNode := resource.GetRawYAMLNode()
  83. err := jobNode.Decode(jobConfig)
  84. if err != nil {
  85. return err // FIXME: descriptive error
  86. }
  87. return d.applyJob(resource, buildConfig, appDeploy, jobConfig)
  88. } else if oneOf(resource.GetValue().Type.GetValue(), "web", "worker") {
  89. } else {
  90. // this should not happen
  91. return fmt.Errorf("internal error: please let the Porter team know about this and quote the following "+
  92. "error:\n-----\nERROR: app '%s' is not one of 'web', 'worker', 'job'", resource.GetValue().Name.GetValue())
  93. }
  94. return nil
  95. }
  96. func (d *DefaultDriver) applyAddon(resource *types.YAMLNode[*types.Resource]) error {
  97. return nil
  98. }
  99. func (d *DefaultDriver) applyJob(
  100. resource *types.YAMLNode[*types.Resource],
  101. buildConfig *types.Build,
  102. appDeploy *porterAppDeploy,
  103. jobConfig *porterJob,
  104. ) error {
  105. _, err := d.APIClient.GetRelease(
  106. context.Background(),
  107. config.GetCLIConfig().Project,
  108. config.GetCLIConfig().Cluster,
  109. d.Namespace,
  110. resource.GetValue().Name.GetValue(),
  111. )
  112. exists := err == nil
  113. flattenedBuildEnv := make(map[string]string)
  114. for k, v := range buildConfig.Env {
  115. flattenedBuildEnv[k.GetValue()] = v.GetValue()
  116. }
  117. var flattenedBuildEnvGroup []apiTypes.EnvGroupMeta
  118. for _, egName := range buildConfig.EnvGroups {
  119. flattenedBuildEnvGroup = append(flattenedBuildEnvGroup, apiTypes.EnvGroupMeta{
  120. Name: egName.GetValue(),
  121. Namespace: d.Namespace,
  122. })
  123. }
  124. sharedOpts := &deploy.SharedOpts{
  125. ProjectID: config.GetCLIConfig().Project,
  126. ClusterID: config.GetCLIConfig().Cluster,
  127. Namespace: d.Namespace,
  128. LocalPath: buildConfig.Context.GetValue(),
  129. LocalDockerfile: buildConfig.Dockerfile.GetValue(),
  130. // OverrideTag
  131. Method: deploy.DeployBuildType(buildConfig.Method.GetValue()),
  132. AdditionalEnv: flattenedBuildEnv,
  133. EnvGroups: flattenedBuildEnvGroup,
  134. // UseCache
  135. }
  136. if exists {
  137. if jobConfig.Once {
  138. // since the job already exists and was marked 'once', simply return
  139. return nil
  140. }
  141. updateAgent, err := deploy.NewDeployAgent(d.APIClient, resource.GetValue().Name.GetValue(), &deploy.DeployOpts{
  142. SharedOpts: sharedOpts,
  143. Local: buildConfig.Method.GetValue() != "registry",
  144. })
  145. if err != nil {
  146. return fmt.Errorf("[porter.yaml v2][app:%s] error creating deploy agent to update app: %w",
  147. resource.GetValue().Name.GetValue(), err)
  148. }
  149. // if the build method is registry, we do not trigger a build
  150. if buildConfig.Method.GetValue() != "registry" {
  151. buildEnv, err := updateAgent.GetBuildEnv(&deploy.GetBuildEnvOpts{
  152. UseNewConfig: true,
  153. // NewConfig: appConf.Values,
  154. })
  155. if err != nil {
  156. return err // FIXME
  157. }
  158. err = updateAgent.SetBuildEnv(buildEnv)
  159. if err != nil {
  160. return err // FIXME
  161. }
  162. var bc *apiTypes.BuildConfig
  163. if buildConfig.Method.GetValue() == "pack" {
  164. // FIXME: temporary fix
  165. var bp []string
  166. for _, b := range buildConfig.Buildpacks {
  167. bp = append(bp, b.GetValue())
  168. }
  169. bc = &apiTypes.BuildConfig{
  170. Builder: buildConfig.Builder.GetValue(),
  171. Buildpacks: bp,
  172. }
  173. }
  174. err = updateAgent.Build(bc)
  175. if err != nil {
  176. return err // FIXME
  177. }
  178. // if !appConf.Build.UseCache { // FIXME
  179. err = updateAgent.Push()
  180. if err != nil {
  181. return err // FIXME
  182. }
  183. // }
  184. }
  185. // err = updateAgent.UpdateImageAndValues(appConf.Values) // FIXME
  186. // if err != nil {
  187. // return err // FIXME
  188. // }
  189. } else { // create the job
  190. // attempt to get repo suffix from environment variables
  191. var repoSuffix string
  192. if repoName := os.Getenv("PORTER_REPO_NAME"); repoName != "" {
  193. if repoOwner := os.Getenv("PORTER_REPO_OWNER"); repoOwner != "" {
  194. repoSuffix = strings.ToLower(strings.ReplaceAll(fmt.Sprintf("%s-%s", repoOwner, repoName), "_", "-"))
  195. }
  196. }
  197. createAgent := &deploy.CreateAgent{
  198. Client: d.APIClient,
  199. CreateOpts: &deploy.CreateOpts{
  200. SharedOpts: sharedOpts,
  201. Kind: resource.GetValue().Type.GetValue(),
  202. ReleaseName: resource.GetValue().Name.GetValue(),
  203. // RegistryURL: registryURL, // FIXME: best way to get this ??
  204. RepoSuffix: repoSuffix,
  205. },
  206. }
  207. if buildConfig.Method.GetValue() == "registry" {
  208. flattenedDeployMap := make(map[string]any)
  209. for k, v := range resource.GetValue().Deploy.GetValue() {
  210. flattenedDeployMap[k.GetValue()] = v.GetValue()
  211. }
  212. values := &porterWebChartValues{}
  213. // delete the aliases from the deploy section
  214. delete(flattenedDeployMap, "command")
  215. delete(flattenedDeployMap, "cpu")
  216. delete(flattenedDeployMap, "memory")
  217. // replace alias values to the original expect yaml values
  218. values.Container.Command = appDeploy.Command
  219. values.Container.Env.Build = flattenedBuildEnv
  220. values.Container.Env.Normal = appDeploy.Env
  221. // values.Container.Env.Synced
  222. values.Resources.Requests.CPU = appDeploy.CPU
  223. values.Resources.Requests.Memory = appDeploy.Memory
  224. if len(appDeploy.Hosts) > 0 {
  225. values.Ingress.CustomDomain = true
  226. values.Ingress.Hosts = appDeploy.Hosts
  227. }
  228. overrideValues := make(map[string]any)
  229. err = mapstructure.Decode(values, &overrideValues)
  230. if err != nil {
  231. return err // FIXME
  232. }
  233. _, err := createAgent.CreateFromRegistry("", nil)
  234. if err != nil {
  235. return fmt.Errorf("[porter.yaml v2][app:%s] error creating job: %w", resource.GetValue().Name.GetValue(), err)
  236. }
  237. } else if oneOf(buildConfig.Method.GetValue(), "pack", "docker") {
  238. _, err := createAgent.CreateFromDocker(nil, "", nil)
  239. if err != nil {
  240. return fmt.Errorf("[porter.yaml v2][app:%s] error creating job: %w", resource.GetValue().Name.GetValue(), err)
  241. }
  242. } else {
  243. // this should not happen
  244. return fmt.Errorf("internal error: please let the Porter team know about this and quote the following "+
  245. "error:\n-----\nERROR: build method was not one of 'pack', 'docker', 'registry' for app '%s'",
  246. resource.GetValue().Name.GetValue())
  247. }
  248. }
  249. return nil
  250. }