cli: Terraform migrations on upgrade (#1685)

* add terraform planning

* overwrite terraform files in upgrade workspace

* Revert "overwrite terraform files in upgrade workspace"

This reverts commit 8bdacfb8bef23ef2cdbdb06bad0855b3bbc42df0.

* prepare terraform workspace

* test upgrade integration

* print upgrade abort

* rename plan file

* write output to file

* add show plan test

* add upgrade tf workdir

* fix workspace preparing

* squash to 1 command

* test

* bazel build

* plan test

* register flag manually

* bazel tidy

* fix linter

* remove MAA variable

* fix workdir

* accept tf variables

* variable fetching

* fix resource indices

* accept Terraform targets

* refactor upgrade command

* Terraform migration apply unit test

* pass down image fetcher to test

* use new flags in e2e test

* move file name to constant

* update buildfiles

* fix version constant

* conditionally create MAA

* move interface down

* upgrade dir

* update buildfiles

* fix interface

* fix createMAA check

* fix imports

* update buildfiles

* wip: workspace backup

* copy utils

* backup upgrade workspace

* remove debug print

* replace old state after upgrade

* check if flag exists

* prepare test workspace

* remove prefix

Co-authored-by: Otto Bittner <cobittner@posteo.net>

* respect file permissions

* refactor tf upgrader

* check workspace before upgrades

* remove temp upgrade dir after completion

* clean up workspace after abortion

* fix upgrade apply test

* fix linter

---------

Co-authored-by: Otto Bittner <cobittner@posteo.net>
This commit is contained in:
Moritz Sanft 2023-05-22 13:31:20 +02:00 committed by GitHub
parent 339e750c18
commit c69e6777bd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 1391 additions and 44 deletions

View file

@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
package terraform
import (
"bytes"
"context"
"errors"
"io/fs"
@ -934,14 +935,143 @@ func TestLogLevelString(t *testing.T) {
}
}
func TestPlan(t *testing.T) {
someError := errors.New("some error")
testCases := map[string]struct {
pathBase string
tf *stubTerraform
fs afero.Fs
wantErr bool
}{
"plan succeeds": {
pathBase: "terraform",
tf: &stubTerraform{},
fs: afero.NewMemMapFs(),
},
"set log path fails": {
pathBase: "terraform",
tf: &stubTerraform{
setLogPathErr: someError,
},
fs: afero.NewMemMapFs(),
wantErr: true,
},
"set log fails": {
pathBase: "terraform",
tf: &stubTerraform{
setLogErr: someError,
},
fs: afero.NewMemMapFs(),
wantErr: true,
},
"plan fails": {
pathBase: "terraform",
tf: &stubTerraform{
planJSONErr: someError,
},
fs: afero.NewMemMapFs(),
wantErr: true,
},
"init fails": {
pathBase: "terraform",
tf: &stubTerraform{
initErr: someError,
},
fs: afero.NewMemMapFs(),
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
require := require.New(t)
c := &Client{
file: file.NewHandler(tc.fs),
tf: tc.tf,
workingDir: tc.pathBase,
}
_, err := c.Plan(context.Background(), LogLevelDebug, constants.TerraformUpgradePlanFile)
if tc.wantErr {
require.Error(err)
} else {
require.NoError(err)
}
})
}
}
func TestShowPlan(t *testing.T) {
someError := errors.New("some error")
testCases := map[string]struct {
pathBase string
tf *stubTerraform
fs afero.Fs
wantErr bool
}{
"show plan succeeds": {
pathBase: "terraform",
tf: &stubTerraform{},
fs: afero.NewMemMapFs(),
},
"set log path fails": {
pathBase: "terraform",
tf: &stubTerraform{
setLogPathErr: someError,
},
fs: afero.NewMemMapFs(),
wantErr: true,
},
"set log fails": {
pathBase: "terraform",
tf: &stubTerraform{
setLogErr: someError,
},
fs: afero.NewMemMapFs(),
wantErr: true,
},
"show plan file fails": {
pathBase: "terraform",
tf: &stubTerraform{
showPlanFileErr: someError,
},
fs: afero.NewMemMapFs(),
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
require := require.New(t)
c := &Client{
file: file.NewHandler(tc.fs),
tf: tc.tf,
workingDir: tc.pathBase,
}
err := c.ShowPlan(context.Background(), LogLevelDebug, "", bytes.NewBuffer(nil))
if tc.wantErr {
require.Error(err)
} else {
require.NoError(err)
}
})
}
}
type stubTerraform struct {
applyErr error
destroyErr error
initErr error
showErr error
setLogErr error
setLogPathErr error
showState *tfjson.State
applyErr error
destroyErr error
initErr error
showErr error
setLogErr error
setLogPathErr error
planJSONErr error
showPlanFileErr error
showState *tfjson.State
}
func (s *stubTerraform) Apply(context.Context, ...tfexec.ApplyOption) error {
@ -960,6 +1090,14 @@ func (s *stubTerraform) Show(context.Context, ...tfexec.ShowOption) (*tfjson.Sta
return s.showState, s.showErr
}
func (s *stubTerraform) Plan(context.Context, ...tfexec.PlanOption) (bool, error) {
return false, s.planJSONErr
}
func (s *stubTerraform) ShowPlanFileRaw(context.Context, string, ...tfexec.ShowOption) (string, error) {
return "", s.showPlanFileErr
}
func (s *stubTerraform) SetLog(_ string) error {
return s.setLogErr
}