terraform.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. package tools
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "os/exec"
  8. "github.com/hashicorp/go-version"
  9. "github.com/hashicorp/hc-install/product"
  10. "github.com/hashicorp/hc-install/releases"
  11. "github.com/hashicorp/terraform-exec/tfexec"
  12. tfjson "github.com/hashicorp/terraform-json"
  13. )
  14. func Terraform(workingDir string) *terraform {
  15. return &terraform{
  16. workingDir: workingDir,
  17. version: "1.14.5",
  18. logger: tfLogParser{},
  19. vars: []string{},
  20. }
  21. }
  22. type (
  23. terraform struct {
  24. tf *tfexec.Terraform
  25. state *tfjson.State
  26. workingDir string
  27. version string
  28. logger io.Writer
  29. vars []string
  30. }
  31. tfLogParser struct{}
  32. )
  33. func (_ tfLogParser) Write(p []byte) (n int, err error) {
  34. var logEntry tfjson.LogMessage
  35. err = json.Unmarshal(p, &logEntry)
  36. if err != nil {
  37. return 0, err
  38. }
  39. msg := []byte(fmt.Sprintf("[%s] %s\n", logEntry.Level(), logEntry.Message()))
  40. log.Write(msg)
  41. return len(msg), nil
  42. }
  43. func (t *terraform) VarsFile(path string) *terraform {
  44. t.vars = append(t.vars, path)
  45. return t
  46. }
  47. func (t *terraform) Version(version string) *terraform {
  48. t.version = version
  49. return t
  50. }
  51. func (t *terraform) Init() error {
  52. var err error
  53. var execPath = ""
  54. var install = true
  55. execPath, err = exec.LookPath("terraform")
  56. if err == nil {
  57. t.tf, err = tfexec.NewTerraform(t.workingDir, execPath)
  58. if err == nil {
  59. v, _, err := t.tf.Version(context.Background(), true)
  60. if err == nil {
  61. if v.String() == t.version {
  62. install = false
  63. }
  64. }
  65. }
  66. }
  67. if install {
  68. installer := &releases.ExactVersion{
  69. Product: product.Terraform,
  70. Version: version.Must(version.NewVersion(t.version)),
  71. }
  72. execPath, err = installer.Install(context.Background())
  73. if err != nil {
  74. return err
  75. }
  76. }
  77. t.tf, err = tfexec.NewTerraform(t.workingDir, execPath)
  78. if err != nil {
  79. return err
  80. }
  81. err = t.tf.InitJSON(context.Background(),
  82. t.logger,
  83. tfexec.Upgrade(true),
  84. )
  85. if err != nil {
  86. return err
  87. }
  88. t.state, err = t.tf.Show(context.Background())
  89. if err != nil {
  90. return err
  91. }
  92. return nil
  93. }
  94. func (t *terraform) Plan() (*tfjson.Plan, error) {
  95. if t.tf == nil {
  96. err := t.Init()
  97. if err != nil {
  98. return nil, err
  99. }
  100. }
  101. opts := []tfexec.PlanOption{
  102. tfexec.Out("terraform.plan"),
  103. }
  104. for _, f := range t.vars {
  105. tfexec.VarFile(f)
  106. }
  107. _, err := t.tf.PlanJSON(context.Background(),
  108. t.logger,
  109. opts...,
  110. )
  111. if err != nil {
  112. return nil, err
  113. }
  114. plan, err := t.tf.ShowPlanFile(context.Background(), t.workingDir+"/terraform.plan")
  115. if err != nil {
  116. return nil, err
  117. }
  118. return plan, nil
  119. }
  120. func (t *terraform) Apply() error {
  121. if t.tf == nil {
  122. err := t.Init()
  123. if err != nil {
  124. return err
  125. }
  126. }
  127. err := t.tf.ApplyJSON(context.Background(), t.logger)
  128. if err != nil {
  129. return err
  130. }
  131. t.state, err = t.tf.Show(context.Background())
  132. if err != nil {
  133. return err
  134. }
  135. return nil
  136. }
  137. func (t *terraform) Show() error {
  138. if t.tf == nil {
  139. err := t.Init()
  140. if err != nil {
  141. return err
  142. }
  143. }
  144. var err error
  145. t.state, err = t.tf.Show(context.Background())
  146. if err != nil {
  147. return err
  148. }
  149. return nil
  150. }
  151. func (t *terraform) Outputs() map[string]any {
  152. outputs := map[string]any{}
  153. if t.state != nil {
  154. if t.state.Values != nil {
  155. if t.state.Values.Outputs != nil {
  156. for k, v := range t.state.Values.Outputs {
  157. outputs[k] = v.Value
  158. }
  159. }
  160. }
  161. }
  162. return outputs
  163. }
  164. func (t *terraform) State() *tfjson.StateModule {
  165. if t.state != nil {
  166. if t.state.Values != nil {
  167. return t.state.Values.RootModule
  168. }
  169. }
  170. return nil
  171. }
  172. func (t *terraform) Destroy() error {
  173. if t.tf == nil {
  174. err := t.Init()
  175. if err != nil {
  176. return err
  177. }
  178. }
  179. err := t.tf.DestroyJSON(context.Background(), t.logger)
  180. if err != nil {
  181. return err
  182. }
  183. t.state, err = t.tf.Show(context.Background())
  184. if err != nil {
  185. return err
  186. }
  187. return nil
  188. }