mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-26 16:09:45 -05:00
625dc26644
* Unify cloudcmd create and upgrade code * Make libvirt runner code a bit more idempotent --------- Signed-off-by: Daniel Weiße <dw@edgeless.systems>
372 lines
10 KiB
Go
372 lines
10 KiB
Go
/*
|
|
Copyright (c) Edgeless Systems GmbH
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
package cloudcmd
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"io"
|
|
"path/filepath"
|
|
"runtime"
|
|
"testing"
|
|
|
|
"github.com/spf13/afero"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
|
)
|
|
|
|
func TestApplier(t *testing.T) {
|
|
t.Setenv("CONSTELLATION_OPENSTACK_DEV", "1")
|
|
failOnNonAMD64 := (runtime.GOARCH != "amd64") || (runtime.GOOS != "linux")
|
|
ip := "192.0.2.1"
|
|
configWithProvider := func(provider cloudprovider.Provider) *config.Config {
|
|
cfg := config.Default()
|
|
cfg.RemoveProviderAndAttestationExcept(provider)
|
|
return cfg
|
|
}
|
|
|
|
testCases := map[string]struct {
|
|
tfClient tfResourceClient
|
|
newTfClientErr error
|
|
libvirt *stubLibvirtRunner
|
|
provider cloudprovider.Provider
|
|
config *config.Config
|
|
policyPatcher *stubPolicyPatcher
|
|
wantErr bool
|
|
wantRollback bool // Use only together with stubClients.
|
|
wantTerraformRollback bool // When libvirt fails, don't call into Terraform.
|
|
}{
|
|
"gcp": {
|
|
tfClient: &stubTerraformClient{ip: ip},
|
|
provider: cloudprovider.GCP,
|
|
config: configWithProvider(cloudprovider.GCP),
|
|
},
|
|
"gcp create cluster error": {
|
|
tfClient: &stubTerraformClient{applyClusterErr: assert.AnError},
|
|
provider: cloudprovider.GCP,
|
|
config: configWithProvider(cloudprovider.GCP),
|
|
wantErr: true,
|
|
wantRollback: true,
|
|
wantTerraformRollback: true,
|
|
},
|
|
"azure": {
|
|
tfClient: &stubTerraformClient{ip: ip},
|
|
provider: cloudprovider.Azure,
|
|
config: configWithProvider(cloudprovider.Azure),
|
|
policyPatcher: &stubPolicyPatcher{},
|
|
},
|
|
"azure trusted launch": {
|
|
tfClient: &stubTerraformClient{ip: ip},
|
|
provider: cloudprovider.Azure,
|
|
config: func() *config.Config {
|
|
cfg := config.Default()
|
|
cfg.RemoveProviderAndAttestationExcept(cloudprovider.Azure)
|
|
cfg.Attestation = config.AttestationConfig{
|
|
AzureTrustedLaunch: &config.AzureTrustedLaunch{},
|
|
}
|
|
return cfg
|
|
}(),
|
|
policyPatcher: &stubPolicyPatcher{},
|
|
},
|
|
"azure new policy patch error": {
|
|
tfClient: &stubTerraformClient{ip: ip},
|
|
provider: cloudprovider.Azure,
|
|
config: configWithProvider(cloudprovider.Azure),
|
|
policyPatcher: &stubPolicyPatcher{assert.AnError},
|
|
wantErr: true,
|
|
},
|
|
"azure create cluster error": {
|
|
tfClient: &stubTerraformClient{applyClusterErr: assert.AnError},
|
|
provider: cloudprovider.Azure,
|
|
config: configWithProvider(cloudprovider.Azure),
|
|
policyPatcher: &stubPolicyPatcher{},
|
|
wantErr: true,
|
|
wantRollback: true,
|
|
wantTerraformRollback: true,
|
|
},
|
|
"openstack": {
|
|
tfClient: &stubTerraformClient{ip: ip},
|
|
libvirt: &stubLibvirtRunner{},
|
|
provider: cloudprovider.OpenStack,
|
|
config: func() *config.Config {
|
|
cfg := config.Default()
|
|
cfg.RemoveProviderAndAttestationExcept(cloudprovider.OpenStack)
|
|
cfg.Provider.OpenStack.Cloud = "testcloud"
|
|
return cfg
|
|
}(),
|
|
},
|
|
"openstack without clouds.yaml": {
|
|
tfClient: &stubTerraformClient{ip: ip},
|
|
libvirt: &stubLibvirtRunner{},
|
|
provider: cloudprovider.OpenStack,
|
|
config: configWithProvider(cloudprovider.OpenStack),
|
|
wantErr: true,
|
|
},
|
|
"openstack create cluster error": {
|
|
tfClient: &stubTerraformClient{applyClusterErr: assert.AnError},
|
|
libvirt: &stubLibvirtRunner{},
|
|
provider: cloudprovider.OpenStack,
|
|
config: func() *config.Config {
|
|
cfg := config.Default()
|
|
cfg.RemoveProviderAndAttestationExcept(cloudprovider.OpenStack)
|
|
cfg.Provider.OpenStack.Cloud = "testcloud"
|
|
return cfg
|
|
}(),
|
|
wantErr: true,
|
|
wantRollback: true,
|
|
wantTerraformRollback: true,
|
|
},
|
|
"qemu": {
|
|
tfClient: &stubTerraformClient{ip: ip},
|
|
libvirt: &stubLibvirtRunner{},
|
|
provider: cloudprovider.QEMU,
|
|
config: configWithProvider(cloudprovider.QEMU),
|
|
wantErr: failOnNonAMD64,
|
|
},
|
|
"qemu create cluster error": {
|
|
tfClient: &stubTerraformClient{applyClusterErr: assert.AnError},
|
|
libvirt: &stubLibvirtRunner{},
|
|
provider: cloudprovider.QEMU,
|
|
config: configWithProvider(cloudprovider.QEMU),
|
|
wantErr: true,
|
|
wantRollback: !failOnNonAMD64, // if we run on non-AMD64/linux, we don't get to a point where rollback is needed
|
|
wantTerraformRollback: true,
|
|
},
|
|
"qemu start libvirt error": {
|
|
tfClient: &stubTerraformClient{ip: ip},
|
|
libvirt: &stubLibvirtRunner{startErr: assert.AnError},
|
|
provider: cloudprovider.QEMU,
|
|
config: configWithProvider(cloudprovider.QEMU),
|
|
wantRollback: !failOnNonAMD64,
|
|
wantTerraformRollback: false,
|
|
wantErr: true,
|
|
},
|
|
"unknown provider": {
|
|
tfClient: &stubTerraformClient{},
|
|
provider: cloudprovider.Unknown,
|
|
config: func() *config.Config {
|
|
cfg := config.Default()
|
|
cfg.RemoveProviderAndAttestationExcept(cloudprovider.AWS)
|
|
cfg.Provider.AWS = nil
|
|
return cfg
|
|
}(),
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for name, tc := range testCases {
|
|
t.Run(name, func(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
applier := &Applier{
|
|
fileHandler: file.NewHandler(afero.NewMemMapFs()),
|
|
imageFetcher: &stubImageFetcher{
|
|
reference: "some-image",
|
|
},
|
|
terraformClient: tc.tfClient,
|
|
libvirtRunner: tc.libvirt,
|
|
rawDownloader: &stubRawDownloader{
|
|
destination: "some-destination",
|
|
},
|
|
policyPatcher: tc.policyPatcher,
|
|
logLevel: terraform.LogLevelNone,
|
|
workingDir: "test",
|
|
backupDir: "test-backup",
|
|
out: &bytes.Buffer{},
|
|
}
|
|
|
|
diff, err := applier.Plan(context.Background(), tc.config)
|
|
if err != nil {
|
|
assert.True(tc.wantErr, "unexpected error: %s", err)
|
|
return
|
|
}
|
|
assert.False(diff)
|
|
|
|
idFile, err := applier.Apply(context.Background(), tc.provider, true)
|
|
|
|
if tc.wantErr {
|
|
assert.Error(err)
|
|
if tc.wantRollback {
|
|
cl := tc.tfClient.(*stubTerraformClient)
|
|
if tc.wantTerraformRollback {
|
|
assert.True(cl.destroyCalled)
|
|
}
|
|
assert.True(cl.cleanUpWorkspaceCalled)
|
|
if tc.provider == cloudprovider.QEMU {
|
|
assert.True(tc.libvirt.stopCalled)
|
|
}
|
|
}
|
|
} else {
|
|
assert.NoError(err)
|
|
assert.Equal(ip, idFile.ClusterEndpoint)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPlan(t *testing.T) {
|
|
setUpFilesystem := func(existingFiles []string) file.Handler {
|
|
fs := file.NewHandler(afero.NewMemMapFs())
|
|
require.NoError(t, fs.Write("test/terraform.tfstate", []byte{}, file.OptMkdirAll))
|
|
for _, f := range existingFiles {
|
|
require.NoError(t, fs.Write(f, []byte{}))
|
|
}
|
|
return fs
|
|
}
|
|
|
|
testCases := map[string]struct {
|
|
upgradeID string
|
|
tf *stubTerraformClient
|
|
fs file.Handler
|
|
want bool
|
|
wantErr bool
|
|
}{
|
|
"success no diff": {
|
|
upgradeID: "1234",
|
|
tf: &stubTerraformClient{},
|
|
fs: setUpFilesystem([]string{}),
|
|
},
|
|
"success diff": {
|
|
upgradeID: "1234",
|
|
tf: &stubTerraformClient{
|
|
planDiff: true,
|
|
},
|
|
fs: setUpFilesystem([]string{}),
|
|
want: true,
|
|
},
|
|
"prepare workspace error": {
|
|
upgradeID: "1234",
|
|
tf: &stubTerraformClient{
|
|
prepareWorkspaceErr: assert.AnError,
|
|
},
|
|
fs: setUpFilesystem([]string{}),
|
|
wantErr: true,
|
|
},
|
|
"plan error": {
|
|
tf: &stubTerraformClient{
|
|
planErr: assert.AnError,
|
|
},
|
|
fs: setUpFilesystem([]string{}),
|
|
wantErr: true,
|
|
},
|
|
"show plan error no diff": {
|
|
upgradeID: "1234",
|
|
tf: &stubTerraformClient{
|
|
showPlanErr: assert.AnError,
|
|
},
|
|
fs: setUpFilesystem([]string{}),
|
|
},
|
|
"show plan error diff": {
|
|
upgradeID: "1234",
|
|
tf: &stubTerraformClient{
|
|
showPlanErr: assert.AnError,
|
|
planDiff: true,
|
|
},
|
|
fs: setUpFilesystem([]string{}),
|
|
wantErr: true,
|
|
},
|
|
"workspace not clean": {
|
|
upgradeID: "1234",
|
|
tf: &stubTerraformClient{},
|
|
fs: setUpFilesystem([]string{filepath.Join(constants.UpgradeDir, "1234", constants.TerraformUpgradeBackupDir)}),
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for name, tc := range testCases {
|
|
t.Run(name, func(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
u := &Applier{
|
|
terraformClient: tc.tf,
|
|
policyPatcher: stubPolicyPatcher{},
|
|
fileHandler: tc.fs,
|
|
imageFetcher: &stubImageFetcher{reference: "some-image"},
|
|
rawDownloader: &stubRawDownloader{destination: "some-destination"},
|
|
libvirtRunner: &stubLibvirtRunner{},
|
|
logLevel: terraform.LogLevelDebug,
|
|
backupDir: filepath.Join(constants.UpgradeDir, tc.upgradeID),
|
|
workingDir: "test",
|
|
out: io.Discard,
|
|
}
|
|
|
|
cfg := config.Default()
|
|
cfg.RemoveProviderAndAttestationExcept(cloudprovider.QEMU)
|
|
|
|
diff, err := u.Plan(context.Background(), cfg)
|
|
if tc.wantErr {
|
|
require.Error(err)
|
|
} else {
|
|
require.NoError(err)
|
|
require.Equal(tc.want, diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestApply(t *testing.T) {
|
|
testCases := map[string]struct {
|
|
upgradeID string
|
|
tf *stubTerraformClient
|
|
policyPatcher stubPolicyPatcher
|
|
fs file.Handler
|
|
wantErr bool
|
|
}{
|
|
"success": {
|
|
upgradeID: "1234",
|
|
tf: &stubTerraformClient{},
|
|
policyPatcher: stubPolicyPatcher{},
|
|
},
|
|
"apply error": {
|
|
upgradeID: "1234",
|
|
tf: &stubTerraformClient{
|
|
applyClusterErr: assert.AnError,
|
|
},
|
|
policyPatcher: stubPolicyPatcher{},
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for name, tc := range testCases {
|
|
t.Run(name, func(t *testing.T) {
|
|
assert := require.New(t)
|
|
|
|
u := &Applier{
|
|
terraformClient: tc.tf,
|
|
logLevel: terraform.LogLevelDebug,
|
|
libvirtRunner: &stubLibvirtRunner{},
|
|
policyPatcher: stubPolicyPatcher{},
|
|
fileHandler: tc.fs,
|
|
backupDir: filepath.Join(constants.UpgradeDir, tc.upgradeID),
|
|
workingDir: "test",
|
|
out: io.Discard,
|
|
}
|
|
|
|
_, err := u.Apply(context.Background(), cloudprovider.QEMU, WithoutRollbackOnError)
|
|
if tc.wantErr {
|
|
assert.Error(err)
|
|
} else {
|
|
assert.NoError(err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type stubPolicyPatcher struct {
|
|
patchErr error
|
|
}
|
|
|
|
func (s stubPolicyPatcher) Patch(_ context.Context, _ string) error {
|
|
return s.patchErr
|
|
}
|