mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-12-15 16:09:39 -05:00
cli: iam destroy (#946)
This commit is contained in:
parent
f1b331bbbd
commit
5137e9fa57
11 changed files with 659 additions and 9 deletions
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
tfjson "github.com/hashicorp/terraform-json"
|
||||
)
|
||||
|
||||
type terraformClient interface {
|
||||
|
|
@ -22,6 +23,7 @@ type terraformClient interface {
|
|||
Destroy(ctx context.Context) error
|
||||
CleanUpWorkspace() error
|
||||
RemoveInstaller()
|
||||
Show(ctx context.Context) (*tfjson.State, error)
|
||||
}
|
||||
|
||||
type libvirtRunner interface {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
tfjson "github.com/hashicorp/terraform-json"
|
||||
|
||||
"go.uber.org/goleak"
|
||||
)
|
||||
|
|
@ -30,14 +31,17 @@ type stubTerraformClient struct {
|
|||
initSecret string
|
||||
iamOutput terraform.IAMOutput
|
||||
uid string
|
||||
tfjsonState *tfjson.State
|
||||
cleanUpWorkspaceCalled bool
|
||||
removeInstallerCalled bool
|
||||
destroyCalled bool
|
||||
showCalled bool
|
||||
createClusterErr error
|
||||
destroyErr error
|
||||
prepareWorkspaceErr error
|
||||
cleanUpWorkspaceErr error
|
||||
iamOutputErr error
|
||||
showErr error
|
||||
}
|
||||
|
||||
func (c *stubTerraformClient) CreateCluster(ctx context.Context) (terraform.CreateOutput, error) {
|
||||
|
|
@ -70,6 +74,11 @@ func (c *stubTerraformClient) RemoveInstaller() {
|
|||
c.removeInstallerCalled = true
|
||||
}
|
||||
|
||||
func (c *stubTerraformClient) Show(ctx context.Context) (*tfjson.State, error) {
|
||||
c.showCalled = true
|
||||
return c.tfjsonState, c.showErr
|
||||
}
|
||||
|
||||
type stubLibvirtRunner struct {
|
||||
startCalled bool
|
||||
stopCalled bool
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ package cloudcmd
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
|
|
@ -15,9 +18,65 @@ import (
|
|||
"github.com/edgelesssys/constellation/v2/cli/internal/iamid"
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/gcpshared"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
)
|
||||
|
||||
// IAMDestroyer destroys an IAM configuration.
|
||||
type IAMDestroyer struct {
|
||||
client terraformClient
|
||||
}
|
||||
|
||||
// NewIAMDestroyer creates a new IAM Destroyer.
|
||||
func NewIAMDestroyer(ctx context.Context) (*IAMDestroyer, error) {
|
||||
cl, err := terraform.New(ctx, constants.TerraformIAMWorkingDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &IAMDestroyer{client: cl}, nil
|
||||
}
|
||||
|
||||
// GetTfstateServiceAccountKey returns the sa_key output from the terraform state.
|
||||
func (d *IAMDestroyer) GetTfstateServiceAccountKey(ctx context.Context) (gcpshared.ServiceAccountKey, error) {
|
||||
tfState, err := d.client.Show(ctx)
|
||||
if err != nil {
|
||||
return gcpshared.ServiceAccountKey{}, err
|
||||
}
|
||||
|
||||
if tfState.Values == nil {
|
||||
return gcpshared.ServiceAccountKey{}, errors.New("no Values field in terraform state")
|
||||
}
|
||||
|
||||
saKeyJSON := tfState.Values.Outputs["sa_key"]
|
||||
if saKeyJSON == nil {
|
||||
return gcpshared.ServiceAccountKey{}, errors.New("no sa_key in outputs of the terraform state")
|
||||
}
|
||||
|
||||
saKeyString, ok := saKeyJSON.Value.(string)
|
||||
if !ok {
|
||||
return gcpshared.ServiceAccountKey{}, errors.New("sa_key field in terraform state is not a string")
|
||||
}
|
||||
saKey, err := base64.StdEncoding.DecodeString(saKeyString)
|
||||
if err != nil {
|
||||
return gcpshared.ServiceAccountKey{}, err
|
||||
}
|
||||
|
||||
var tfSaKey gcpshared.ServiceAccountKey
|
||||
if err := json.Unmarshal(saKey, &tfSaKey); err != nil {
|
||||
return gcpshared.ServiceAccountKey{}, err
|
||||
}
|
||||
|
||||
return tfSaKey, nil
|
||||
}
|
||||
|
||||
// DestroyIAMConfiguration destroys the previously created IAM configuration and deletes the local IAM terraform files.
|
||||
func (d *IAMDestroyer) DestroyIAMConfiguration(ctx context.Context) error {
|
||||
if err := d.client.Destroy(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return d.client.CleanUpWorkspace()
|
||||
}
|
||||
|
||||
// IAMCreator creates the IAM configuration on the cloud provider.
|
||||
type IAMCreator struct {
|
||||
out io.Writer
|
||||
|
|
|
|||
|
|
@ -8,13 +8,18 @@ package cloudcmd
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/iamid"
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/gcpshared"
|
||||
tfjson "github.com/hashicorp/terraform-json"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestIAMCreator(t *testing.T) {
|
||||
|
|
@ -148,3 +153,186 @@ func TestIAMCreator(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDestroyIAMConfiguration(t *testing.T) {
|
||||
newError := func() error {
|
||||
return errors.New("failed")
|
||||
}
|
||||
|
||||
testCases := map[string]struct {
|
||||
tfClient *stubTerraformClient
|
||||
wantErr bool
|
||||
wantDestroyCalled bool
|
||||
wantCleanupWorkspaceCalled bool
|
||||
}{
|
||||
"destroy error": {
|
||||
tfClient: &stubTerraformClient{destroyErr: newError()},
|
||||
wantErr: true,
|
||||
wantDestroyCalled: true,
|
||||
},
|
||||
"destroy": {
|
||||
tfClient: &stubTerraformClient{},
|
||||
wantDestroyCalled: true,
|
||||
wantCleanupWorkspaceCalled: true,
|
||||
},
|
||||
"cleanup error": {
|
||||
tfClient: &stubTerraformClient{cleanUpWorkspaceErr: newError()},
|
||||
wantErr: true,
|
||||
wantDestroyCalled: true,
|
||||
wantCleanupWorkspaceCalled: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
destroyer := &IAMDestroyer{client: tc.tfClient}
|
||||
|
||||
err := destroyer.DestroyIAMConfiguration(context.Background())
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
}
|
||||
|
||||
assert.Equal(tc.wantDestroyCalled, tc.tfClient.destroyCalled)
|
||||
assert.Equal(tc.wantCleanupWorkspaceCalled, tc.tfClient.cleanUpWorkspaceCalled)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTfstateServiceAccountKey(t *testing.T) {
|
||||
someError := errors.New("failed")
|
||||
|
||||
gcpFile := `
|
||||
{
|
||||
"auth_provider_x509_cert_url": "",
|
||||
"auth_uri": "",
|
||||
"client_email": "",
|
||||
"client_id": "",
|
||||
"client_x509_cert_url": "",
|
||||
"private_key": "",
|
||||
"private_key_id": "",
|
||||
"project_id": "",
|
||||
"token_uri": "",
|
||||
"type": ""
|
||||
}
|
||||
`
|
||||
gcpFileB64 := base64.StdEncoding.EncodeToString([]byte(gcpFile))
|
||||
|
||||
testCases := map[string]struct {
|
||||
cl *stubTerraformClient
|
||||
wantValidSaKey bool
|
||||
wantErr bool
|
||||
wantShowCalled bool
|
||||
}{
|
||||
"valid": {
|
||||
cl: &stubTerraformClient{
|
||||
tfjsonState: &tfjson.State{
|
||||
Values: &tfjson.StateValues{
|
||||
Outputs: map[string]*tfjson.StateOutput{
|
||||
"sa_key": {
|
||||
Value: gcpFileB64,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantValidSaKey: true,
|
||||
wantShowCalled: true,
|
||||
},
|
||||
"show error": {
|
||||
cl: &stubTerraformClient{
|
||||
showErr: someError,
|
||||
},
|
||||
wantErr: true,
|
||||
wantShowCalled: true,
|
||||
},
|
||||
"nil tfstate values": {
|
||||
cl: &stubTerraformClient{
|
||||
tfjsonState: &tfjson.State{
|
||||
Values: nil,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantShowCalled: true,
|
||||
},
|
||||
"no key": {
|
||||
cl: &stubTerraformClient{
|
||||
tfjsonState: &tfjson.State{
|
||||
Values: &tfjson.StateValues{},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantShowCalled: true,
|
||||
},
|
||||
"invalid base64": {
|
||||
cl: &stubTerraformClient{
|
||||
tfjsonState: &tfjson.State{
|
||||
Values: &tfjson.StateValues{
|
||||
Outputs: map[string]*tfjson.StateOutput{
|
||||
"sa_key": {
|
||||
Value: "iamnotvalid",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantShowCalled: true,
|
||||
},
|
||||
"valid base64 invalid json": {
|
||||
cl: &stubTerraformClient{
|
||||
tfjsonState: &tfjson.State{
|
||||
Values: &tfjson.StateValues{
|
||||
Outputs: map[string]*tfjson.StateOutput{
|
||||
"sa_key": {
|
||||
Value: base64.StdEncoding.EncodeToString([]byte("asdf")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantShowCalled: true,
|
||||
},
|
||||
"not string": {
|
||||
cl: &stubTerraformClient{
|
||||
tfjsonState: &tfjson.State{
|
||||
Values: &tfjson.StateValues{
|
||||
Outputs: map[string]*tfjson.StateOutput{
|
||||
"sa_key": {
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantShowCalled: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
destroyer := IAMDestroyer{client: tc.cl}
|
||||
|
||||
saKey, err := destroyer.GetTfstateServiceAccountKey(context.Background())
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
assert.NoError(err)
|
||||
|
||||
var saKeyComp gcpshared.ServiceAccountKey
|
||||
require.NoError(t, json.Unmarshal([]byte(gcpFile), &saKeyComp))
|
||||
|
||||
assert.Equal(saKey, saKeyComp)
|
||||
assert.Equal(tc.wantShowCalled, tc.cl.showCalled)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue