mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-01-13 00:19:32 -05:00
Remove state file
This commit is contained in:
parent
0d1fd8fb2a
commit
1556e239ca
@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Removed
|
### Removed
|
||||||
<!-- For now removed features. -->
|
<!-- For now removed features. -->
|
||||||
- `endpoint` flag of `constellation init`. IP is now always taken from the `constellation-id.json` file.
|
- `endpoint` flag of `constellation init`. IP is now always taken from the `constellation-id.json` file.
|
||||||
|
- `constellation-state.json` file won't be created anymore. Resources are now managed through Terraform.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
@ -10,12 +10,10 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type terraformClient interface {
|
type terraformClient interface {
|
||||||
GetState() state.ConstellationState
|
CreateCluster(ctx context.Context, name string, input terraform.Variables) (string, error)
|
||||||
CreateCluster(ctx context.Context, name string, input terraform.Variables) error
|
|
||||||
DestroyCluster(ctx context.Context) error
|
DestroyCluster(ctx context.Context) error
|
||||||
CleanUpWorkspace() error
|
CleanUpWorkspace() error
|
||||||
RemoveInstaller()
|
RemoveInstaller()
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
|
||||||
"go.uber.org/goleak"
|
"go.uber.org/goleak"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -23,7 +22,7 @@ func TestMain(m *testing.M) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type stubTerraformClient struct {
|
type stubTerraformClient struct {
|
||||||
state state.ConstellationState
|
ip string
|
||||||
cleanUpWorkspaceCalled bool
|
cleanUpWorkspaceCalled bool
|
||||||
removeInstallerCalled bool
|
removeInstallerCalled bool
|
||||||
destroyClusterCalled bool
|
destroyClusterCalled bool
|
||||||
@ -32,12 +31,8 @@ type stubTerraformClient struct {
|
|||||||
cleanUpWorkspaceErr error
|
cleanUpWorkspaceErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *stubTerraformClient) GetState() state.ConstellationState {
|
func (c *stubTerraformClient) CreateCluster(ctx context.Context, name string, input terraform.Variables) (string, error) {
|
||||||
return c.state
|
return c.ip, c.createClusterErr
|
||||||
}
|
|
||||||
|
|
||||||
func (c *stubTerraformClient) CreateCluster(ctx context.Context, name string, input terraform.Variables) error {
|
|
||||||
return c.createClusterErr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *stubTerraformClient) DestroyCluster(ctx context.Context) error {
|
func (c *stubTerraformClient) DestroyCluster(ctx context.Context) error {
|
||||||
|
@ -15,11 +15,11 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
|
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Creator creates cloud resources.
|
// Creator creates cloud resources.
|
||||||
@ -44,41 +44,41 @@ func NewCreator(out io.Writer) *Creator {
|
|||||||
|
|
||||||
// Create creates the handed amount of instances and all the needed resources.
|
// Create creates the handed amount of instances and all the needed resources.
|
||||||
func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, config *config.Config, name, insType string, controlPlaneCount, workerCount int,
|
func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, config *config.Config, name, insType string, controlPlaneCount, workerCount int,
|
||||||
) (state.ConstellationState, error) {
|
) (clusterid.File, error) {
|
||||||
switch provider {
|
switch provider {
|
||||||
case cloudprovider.GCP:
|
case cloudprovider.GCP:
|
||||||
cl, err := c.newTerraformClient(ctx, provider)
|
cl, err := c.newTerraformClient(ctx, provider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return state.ConstellationState{}, err
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
defer cl.RemoveInstaller()
|
defer cl.RemoveInstaller()
|
||||||
return c.createGCP(ctx, cl, config, name, insType, controlPlaneCount, workerCount)
|
return c.createGCP(ctx, cl, config, name, insType, controlPlaneCount, workerCount)
|
||||||
case cloudprovider.Azure:
|
case cloudprovider.Azure:
|
||||||
cl, err := c.newTerraformClient(ctx, provider)
|
cl, err := c.newTerraformClient(ctx, provider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return state.ConstellationState{}, err
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
defer cl.RemoveInstaller()
|
defer cl.RemoveInstaller()
|
||||||
return c.createAzure(ctx, cl, config, name, insType, controlPlaneCount, workerCount)
|
return c.createAzure(ctx, cl, config, name, insType, controlPlaneCount, workerCount)
|
||||||
case cloudprovider.QEMU:
|
case cloudprovider.QEMU:
|
||||||
if runtime.GOARCH != "amd64" || runtime.GOOS != "linux" {
|
if runtime.GOARCH != "amd64" || runtime.GOOS != "linux" {
|
||||||
return state.ConstellationState{}, fmt.Errorf("creation of a QEMU based Constellation is not supported for %s/%s", runtime.GOOS, runtime.GOARCH)
|
return clusterid.File{}, fmt.Errorf("creation of a QEMU based Constellation is not supported for %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
}
|
}
|
||||||
cl, err := c.newTerraformClient(ctx, provider)
|
cl, err := c.newTerraformClient(ctx, provider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return state.ConstellationState{}, err
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
defer cl.RemoveInstaller()
|
defer cl.RemoveInstaller()
|
||||||
lv := c.newLibvirtRunner()
|
lv := c.newLibvirtRunner()
|
||||||
return c.createQEMU(ctx, cl, lv, name, config, controlPlaneCount, workerCount)
|
return c.createQEMU(ctx, cl, lv, name, config, controlPlaneCount, workerCount)
|
||||||
default:
|
default:
|
||||||
return state.ConstellationState{}, fmt.Errorf("unsupported cloud provider: %s", provider)
|
return clusterid.File{}, fmt.Errorf("unsupported cloud provider: %s", provider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Creator) createGCP(ctx context.Context, cl terraformClient, config *config.Config,
|
func (c *Creator) createGCP(ctx context.Context, cl terraformClient, config *config.Config,
|
||||||
name, insType string, controlPlaneCount, workerCount int,
|
name, insType string, controlPlaneCount, workerCount int,
|
||||||
) (stat state.ConstellationState, retErr error) {
|
) (idFile clusterid.File, retErr error) {
|
||||||
defer rollbackOnError(context.Background(), c.out, &retErr, &rollbackerTerraform{client: cl})
|
defer rollbackOnError(context.Background(), c.out, &retErr, &rollbackerTerraform{client: cl})
|
||||||
|
|
||||||
vars := &terraform.GCPVariables{
|
vars := &terraform.GCPVariables{
|
||||||
@ -98,16 +98,20 @@ func (c *Creator) createGCP(ctx context.Context, cl terraformClient, config *con
|
|||||||
Debug: config.IsDebugCluster(),
|
Debug: config.IsDebugCluster(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cl.CreateCluster(ctx, name, vars); err != nil {
|
ip, err := cl.CreateCluster(ctx, name, vars)
|
||||||
return state.ConstellationState{}, err
|
if err != nil {
|
||||||
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return cl.GetState(), nil
|
return clusterid.File{
|
||||||
|
CloudProvider: cloudprovider.GCP,
|
||||||
|
IP: ip,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Creator) createAzure(ctx context.Context, cl terraformClient, config *config.Config,
|
func (c *Creator) createAzure(ctx context.Context, cl terraformClient, config *config.Config,
|
||||||
name, insType string, controlPlaneCount, workerCount int,
|
name, insType string, controlPlaneCount, workerCount int,
|
||||||
) (stat state.ConstellationState, retErr error) {
|
) (idFile clusterid.File, retErr error) {
|
||||||
defer rollbackOnError(context.Background(), c.out, &retErr, &rollbackerTerraform{client: cl})
|
defer rollbackOnError(context.Background(), c.out, &retErr, &rollbackerTerraform{client: cl})
|
||||||
|
|
||||||
vars := &terraform.AzureVariables{
|
vars := &terraform.AzureVariables{
|
||||||
@ -127,16 +131,20 @@ func (c *Creator) createAzure(ctx context.Context, cl terraformClient, config *c
|
|||||||
Debug: config.IsDebugCluster(),
|
Debug: config.IsDebugCluster(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cl.CreateCluster(ctx, name, vars); err != nil {
|
ip, err := cl.CreateCluster(ctx, name, vars)
|
||||||
return state.ConstellationState{}, err
|
if err != nil {
|
||||||
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return cl.GetState(), nil
|
return clusterid.File{
|
||||||
|
CloudProvider: cloudprovider.Azure,
|
||||||
|
IP: ip,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirtRunner, name string, config *config.Config,
|
func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirtRunner, name string, config *config.Config,
|
||||||
controlPlaneCount, workerCount int,
|
controlPlaneCount, workerCount int,
|
||||||
) (stat state.ConstellationState, retErr error) {
|
) (idFile clusterid.File, retErr error) {
|
||||||
defer rollbackOnError(context.Background(), c.out, &retErr, &rollbackerQEMU{client: cl, libvirt: lv})
|
defer rollbackOnError(context.Background(), c.out, &retErr, &rollbackerQEMU{client: cl, libvirt: lv})
|
||||||
|
|
||||||
libvirtURI := config.Provider.QEMU.LibvirtURI
|
libvirtURI := config.Provider.QEMU.LibvirtURI
|
||||||
@ -146,7 +154,7 @@ func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirt
|
|||||||
// if no libvirt URI is specified, start a libvirt container
|
// if no libvirt URI is specified, start a libvirt container
|
||||||
case libvirtURI == "":
|
case libvirtURI == "":
|
||||||
if err := lv.Start(ctx, name, config.Provider.QEMU.LibvirtContainerImage); err != nil {
|
if err := lv.Start(ctx, name, config.Provider.QEMU.LibvirtContainerImage); err != nil {
|
||||||
return state.ConstellationState{}, err
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
libvirtURI = libvirt.LibvirtTCPConnectURI
|
libvirtURI = libvirt.LibvirtTCPConnectURI
|
||||||
|
|
||||||
@ -162,11 +170,11 @@ func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirt
|
|||||||
case strings.HasPrefix(libvirtURI, "qemu+unix://"):
|
case strings.HasPrefix(libvirtURI, "qemu+unix://"):
|
||||||
unixURI, err := url.Parse(strings.TrimPrefix(libvirtURI, "qemu+unix://"))
|
unixURI, err := url.Parse(strings.TrimPrefix(libvirtURI, "qemu+unix://"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return state.ConstellationState{}, err
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
libvirtSocketPath = unixURI.Query().Get("socket")
|
libvirtSocketPath = unixURI.Query().Get("socket")
|
||||||
if libvirtSocketPath == "" {
|
if libvirtSocketPath == "" {
|
||||||
return state.ConstellationState{}, fmt.Errorf("socket path not specified in qemu+unix URI: %s", libvirtURI)
|
return clusterid.File{}, fmt.Errorf("socket path not specified in qemu+unix URI: %s", libvirtURI)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,9 +200,13 @@ func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirt
|
|||||||
MetadataLibvirtURI: metadataLibvirtURI,
|
MetadataLibvirtURI: metadataLibvirtURI,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cl.CreateCluster(ctx, name, vars); err != nil {
|
ip, err := cl.CreateCluster(ctx, name, vars)
|
||||||
return state.ConstellationState{}, err
|
if err != nil {
|
||||||
|
return clusterid.File{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return cl.GetState(), nil
|
return clusterid.File{
|
||||||
|
CloudProvider: cloudprovider.QEMU,
|
||||||
|
IP: ip,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -15,23 +15,12 @@ import (
|
|||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreator(t *testing.T) {
|
func TestCreator(t *testing.T) {
|
||||||
failOnNonAMD64 := (runtime.GOARCH != "amd64") || (runtime.GOOS != "linux")
|
failOnNonAMD64 := (runtime.GOARCH != "amd64") || (runtime.GOOS != "linux")
|
||||||
|
ip := "192.0.2.1"
|
||||||
wantGCPState := state.ConstellationState{
|
|
||||||
CloudProvider: cloudprovider.GCP.String(),
|
|
||||||
LoadBalancerIP: "192.0.2.1",
|
|
||||||
}
|
|
||||||
|
|
||||||
wantQEMUState := state.ConstellationState{
|
|
||||||
CloudProvider: cloudprovider.QEMU.String(),
|
|
||||||
LoadBalancerIP: "192.0.2.1",
|
|
||||||
}
|
|
||||||
|
|
||||||
someErr := errors.New("failed")
|
someErr := errors.New("failed")
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
@ -40,15 +29,13 @@ func TestCreator(t *testing.T) {
|
|||||||
libvirt *stubLibvirtRunner
|
libvirt *stubLibvirtRunner
|
||||||
provider cloudprovider.Provider
|
provider cloudprovider.Provider
|
||||||
config *config.Config
|
config *config.Config
|
||||||
wantState state.ConstellationState
|
|
||||||
wantErr bool
|
wantErr bool
|
||||||
wantRollback bool // Use only together with stubClients.
|
wantRollback bool // Use only together with stubClients.
|
||||||
}{
|
}{
|
||||||
"gcp": {
|
"gcp": {
|
||||||
tfClient: &stubTerraformClient{state: wantGCPState},
|
tfClient: &stubTerraformClient{ip: ip},
|
||||||
provider: cloudprovider.GCP,
|
provider: cloudprovider.GCP,
|
||||||
config: config.Default(),
|
config: config.Default(),
|
||||||
wantState: wantGCPState,
|
|
||||||
},
|
},
|
||||||
"gcp newTerraformClient error": {
|
"gcp newTerraformClient error": {
|
||||||
newTfClientErr: someErr,
|
newTfClientErr: someErr,
|
||||||
@ -64,11 +51,10 @@ func TestCreator(t *testing.T) {
|
|||||||
wantRollback: true,
|
wantRollback: true,
|
||||||
},
|
},
|
||||||
"qemu": {
|
"qemu": {
|
||||||
tfClient: &stubTerraformClient{state: wantQEMUState},
|
tfClient: &stubTerraformClient{ip: ip},
|
||||||
libvirt: &stubLibvirtRunner{},
|
libvirt: &stubLibvirtRunner{},
|
||||||
provider: cloudprovider.QEMU,
|
provider: cloudprovider.QEMU,
|
||||||
config: config.Default(),
|
config: config.Default(),
|
||||||
wantState: wantQEMUState,
|
|
||||||
wantErr: failOnNonAMD64,
|
wantErr: failOnNonAMD64,
|
||||||
},
|
},
|
||||||
"qemu newTerraformClient error": {
|
"qemu newTerraformClient error": {
|
||||||
@ -87,7 +73,7 @@ func TestCreator(t *testing.T) {
|
|||||||
wantRollback: !failOnNonAMD64, // if we run on non-AMD64/linux, we don't get to a point where rollback is needed
|
wantRollback: !failOnNonAMD64, // if we run on non-AMD64/linux, we don't get to a point where rollback is needed
|
||||||
},
|
},
|
||||||
"qemu start libvirt error": {
|
"qemu start libvirt error": {
|
||||||
tfClient: &stubTerraformClient{state: wantQEMUState},
|
tfClient: &stubTerraformClient{ip: ip},
|
||||||
libvirt: &stubLibvirtRunner{startErr: someErr},
|
libvirt: &stubLibvirtRunner{startErr: someErr},
|
||||||
provider: cloudprovider.QEMU,
|
provider: cloudprovider.QEMU,
|
||||||
config: config.Default(),
|
config: config.Default(),
|
||||||
@ -115,7 +101,7 @@ func TestCreator(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
state, err := creator.Create(context.Background(), tc.provider, tc.config, "name", "type", 2, 3)
|
idFile, err := creator.Create(context.Background(), tc.provider, tc.config, "name", "type", 2, 3)
|
||||||
|
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
@ -129,7 +115,8 @@ func TestCreator(t *testing.T) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
assert.Equal(tc.wantState, state)
|
assert.Equal(tc.provider, idFile.CloudProvider)
|
||||||
|
assert.Equal(ip, idFile.IP)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,11 @@ package cloudcmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"errors"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
|
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Terminator deletes cloud provider resources.
|
// Terminator deletes cloud provider resources.
|
||||||
@ -35,10 +34,18 @@ func NewTerminator() *Terminator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Terminate deletes the could provider resources defined in the constellation state.
|
// Terminate deletes the could provider resources defined in the constellation state.
|
||||||
func (t *Terminator) Terminate(ctx context.Context, state state.ConstellationState) error {
|
func (t *Terminator) Terminate(ctx context.Context, provider cloudprovider.Provider) (retErr error) {
|
||||||
provider := cloudprovider.FromString(state.CloudProvider)
|
|
||||||
if provider == cloudprovider.Unknown {
|
if provider == cloudprovider.Unknown {
|
||||||
return fmt.Errorf("unknown cloud provider %s", state.CloudProvider)
|
return errors.New("unknown cloud provider")
|
||||||
|
}
|
||||||
|
|
||||||
|
if provider == cloudprovider.QEMU {
|
||||||
|
libvirt := t.newLibvirtRunner()
|
||||||
|
defer func() {
|
||||||
|
if retErr == nil {
|
||||||
|
retErr = libvirt.Stop(ctx)
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
cl, err := t.newTerraformClient(ctx, provider)
|
cl, err := t.newTerraformClient(ctx, provider)
|
||||||
@ -47,11 +54,6 @@ func (t *Terminator) Terminate(ctx context.Context, state state.ConstellationSta
|
|||||||
}
|
}
|
||||||
defer cl.RemoveInstaller()
|
defer cl.RemoveInstaller()
|
||||||
|
|
||||||
if provider == cloudprovider.QEMU {
|
|
||||||
libvirt := t.newLibvirtRunner()
|
|
||||||
return t.terminateQEMU(ctx, cl, libvirt)
|
|
||||||
}
|
|
||||||
|
|
||||||
return t.terminateTerraform(ctx, cl)
|
return t.terminateTerraform(ctx, cl)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,13 +63,3 @@ func (t *Terminator) terminateTerraform(ctx context.Context, cl terraformClient)
|
|||||||
}
|
}
|
||||||
return cl.CleanUpWorkspace()
|
return cl.CleanUpWorkspace()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminator) terminateQEMU(ctx context.Context, cl terraformClient, lv libvirtRunner) error {
|
|
||||||
if err := cl.DestroyCluster(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := lv.Stop(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return cl.CleanUpWorkspace()
|
|
||||||
}
|
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -23,53 +22,52 @@ func TestTerminator(t *testing.T) {
|
|||||||
tfClient terraformClient
|
tfClient terraformClient
|
||||||
newTfClientErr error
|
newTfClientErr error
|
||||||
libvirt *stubLibvirtRunner
|
libvirt *stubLibvirtRunner
|
||||||
state state.ConstellationState
|
provider cloudprovider.Provider
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
"gcp": {
|
"gcp": {
|
||||||
tfClient: &stubTerraformClient{},
|
tfClient: &stubTerraformClient{},
|
||||||
state: state.ConstellationState{CloudProvider: cloudprovider.GCP.String()},
|
provider: cloudprovider.GCP,
|
||||||
},
|
},
|
||||||
"gcp newTfClientErr": {
|
"gcp newTfClientErr": {
|
||||||
newTfClientErr: someErr,
|
newTfClientErr: someErr,
|
||||||
state: state.ConstellationState{CloudProvider: cloudprovider.GCP.String()},
|
provider: cloudprovider.GCP,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"gcp destroy cluster error": {
|
"gcp destroy cluster error": {
|
||||||
tfClient: &stubTerraformClient{destroyClusterErr: someErr},
|
tfClient: &stubTerraformClient{destroyClusterErr: someErr},
|
||||||
state: state.ConstellationState{CloudProvider: cloudprovider.GCP.String()},
|
provider: cloudprovider.GCP,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"gcp clean up workspace error": {
|
"gcp clean up workspace error": {
|
||||||
tfClient: &stubTerraformClient{cleanUpWorkspaceErr: someErr},
|
tfClient: &stubTerraformClient{cleanUpWorkspaceErr: someErr},
|
||||||
state: state.ConstellationState{CloudProvider: cloudprovider.GCP.String()},
|
provider: cloudprovider.GCP,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"qemu": {
|
"qemu": {
|
||||||
tfClient: &stubTerraformClient{},
|
tfClient: &stubTerraformClient{},
|
||||||
libvirt: &stubLibvirtRunner{},
|
libvirt: &stubLibvirtRunner{},
|
||||||
state: state.ConstellationState{CloudProvider: cloudprovider.QEMU.String()},
|
provider: cloudprovider.QEMU,
|
||||||
},
|
},
|
||||||
"qemu destroy cluster error": {
|
"qemu destroy cluster error": {
|
||||||
tfClient: &stubTerraformClient{destroyClusterErr: someErr},
|
tfClient: &stubTerraformClient{destroyClusterErr: someErr},
|
||||||
libvirt: &stubLibvirtRunner{},
|
libvirt: &stubLibvirtRunner{},
|
||||||
state: state.ConstellationState{CloudProvider: cloudprovider.QEMU.String()},
|
provider: cloudprovider.QEMU,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"qemu clean up workspace error": {
|
"qemu clean up workspace error": {
|
||||||
tfClient: &stubTerraformClient{cleanUpWorkspaceErr: someErr},
|
tfClient: &stubTerraformClient{cleanUpWorkspaceErr: someErr},
|
||||||
libvirt: &stubLibvirtRunner{},
|
libvirt: &stubLibvirtRunner{},
|
||||||
state: state.ConstellationState{CloudProvider: cloudprovider.QEMU.String()},
|
provider: cloudprovider.QEMU,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"qemu stop libvirt error": {
|
"qemu stop libvirt error": {
|
||||||
tfClient: &stubTerraformClient{},
|
tfClient: &stubTerraformClient{},
|
||||||
libvirt: &stubLibvirtRunner{stopErr: someErr},
|
libvirt: &stubLibvirtRunner{stopErr: someErr},
|
||||||
state: state.ConstellationState{CloudProvider: cloudprovider.QEMU.String()},
|
provider: cloudprovider.QEMU,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"unknown cloud provider": {
|
"unknown cloud provider": {
|
||||||
state: state.ConstellationState{},
|
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -87,7 +85,7 @@ func TestTerminator(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
err := terminator.Terminate(context.Background(), tc.state)
|
err := terminator.Terminate(context.Background(), tc.provider)
|
||||||
|
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
@ -96,7 +94,7 @@ func TestTerminator(t *testing.T) {
|
|||||||
cl := tc.tfClient.(*stubTerraformClient)
|
cl := tc.tfClient.(*stubTerraformClient)
|
||||||
assert.True(cl.destroyClusterCalled)
|
assert.True(cl.destroyClusterCalled)
|
||||||
assert.True(cl.removeInstallerCalled)
|
assert.True(cl.removeInstallerCalled)
|
||||||
if cloudprovider.FromString(tc.state.CloudProvider) == cloudprovider.QEMU {
|
if tc.provider == cloudprovider.QEMU {
|
||||||
assert.True(tc.libvirt.stopCalled)
|
assert.True(tc.libvirt.stopCalled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
25
cli/internal/clusterid/id.go
Normal file
25
cli/internal/clusterid/id.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) Edgeless Systems GmbH
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package clusterid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
|
)
|
||||||
|
|
||||||
|
// File contains identifying information about a cluster.
|
||||||
|
type File struct {
|
||||||
|
// ClusterID is the unique identifier of the cluster.
|
||||||
|
ClusterID string `json:"clusterID,omitempty"`
|
||||||
|
// OwnerID is the unique identifier of the owner of the cluster.
|
||||||
|
OwnerID string `json:"ownerID,omitempty"`
|
||||||
|
// UID is the unique identifier of the cluster, used for infrastructure management.
|
||||||
|
UID string `json:"uid,omitempty"`
|
||||||
|
// CloudProvider is the cloud provider of the cluster.
|
||||||
|
CloudProvider cloudprovider.Provider `json:"cloudprovider,omitempty"`
|
||||||
|
// IP is the IP address the cluster can be reached at (often the load balancer).
|
||||||
|
IP string `json:"ip,omitempty"`
|
||||||
|
}
|
@ -9,9 +9,9 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type cloudCreator interface {
|
type cloudCreator interface {
|
||||||
@ -21,9 +21,9 @@ type cloudCreator interface {
|
|||||||
config *config.Config,
|
config *config.Config,
|
||||||
name, insType string,
|
name, insType string,
|
||||||
coordCount, nodeCount int,
|
coordCount, nodeCount int,
|
||||||
) (state.ConstellationState, error)
|
) (clusterid.File, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type cloudTerminator interface {
|
type cloudTerminator interface {
|
||||||
Terminate(context.Context, state.ConstellationState) error
|
Terminate(context.Context, cloudprovider.Provider) error
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
|
||||||
"go.uber.org/goleak"
|
"go.uber.org/goleak"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ func TestMain(m *testing.M) {
|
|||||||
|
|
||||||
type stubCloudCreator struct {
|
type stubCloudCreator struct {
|
||||||
createCalled bool
|
createCalled bool
|
||||||
state state.ConstellationState
|
id clusterid.File
|
||||||
createErr error
|
createErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,9 +35,10 @@ func (c *stubCloudCreator) Create(
|
|||||||
config *config.Config,
|
config *config.Config,
|
||||||
name, insType string,
|
name, insType string,
|
||||||
coordCount, nodeCount int,
|
coordCount, nodeCount int,
|
||||||
) (state.ConstellationState, error) {
|
) (clusterid.File, error) {
|
||||||
c.createCalled = true
|
c.createCalled = true
|
||||||
return c.state, c.createErr
|
c.id.CloudProvider = provider
|
||||||
|
return c.id, c.createErr
|
||||||
}
|
}
|
||||||
|
|
||||||
type stubCloudTerminator struct {
|
type stubCloudTerminator struct {
|
||||||
@ -45,7 +46,7 @@ type stubCloudTerminator struct {
|
|||||||
terminateErr error
|
terminateErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *stubCloudTerminator) Terminate(context.Context, state.ConstellationState) error {
|
func (c *stubCloudTerminator) Terminate(context.Context, cloudprovider.Provider) error {
|
||||||
c.called = true
|
c.called = true
|
||||||
return c.terminateErr
|
return c.terminateErr
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ import (
|
|||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@ -118,17 +117,13 @@ func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler,
|
|||||||
}
|
}
|
||||||
|
|
||||||
spinner.Start("Creating", false)
|
spinner.Start("Creating", false)
|
||||||
state, err := creator.Create(cmd.Context(), provider, config, flags.name, instanceType, flags.controllerCount, flags.workerCount)
|
idFile, err := creator.Create(cmd.Context(), provider, config, flags.name, instanceType, flags.controllerCount, flags.workerCount)
|
||||||
spinner.Stop()
|
spinner.Stop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := fileHandler.WriteJSON(constants.StateFilename, state, file.OptNone); err != nil {
|
if err := fileHandler.WriteJSON(constants.ClusterIDsFileName, idFile, file.OptNone); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := writeIPtoIDFile(fileHandler, state); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,9 +190,6 @@ type createFlags struct {
|
|||||||
|
|
||||||
// checkDirClean checks if files of a previous Constellation are left in the current working dir.
|
// checkDirClean checks if files of a previous Constellation are left in the current working dir.
|
||||||
func checkDirClean(fileHandler file.Handler) error {
|
func checkDirClean(fileHandler file.Handler) error {
|
||||||
if _, err := fileHandler.Stat(constants.StateFilename); !errors.Is(err, fs.ErrNotExist) {
|
|
||||||
return fmt.Errorf("file '%s' already exists in working directory, run 'constellation terminate' before creating a new one", constants.StateFilename)
|
|
||||||
}
|
|
||||||
if _, err := fileHandler.Stat(constants.AdminConfFilename); !errors.Is(err, fs.ErrNotExist) {
|
if _, err := fileHandler.Stat(constants.AdminConfFilename); !errors.Is(err, fs.ErrNotExist) {
|
||||||
return fmt.Errorf("file '%s' already exists in working directory, run 'constellation terminate' before creating a new one", constants.AdminConfFilename)
|
return fmt.Errorf("file '%s' already exists in working directory, run 'constellation terminate' before creating a new one", constants.AdminConfFilename)
|
||||||
}
|
}
|
||||||
@ -211,15 +203,6 @@ func checkDirClean(fileHandler file.Handler) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeIPtoIDFile(fileHandler file.Handler, state state.ConstellationState) error {
|
|
||||||
ip := state.LoadBalancerIP
|
|
||||||
if ip == "" {
|
|
||||||
return fmt.Errorf("bootstrapper ip not found")
|
|
||||||
}
|
|
||||||
idFile := clusterIDsFile{IP: ip}
|
|
||||||
return fileHandler.WriteJSON(constants.ClusterIDsFileName, idFile, file.OptNone)
|
|
||||||
}
|
|
||||||
|
|
||||||
func must(err error) {
|
func must(err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -13,11 +13,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -30,7 +30,7 @@ func TestCreate(t *testing.T) {
|
|||||||
require.NoError(file.WriteYAML(constants.ConfigFilename, defaultConfigWithExpectedMeasurements(t, config.Default(), provider)))
|
require.NoError(file.WriteYAML(constants.ConfigFilename, defaultConfigWithExpectedMeasurements(t, config.Default(), provider)))
|
||||||
return fs
|
return fs
|
||||||
}
|
}
|
||||||
testState := state.ConstellationState{Name: "test", LoadBalancerIP: "192.0.2.1"}
|
idFile := clusterid.File{IP: "192.0.2.1"}
|
||||||
someErr := errors.New("failed")
|
someErr := errors.New("failed")
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
@ -48,7 +48,7 @@ func TestCreate(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
"create": {
|
"create": {
|
||||||
setupFs: fsWithDefaultConfig,
|
setupFs: fsWithDefaultConfig,
|
||||||
creator: &stubCloudCreator{state: testState},
|
creator: &stubCloudCreator{id: idFile},
|
||||||
provider: cloudprovider.GCP,
|
provider: cloudprovider.GCP,
|
||||||
controllerCountFlag: intPtr(1),
|
controllerCountFlag: intPtr(1),
|
||||||
workerCountFlag: intPtr(2),
|
workerCountFlag: intPtr(2),
|
||||||
@ -56,7 +56,7 @@ func TestCreate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"interactive": {
|
"interactive": {
|
||||||
setupFs: fsWithDefaultConfig,
|
setupFs: fsWithDefaultConfig,
|
||||||
creator: &stubCloudCreator{state: testState},
|
creator: &stubCloudCreator{id: idFile},
|
||||||
provider: cloudprovider.Azure,
|
provider: cloudprovider.Azure,
|
||||||
controllerCountFlag: intPtr(2),
|
controllerCountFlag: intPtr(2),
|
||||||
workerCountFlag: intPtr(1),
|
workerCountFlag: intPtr(1),
|
||||||
@ -119,21 +119,6 @@ func TestCreate(t *testing.T) {
|
|||||||
controllerCountFlag: intPtr(3),
|
controllerCountFlag: intPtr(3),
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"old state in directory": {
|
|
||||||
setupFs: func(require *require.Assertions, csp cloudprovider.Provider) afero.Fs {
|
|
||||||
fs := afero.NewMemMapFs()
|
|
||||||
fileHandler := file.NewHandler(fs)
|
|
||||||
require.NoError(fileHandler.Write(constants.StateFilename, []byte{1}, file.OptNone))
|
|
||||||
require.NoError(fileHandler.WriteYAML(constants.ConfigFilename, defaultConfigWithExpectedMeasurements(t, config.Default(), csp)))
|
|
||||||
return fs
|
|
||||||
},
|
|
||||||
creator: &stubCloudCreator{},
|
|
||||||
provider: cloudprovider.GCP,
|
|
||||||
controllerCountFlag: intPtr(1),
|
|
||||||
workerCountFlag: intPtr(1),
|
|
||||||
yesFlag: true,
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
"old adminConf in directory": {
|
"old adminConf in directory": {
|
||||||
setupFs: func(require *require.Assertions, csp cloudprovider.Provider) afero.Fs {
|
setupFs: func(require *require.Assertions, csp cloudprovider.Provider) afero.Fs {
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
@ -237,11 +222,12 @@ func TestCreate(t *testing.T) {
|
|||||||
assert.False(tc.creator.createCalled)
|
assert.False(tc.creator.createCalled)
|
||||||
} else {
|
} else {
|
||||||
assert.True(tc.creator.createCalled)
|
assert.True(tc.creator.createCalled)
|
||||||
var state state.ConstellationState
|
var gotIDFile clusterid.File
|
||||||
require.NoError(fileHandler.ReadJSON(constants.StateFilename, &state))
|
require.NoError(fileHandler.ReadJSON(constants.ClusterIDsFileName, &gotIDFile))
|
||||||
var idFile clusterIDsFile
|
assert.Equal(gotIDFile, clusterid.File{
|
||||||
require.NoError(fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile))
|
IP: idFile.IP,
|
||||||
assert.Equal(state, testState)
|
CloudProvider: tc.provider,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -267,14 +253,9 @@ func TestCheckDirClean(t *testing.T) {
|
|||||||
existingFiles: []string{constants.MasterSecretFilename},
|
existingFiles: []string{constants.MasterSecretFilename},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"state file exists": {
|
|
||||||
fileHandler: file.NewHandler(afero.NewMemMapFs()),
|
|
||||||
existingFiles: []string{constants.StateFilename},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
"multiple exist": {
|
"multiple exist": {
|
||||||
fileHandler: file.NewHandler(afero.NewMemMapFs()),
|
fileHandler: file.NewHandler(afero.NewMemMapFs()),
|
||||||
existingFiles: []string{constants.AdminConfFilename, constants.MasterSecretFilename, constants.StateFilename},
|
existingFiles: []string{constants.AdminConfFilename, constants.MasterSecretFilename},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) Edgeless Systems GmbH
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
type clusterIDsFile struct {
|
|
||||||
ClusterID string `json:"clusterID,omitempty"`
|
|
||||||
OwnerID string `json:"ownerID,omitempty"`
|
|
||||||
IP string `json:"ip,omitempty"`
|
|
||||||
}
|
|
@ -18,6 +18,7 @@ import (
|
|||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/bootstrapper/initproto"
|
"github.com/edgelesssys/constellation/v2/bootstrapper/initproto"
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
||||||
|
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/helm"
|
"github.com/edgelesssys/constellation/v2/cli/internal/helm"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/azureshared"
|
"github.com/edgelesssys/constellation/v2/internal/azureshared"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
@ -48,7 +49,6 @@ func NewInitCmd() *cobra.Command {
|
|||||||
RunE: runInitialize,
|
RunE: runInitialize,
|
||||||
}
|
}
|
||||||
cmd.Flags().String("master-secret", "", "path to base64-encoded master secret")
|
cmd.Flags().String("master-secret", "", "path to base64-encoded master secret")
|
||||||
cmd.Flags().String("endpoint", "", "endpoint of the bootstrapper, passed as HOST[:PORT]")
|
|
||||||
cmd.Flags().Bool("conformance", false, "enable conformance mode")
|
cmd.Flags().Bool("conformance", false, "enable conformance mode")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ func runInitialize(cmd *cobra.Command, args []string) error {
|
|||||||
func initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator) *dialer.Dialer,
|
func initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator) *dialer.Dialer,
|
||||||
fileHandler file.Handler, helmLoader helmLoader, quotaChecker license.QuotaChecker, spinner spinnerInterf,
|
fileHandler file.Handler, helmLoader helmLoader, quotaChecker license.QuotaChecker, spinner spinnerInterf,
|
||||||
) error {
|
) error {
|
||||||
flags, err := evalFlagArgs(cmd, fileHandler)
|
flags, err := evalFlagArgs(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -84,6 +84,11 @@ func initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator
|
|||||||
return fmt.Errorf("reading and validating config: %w", err)
|
return fmt.Errorf("reading and validating config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var idFile clusterid.File
|
||||||
|
if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err != nil {
|
||||||
|
return fmt.Errorf("reading cluster ID file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
k8sVersion, err := versions.NewValidK8sVersion(config.KubernetesVersion)
|
k8sVersion, err := versions.NewValidK8sVersion(config.KubernetesVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("validating kubernetes version: %w", err)
|
return fmt.Errorf("validating kubernetes version: %w", err)
|
||||||
@ -142,13 +147,14 @@ func initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator
|
|||||||
EnforceIdkeydigest: getEnforceIDKeyDigest(provider, config),
|
EnforceIdkeydigest: getEnforceIDKeyDigest(provider, config),
|
||||||
ConformanceMode: flags.conformance,
|
ConformanceMode: flags.conformance,
|
||||||
}
|
}
|
||||||
resp, err := initCall(cmd.Context(), newDialer(validator), flags.endpoint, req)
|
resp, err := initCall(cmd.Context(), newDialer(validator), idFile.IP, req)
|
||||||
spinner.Stop()
|
spinner.Stop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := writeOutput(resp, flags.endpoint, cmd.OutOrStdout(), fileHandler); err != nil {
|
idFile.CloudProvider = provider
|
||||||
|
if err := writeOutput(idFile, resp, cmd.OutOrStdout(), fileHandler); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +196,7 @@ func (d *initDoer) Do(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeOutput(resp *initproto.InitResponse, ip string, wr io.Writer, fileHandler file.Handler) error {
|
func writeOutput(idFile clusterid.File, resp *initproto.InitResponse, wr io.Writer, fileHandler file.Handler) error {
|
||||||
fmt.Fprint(wr, "Your Constellation cluster was successfully initialized.\n\n")
|
fmt.Fprint(wr, "Your Constellation cluster was successfully initialized.\n\n")
|
||||||
|
|
||||||
ownerID := base64.StdEncoding.EncodeToString(resp.OwnerId)
|
ownerID := base64.StdEncoding.EncodeToString(resp.OwnerId)
|
||||||
@ -207,11 +213,9 @@ func writeOutput(resp *initproto.InitResponse, ip string, wr io.Writer, fileHand
|
|||||||
return fmt.Errorf("writing kubeconfig: %w", err)
|
return fmt.Errorf("writing kubeconfig: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
idFile := clusterIDsFile{
|
idFile.OwnerID = ownerID
|
||||||
ClusterID: clusterID,
|
idFile.ClusterID = clusterID
|
||||||
OwnerID: ownerID,
|
|
||||||
IP: ip,
|
|
||||||
}
|
|
||||||
if err := fileHandler.WriteJSON(constants.ClusterIDsFileName, idFile, file.OptOverwrite); err != nil {
|
if err := fileHandler.WriteJSON(constants.ClusterIDsFileName, idFile, file.OptOverwrite); err != nil {
|
||||||
return fmt.Errorf("writing Constellation id file: %w", err)
|
return fmt.Errorf("writing Constellation id file: %w", err)
|
||||||
}
|
}
|
||||||
@ -249,21 +253,11 @@ func getEnforceIDKeyDigest(provider cloudprovider.Provider, config *config.Confi
|
|||||||
|
|
||||||
// evalFlagArgs gets the flag values and does preprocessing of these values like
|
// evalFlagArgs gets the flag values and does preprocessing of these values like
|
||||||
// reading the content from file path flags and deriving other values from flag combinations.
|
// reading the content from file path flags and deriving other values from flag combinations.
|
||||||
func evalFlagArgs(cmd *cobra.Command, fileHandler file.Handler) (initFlags, error) {
|
func evalFlagArgs(cmd *cobra.Command) (initFlags, error) {
|
||||||
masterSecretPath, err := cmd.Flags().GetString("master-secret")
|
masterSecretPath, err := cmd.Flags().GetString("master-secret")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return initFlags{}, fmt.Errorf("parsing master-secret path flag: %w", err)
|
return initFlags{}, fmt.Errorf("parsing master-secret path flag: %w", err)
|
||||||
}
|
}
|
||||||
endpoint, err := cmd.Flags().GetString("endpoint")
|
|
||||||
if err != nil {
|
|
||||||
return initFlags{}, fmt.Errorf("parsing endpoint flag: %w", err)
|
|
||||||
}
|
|
||||||
if endpoint == "" {
|
|
||||||
endpoint, err = readIPFromIDFile(fileHandler)
|
|
||||||
if err != nil {
|
|
||||||
return initFlags{}, fmt.Errorf("getting bootstrapper endpoint: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conformance, err := cmd.Flags().GetBool("conformance")
|
conformance, err := cmd.Flags().GetBool("conformance")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return initFlags{}, fmt.Errorf("parsing autoscale flag: %w", err)
|
return initFlags{}, fmt.Errorf("parsing autoscale flag: %w", err)
|
||||||
@ -275,7 +269,6 @@ func evalFlagArgs(cmd *cobra.Command, fileHandler file.Handler) (initFlags, erro
|
|||||||
|
|
||||||
return initFlags{
|
return initFlags{
|
||||||
configPath: configPath,
|
configPath: configPath,
|
||||||
endpoint: endpoint,
|
|
||||||
conformance: conformance,
|
conformance: conformance,
|
||||||
masterSecretPath: masterSecretPath,
|
masterSecretPath: masterSecretPath,
|
||||||
}, nil
|
}, nil
|
||||||
@ -285,7 +278,6 @@ func evalFlagArgs(cmd *cobra.Command, fileHandler file.Handler) (initFlags, erro
|
|||||||
type initFlags struct {
|
type initFlags struct {
|
||||||
configPath string
|
configPath string
|
||||||
masterSecretPath string
|
masterSecretPath string
|
||||||
endpoint string
|
|
||||||
conformance bool
|
conformance bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,7 +326,7 @@ func readOrGenerateMasterSecret(writer io.Writer, fileHandler file.Handler, file
|
|||||||
}
|
}
|
||||||
|
|
||||||
func readIPFromIDFile(fileHandler file.Handler) (string, error) {
|
func readIPFromIDFile(fileHandler file.Handler) (string, error) {
|
||||||
var idFile clusterIDsFile
|
var idFile clusterid.File
|
||||||
if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err != nil {
|
if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,8 @@ import (
|
|||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/bootstrapper/initproto"
|
"github.com/edgelesssys/constellation/v2/bootstrapper/initproto"
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
||||||
|
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudtypes"
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
@ -31,7 +31,6 @@ import (
|
|||||||
"github.com/edgelesssys/constellation/v2/internal/grpc/testdialer"
|
"github.com/edgelesssys/constellation/v2/internal/grpc/testdialer"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/license"
|
"github.com/edgelesssys/constellation/v2/internal/license"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/oid"
|
"github.com/edgelesssys/constellation/v2/internal/oid"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -48,18 +47,9 @@ func TestInitArgumentValidation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInitialize(t *testing.T) {
|
func TestInitialize(t *testing.T) {
|
||||||
testGcpState := &state.ConstellationState{
|
|
||||||
CloudProvider: "GCP",
|
|
||||||
}
|
|
||||||
gcpServiceAccKey := &gcpshared.ServiceAccountKey{
|
gcpServiceAccKey := &gcpshared.ServiceAccountKey{
|
||||||
Type: "service_account",
|
Type: "service_account",
|
||||||
}
|
}
|
||||||
testAzureState := &state.ConstellationState{
|
|
||||||
CloudProvider: "Azure",
|
|
||||||
}
|
|
||||||
testQemuState := &state.ConstellationState{
|
|
||||||
CloudProvider: "QEMU",
|
|
||||||
}
|
|
||||||
testInitResp := &initproto.InitResponse{
|
testInitResp := &initproto.InitResponse{
|
||||||
Kubeconfig: []byte("kubeconfig"),
|
Kubeconfig: []byte("kubeconfig"),
|
||||||
OwnerId: []byte("ownerID"),
|
OwnerId: []byte("ownerID"),
|
||||||
@ -69,59 +59,51 @@ func TestInitialize(t *testing.T) {
|
|||||||
someErr := errors.New("failed")
|
someErr := errors.New("failed")
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
state *state.ConstellationState
|
provider cloudprovider.Provider
|
||||||
idFile *clusterIDsFile
|
idFile *clusterid.File
|
||||||
configMutator func(*config.Config)
|
configMutator func(*config.Config)
|
||||||
serviceAccKey *gcpshared.ServiceAccountKey
|
serviceAccKey *gcpshared.ServiceAccountKey
|
||||||
helmLoader stubHelmLoader
|
helmLoader stubHelmLoader
|
||||||
initServerAPI *stubInitServer
|
initServerAPI *stubInitServer
|
||||||
endpointFlag string
|
|
||||||
masterSecretShouldExist bool
|
masterSecretShouldExist bool
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
"initialize some gcp instances": {
|
"initialize some gcp instances": {
|
||||||
state: testGcpState,
|
provider: cloudprovider.GCP,
|
||||||
idFile: &clusterIDsFile{IP: "192.0.2.1"},
|
idFile: &clusterid.File{IP: "192.0.2.1"},
|
||||||
configMutator: func(c *config.Config) { c.Provider.GCP.ServiceAccountKeyPath = serviceAccPath },
|
configMutator: func(c *config.Config) { c.Provider.GCP.ServiceAccountKeyPath = serviceAccPath },
|
||||||
serviceAccKey: gcpServiceAccKey,
|
serviceAccKey: gcpServiceAccKey,
|
||||||
initServerAPI: &stubInitServer{initResp: testInitResp},
|
initServerAPI: &stubInitServer{initResp: testInitResp},
|
||||||
},
|
},
|
||||||
"initialize some azure instances": {
|
"initialize some azure instances": {
|
||||||
state: testAzureState,
|
provider: cloudprovider.Azure,
|
||||||
idFile: &clusterIDsFile{IP: "192.0.2.1"},
|
idFile: &clusterid.File{IP: "192.0.2.1"},
|
||||||
initServerAPI: &stubInitServer{initResp: testInitResp},
|
initServerAPI: &stubInitServer{initResp: testInitResp},
|
||||||
},
|
},
|
||||||
"initialize some qemu instances": {
|
"initialize some qemu instances": {
|
||||||
state: testQemuState,
|
provider: cloudprovider.QEMU,
|
||||||
idFile: &clusterIDsFile{IP: "192.0.2.1"},
|
idFile: &clusterid.File{IP: "192.0.2.1"},
|
||||||
initServerAPI: &stubInitServer{initResp: testInitResp},
|
initServerAPI: &stubInitServer{initResp: testInitResp},
|
||||||
},
|
},
|
||||||
"initialize with endpoint flag": {
|
"empty id file": {
|
||||||
state: testGcpState,
|
provider: cloudprovider.GCP,
|
||||||
configMutator: func(c *config.Config) { c.Provider.GCP.ServiceAccountKeyPath = serviceAccPath },
|
idFile: &clusterid.File{},
|
||||||
serviceAccKey: gcpServiceAccKey,
|
|
||||||
initServerAPI: &stubInitServer{initResp: testInitResp},
|
|
||||||
endpointFlag: "192.0.2.1",
|
|
||||||
},
|
|
||||||
"empty state": {
|
|
||||||
state: &state.ConstellationState{},
|
|
||||||
idFile: &clusterIDsFile{IP: "192.0.2.1"},
|
|
||||||
initServerAPI: &stubInitServer{},
|
initServerAPI: &stubInitServer{},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"neither endpoint flag nor id file": {
|
"no id file": {
|
||||||
state: &state.ConstellationState{},
|
provider: cloudprovider.GCP,
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"init call fails": {
|
"init call fails": {
|
||||||
state: testGcpState,
|
provider: cloudprovider.GCP,
|
||||||
idFile: &clusterIDsFile{IP: "192.0.2.1"},
|
idFile: &clusterid.File{IP: "192.0.2.1"},
|
||||||
initServerAPI: &stubInitServer{initErr: someErr},
|
initServerAPI: &stubInitServer{initErr: someErr},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"fail missing enforced PCR": {
|
"fail missing enforced PCR": {
|
||||||
state: testGcpState,
|
provider: cloudprovider.GCP,
|
||||||
idFile: &clusterIDsFile{IP: "192.0.2.1"},
|
idFile: &clusterid.File{IP: "192.0.2.1"},
|
||||||
configMutator: func(c *config.Config) {
|
configMutator: func(c *config.Config) {
|
||||||
c.Provider.GCP.EnforcedMeasurements = append(c.Provider.GCP.EnforcedMeasurements, 10)
|
c.Provider.GCP.EnforcedMeasurements = append(c.Provider.GCP.EnforcedMeasurements, 10)
|
||||||
},
|
},
|
||||||
@ -158,22 +140,17 @@ func TestInitialize(t *testing.T) {
|
|||||||
|
|
||||||
// Flags
|
// Flags
|
||||||
cmd.Flags().String("config", constants.ConfigFilename, "") // register persistent flag manually
|
cmd.Flags().String("config", constants.ConfigFilename, "") // register persistent flag manually
|
||||||
if tc.endpointFlag != "" {
|
|
||||||
require.NoError(cmd.Flags().Set("endpoint", tc.endpointFlag))
|
|
||||||
}
|
|
||||||
|
|
||||||
// File system preparation
|
// File system preparation
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
fileHandler := file.NewHandler(fs)
|
fileHandler := file.NewHandler(fs)
|
||||||
config := defaultConfigWithExpectedMeasurements(t, config.Default(), cloudprovider.FromString(tc.state.CloudProvider))
|
config := defaultConfigWithExpectedMeasurements(t, config.Default(), tc.provider)
|
||||||
if tc.configMutator != nil {
|
if tc.configMutator != nil {
|
||||||
tc.configMutator(config)
|
tc.configMutator(config)
|
||||||
}
|
}
|
||||||
require.NoError(fileHandler.WriteYAML(constants.ConfigFilename, config, file.OptNone))
|
require.NoError(fileHandler.WriteYAML(constants.ConfigFilename, config, file.OptNone))
|
||||||
if tc.state != nil {
|
|
||||||
require.NoError(fileHandler.WriteJSON(constants.StateFilename, tc.state, file.OptNone))
|
|
||||||
}
|
|
||||||
if tc.idFile != nil {
|
if tc.idFile != nil {
|
||||||
|
tc.idFile.CloudProvider = tc.provider
|
||||||
require.NoError(fileHandler.WriteJSON(constants.ClusterIDsFileName, tc.idFile, file.OptNone))
|
require.NoError(fileHandler.WriteJSON(constants.ClusterIDsFileName, tc.idFile, file.OptNone))
|
||||||
}
|
}
|
||||||
if tc.serviceAccKey != nil {
|
if tc.serviceAccKey != nil {
|
||||||
@ -218,17 +195,22 @@ func TestWriteOutput(t *testing.T) {
|
|||||||
ownerID := base64.StdEncoding.EncodeToString(resp.OwnerId)
|
ownerID := base64.StdEncoding.EncodeToString(resp.OwnerId)
|
||||||
clusterID := base64.StdEncoding.EncodeToString(resp.ClusterId)
|
clusterID := base64.StdEncoding.EncodeToString(resp.ClusterId)
|
||||||
|
|
||||||
expectedIDFile := clusterIDsFile{
|
expectedIDFile := clusterid.File{
|
||||||
ClusterID: clusterID,
|
ClusterID: clusterID,
|
||||||
OwnerID: ownerID,
|
OwnerID: ownerID,
|
||||||
IP: "cluster-ip",
|
IP: "cluster-ip",
|
||||||
|
UID: "test-uid",
|
||||||
}
|
}
|
||||||
|
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
testFs := afero.NewMemMapFs()
|
testFs := afero.NewMemMapFs()
|
||||||
fileHandler := file.NewHandler(testFs)
|
fileHandler := file.NewHandler(testFs)
|
||||||
|
|
||||||
err := writeOutput(resp, "cluster-ip", &out, fileHandler)
|
idFile := clusterid.File{
|
||||||
|
UID: "test-uid",
|
||||||
|
IP: "cluster-ip",
|
||||||
|
}
|
||||||
|
err := writeOutput(idFile, resp, &out, fileHandler)
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
// assert.Contains(out.String(), ownerID)
|
// assert.Contains(out.String(), ownerID)
|
||||||
assert.Contains(out.String(), clusterID)
|
assert.Contains(out.String(), clusterID)
|
||||||
@ -241,7 +223,7 @@ func TestWriteOutput(t *testing.T) {
|
|||||||
|
|
||||||
idsFile, err := afs.ReadFile(constants.ClusterIDsFileName)
|
idsFile, err := afs.ReadFile(constants.ClusterIDsFileName)
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
var testIDFile clusterIDsFile
|
var testIDFile clusterid.File
|
||||||
err = json.Unmarshal(idsFile, &testIDFile)
|
err = json.Unmarshal(idsFile, &testIDFile)
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
assert.Equal(expectedIDFile, testIDFile)
|
assert.Equal(expectedIDFile, testIDFile)
|
||||||
@ -366,8 +348,7 @@ func TestAttestation(t *testing.T) {
|
|||||||
OwnerId: []byte("ownerID"),
|
OwnerId: []byte("ownerID"),
|
||||||
ClusterId: []byte("clusterID"),
|
ClusterId: []byte("clusterID"),
|
||||||
}}
|
}}
|
||||||
existingState := state.ConstellationState{CloudProvider: "QEMU"}
|
existingIDFile := &clusterid.File{IP: "192.0.2.4", CloudProvider: cloudprovider.QEMU}
|
||||||
existingIDFile := &clusterIDsFile{IP: "192.0.2.4"}
|
|
||||||
|
|
||||||
netDialer := testdialer.NewBufconnDialer()
|
netDialer := testdialer.NewBufconnDialer()
|
||||||
newDialer := func(v *cloudcmd.Validator) *dialer.Dialer {
|
newDialer := func(v *cloudcmd.Validator) *dialer.Dialer {
|
||||||
@ -404,7 +385,6 @@ func TestAttestation(t *testing.T) {
|
|||||||
|
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
fileHandler := file.NewHandler(fs)
|
fileHandler := file.NewHandler(fs)
|
||||||
require.NoError(fileHandler.WriteJSON(constants.StateFilename, existingState, file.OptNone))
|
|
||||||
require.NoError(fileHandler.WriteJSON(constants.ClusterIDsFileName, existingIDFile, file.OptNone))
|
require.NoError(fileHandler.WriteJSON(constants.ClusterIDsFileName, existingIDFile, file.OptNone))
|
||||||
|
|
||||||
cfg := config.Default()
|
cfg := config.Default()
|
||||||
|
@ -11,10 +11,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"go.uber.org/multierr"
|
"go.uber.org/multierr"
|
||||||
@ -45,14 +45,14 @@ func runDown(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func checkForMiniCluster(fileHandler file.Handler) error {
|
func checkForMiniCluster(fileHandler file.Handler) error {
|
||||||
var state state.ConstellationState
|
var idFile clusterid.File
|
||||||
if err := fileHandler.ReadJSON(constants.StateFilename, &state); err != nil {
|
if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if cloudprovider.FromString(state.CloudProvider) != cloudprovider.QEMU {
|
if idFile.CloudProvider != cloudprovider.QEMU {
|
||||||
return errors.New("cluster is not a QEMU based Constellation")
|
return errors.New("cluster is not a QEMU based Constellation")
|
||||||
}
|
}
|
||||||
if state.Name != "mini" {
|
if idFile.UID != "mini" {
|
||||||
return errors.New("cluster is not a mini Constellation cluster")
|
return errors.New("cluster is not a mini Constellation cluster")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,15 +199,14 @@ func prepareConfig(cmd *cobra.Command, fileHandler file.Handler) (*config.Config
|
|||||||
|
|
||||||
// createMiniCluster creates a new cluster using the given config.
|
// createMiniCluster creates a new cluster using the given config.
|
||||||
func createMiniCluster(ctx context.Context, fileHandler file.Handler, creator cloudCreator, config *config.Config) error {
|
func createMiniCluster(ctx context.Context, fileHandler file.Handler, creator cloudCreator, config *config.Config) error {
|
||||||
state, err := creator.Create(ctx, cloudprovider.QEMU, config, "mini", "", 1, 1)
|
idFile, err := creator.Create(ctx, cloudprovider.QEMU, config, "mini", "", 1, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := fileHandler.WriteJSON(constants.StateFilename, state); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return writeIPtoIDFile(fileHandler, state)
|
idFile.UID = "mini" // use UID "mini" to identify mini constellation clusters.
|
||||||
|
|
||||||
|
return fileHandler.WriteJSON(constants.ClusterIDsFileName, idFile, file.OptNone)
|
||||||
}
|
}
|
||||||
|
|
||||||
// initializeMiniCluster initializes a QEMU cluster.
|
// initializeMiniCluster initializes a QEMU cluster.
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
||||||
|
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||||
"github.com/edgelesssys/constellation/v2/disk-mapper/recoverproto"
|
"github.com/edgelesssys/constellation/v2/disk-mapper/recoverproto"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||||
@ -226,7 +227,7 @@ func TestParseRecoverFlags(t *testing.T) {
|
|||||||
|
|
||||||
fileHandler := file.NewHandler(afero.NewMemMapFs())
|
fileHandler := file.NewHandler(afero.NewMemMapFs())
|
||||||
if tc.writeIDFile {
|
if tc.writeIDFile {
|
||||||
require.NoError(fileHandler.WriteJSON(constants.ClusterIDsFileName, &clusterIDsFile{IP: "192.0.2.42"}))
|
require.NoError(fileHandler.WriteJSON(constants.ClusterIDsFileName, &clusterid.File{IP: "192.0.2.42"}))
|
||||||
}
|
}
|
||||||
|
|
||||||
flags, err := parseRecoverFlags(cmd, fileHandler)
|
flags, err := parseRecoverFlags(cmd, fileHandler)
|
||||||
|
@ -16,9 +16,9 @@ import (
|
|||||||
"go.uber.org/multierr"
|
"go.uber.org/multierr"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
||||||
|
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewTerminateCmd returns a new cobra.Command for the terminate command.
|
// NewTerminateCmd returns a new cobra.Command for the terminate command.
|
||||||
@ -45,13 +45,13 @@ func runTerminate(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
func terminate(cmd *cobra.Command, terminator cloudTerminator, fileHandler file.Handler, spinner spinnerInterf,
|
func terminate(cmd *cobra.Command, terminator cloudTerminator, fileHandler file.Handler, spinner spinnerInterf,
|
||||||
) error {
|
) error {
|
||||||
var stat state.ConstellationState
|
var idFile clusterid.File
|
||||||
if err := fileHandler.ReadJSON(constants.StateFilename, &stat); err != nil {
|
if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err != nil {
|
||||||
return fmt.Errorf("reading Constellation state: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
spinner.Start("Terminating", false)
|
spinner.Start("Terminating", false)
|
||||||
err := terminator.Terminate(cmd.Context(), stat)
|
err := terminator.Terminate(cmd.Context(), idFile.CloudProvider)
|
||||||
spinner.Stop()
|
spinner.Stop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("terminating Constellation cluster: %w", err)
|
return fmt.Errorf("terminating Constellation cluster: %w", err)
|
||||||
@ -60,10 +60,6 @@ func terminate(cmd *cobra.Command, terminator cloudTerminator, fileHandler file.
|
|||||||
cmd.Println("Your Constellation cluster was terminated successfully.")
|
cmd.Println("Your Constellation cluster was terminated successfully.")
|
||||||
|
|
||||||
var retErr error
|
var retErr error
|
||||||
if err := fileHandler.Remove(constants.StateFilename); err != nil {
|
|
||||||
retErr = multierr.Append(err, fmt.Errorf("failed to remove file: '%s', please remove it manually", constants.StateFilename))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fileHandler.Remove(constants.AdminConfFilename); err != nil && !errors.Is(err, fs.ErrNotExist) {
|
if err := fileHandler.Remove(constants.AdminConfFilename); err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||||
retErr = multierr.Append(err, fmt.Errorf("failed to remove file: '%s', please remove it manually", constants.AdminConfFilename))
|
retErr = multierr.Append(err, fmt.Errorf("failed to remove file: '%s', please remove it manually", constants.AdminConfFilename))
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,10 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -46,47 +47,46 @@ func TestTerminateCmdArgumentValidation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTerminate(t *testing.T) {
|
func TestTerminate(t *testing.T) {
|
||||||
setupFs := func(require *require.Assertions, state state.ConstellationState) afero.Fs {
|
setupFs := func(require *require.Assertions, idFile clusterid.File) afero.Fs {
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
fileHandler := file.NewHandler(fs)
|
fileHandler := file.NewHandler(fs)
|
||||||
require.NoError(fileHandler.Write(constants.AdminConfFilename, []byte{1, 2}, file.OptNone))
|
require.NoError(fileHandler.Write(constants.AdminConfFilename, []byte{1, 2}, file.OptNone))
|
||||||
require.NoError(fileHandler.Write(constants.WGQuickConfigFilename, []byte{1, 2}, file.OptNone))
|
require.NoError(fileHandler.Write(constants.WGQuickConfigFilename, []byte{1, 2}, file.OptNone))
|
||||||
require.NoError(fileHandler.Write(constants.ClusterIDsFileName, []byte{1, 2}, file.OptNone))
|
require.NoError(fileHandler.WriteJSON(constants.ClusterIDsFileName, idFile, file.OptNone))
|
||||||
require.NoError(fileHandler.WriteJSON(constants.StateFilename, state, file.OptNone))
|
|
||||||
return fs
|
return fs
|
||||||
}
|
}
|
||||||
someErr := errors.New("failed")
|
someErr := errors.New("failed")
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
state state.ConstellationState
|
idFile clusterid.File
|
||||||
setupFs func(*require.Assertions, state.ConstellationState) afero.Fs
|
setupFs func(*require.Assertions, clusterid.File) afero.Fs
|
||||||
terminator spyCloudTerminator
|
terminator spyCloudTerminator
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
"success": {
|
"success": {
|
||||||
state: state.ConstellationState{CloudProvider: "gcp"},
|
idFile: clusterid.File{CloudProvider: cloudprovider.GCP},
|
||||||
setupFs: setupFs,
|
setupFs: setupFs,
|
||||||
terminator: &stubCloudTerminator{},
|
terminator: &stubCloudTerminator{},
|
||||||
},
|
},
|
||||||
"files to remove do not exist": {
|
"files to remove do not exist": {
|
||||||
state: state.ConstellationState{CloudProvider: "gcp"},
|
idFile: clusterid.File{CloudProvider: cloudprovider.GCP},
|
||||||
setupFs: func(require *require.Assertions, state state.ConstellationState) afero.Fs {
|
setupFs: func(require *require.Assertions, idFile clusterid.File) afero.Fs {
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
fileHandler := file.NewHandler(fs)
|
fileHandler := file.NewHandler(fs)
|
||||||
require.NoError(fileHandler.WriteJSON(constants.StateFilename, state, file.OptNone))
|
require.NoError(fileHandler.WriteJSON(constants.ClusterIDsFileName, idFile, file.OptNone))
|
||||||
return fs
|
return fs
|
||||||
},
|
},
|
||||||
terminator: &stubCloudTerminator{},
|
terminator: &stubCloudTerminator{},
|
||||||
},
|
},
|
||||||
"terminate error": {
|
"terminate error": {
|
||||||
state: state.ConstellationState{CloudProvider: "gcp"},
|
idFile: clusterid.File{CloudProvider: cloudprovider.GCP},
|
||||||
setupFs: setupFs,
|
setupFs: setupFs,
|
||||||
terminator: &stubCloudTerminator{terminateErr: someErr},
|
terminator: &stubCloudTerminator{terminateErr: someErr},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"missing state file": {
|
"missing state file": {
|
||||||
state: state.ConstellationState{CloudProvider: "gcp"},
|
idFile: clusterid.File{CloudProvider: cloudprovider.GCP},
|
||||||
setupFs: func(require *require.Assertions, state state.ConstellationState) afero.Fs {
|
setupFs: func(require *require.Assertions, idFile clusterid.File) afero.Fs {
|
||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
fileHandler := file.NewHandler(fs)
|
fileHandler := file.NewHandler(fs)
|
||||||
require.NoError(fileHandler.Write(constants.AdminConfFilename, []byte{1, 2}, file.OptNone))
|
require.NoError(fileHandler.Write(constants.AdminConfFilename, []byte{1, 2}, file.OptNone))
|
||||||
@ -97,9 +97,9 @@ func TestTerminate(t *testing.T) {
|
|||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"remove file fails": {
|
"remove file fails": {
|
||||||
state: state.ConstellationState{CloudProvider: "gcp"},
|
idFile: clusterid.File{CloudProvider: cloudprovider.GCP},
|
||||||
setupFs: func(require *require.Assertions, state state.ConstellationState) afero.Fs {
|
setupFs: func(require *require.Assertions, idFile clusterid.File) afero.Fs {
|
||||||
fs := setupFs(require, state)
|
fs := setupFs(require, idFile)
|
||||||
return afero.NewReadOnlyFs(fs)
|
return afero.NewReadOnlyFs(fs)
|
||||||
},
|
},
|
||||||
terminator: &stubCloudTerminator{},
|
terminator: &stubCloudTerminator{},
|
||||||
@ -117,7 +117,7 @@ func TestTerminate(t *testing.T) {
|
|||||||
cmd.SetErr(&bytes.Buffer{})
|
cmd.SetErr(&bytes.Buffer{})
|
||||||
|
|
||||||
require.NotNil(tc.setupFs)
|
require.NotNil(tc.setupFs)
|
||||||
fileHandler := file.NewHandler(tc.setupFs(require, tc.state))
|
fileHandler := file.NewHandler(tc.setupFs(require, tc.idFile))
|
||||||
|
|
||||||
err := terminate(cmd, tc.terminator, fileHandler, nopSpinner{})
|
err := terminate(cmd, tc.terminator, fileHandler, nopSpinner{})
|
||||||
|
|
||||||
@ -126,8 +126,6 @@ func TestTerminate(t *testing.T) {
|
|||||||
} else {
|
} else {
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
assert.True(tc.terminator.Called())
|
assert.True(tc.terminator.Called())
|
||||||
_, err := fileHandler.Stat(constants.StateFilename)
|
|
||||||
assert.Error(err)
|
|
||||||
_, err = fileHandler.Stat(constants.AdminConfFilename)
|
_, err = fileHandler.Stat(constants.AdminConfFilename)
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
_, err = fileHandler.Stat(constants.WGQuickConfigFilename)
|
_, err = fileHandler.Stat(constants.WGQuickConfigFilename)
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
||||||
|
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/atls"
|
"github.com/edgelesssys/constellation/v2/internal/atls"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/crypto"
|
"github.com/edgelesssys/constellation/v2/internal/crypto"
|
||||||
@ -117,7 +118,7 @@ func parseVerifyFlags(cmd *cobra.Command, fileHandler file.Handler) (verifyFlags
|
|||||||
emptyEndpoint := endpoint == ""
|
emptyEndpoint := endpoint == ""
|
||||||
emptyIDs := ownerID == "" && clusterID == ""
|
emptyIDs := ownerID == "" && clusterID == ""
|
||||||
if emptyEndpoint || emptyIDs {
|
if emptyEndpoint || emptyIDs {
|
||||||
var idFile clusterIDsFile
|
var idFile clusterid.File
|
||||||
if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err == nil {
|
if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err == nil {
|
||||||
if emptyEndpoint {
|
if emptyEndpoint {
|
||||||
cmd.Printf("Using endpoint from %q. Specify --node-endpoint to override this.\n", constants.ClusterIDsFileName)
|
cmd.Printf("Using endpoint from %q. Specify --node-endpoint to override this.\n", constants.ClusterIDsFileName)
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/atls"
|
"github.com/edgelesssys/constellation/v2/internal/atls"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||||
@ -44,7 +45,7 @@ func TestVerify(t *testing.T) {
|
|||||||
configFlag string
|
configFlag string
|
||||||
ownerIDFlag string
|
ownerIDFlag string
|
||||||
clusterIDFlag string
|
clusterIDFlag string
|
||||||
idFile *clusterIDsFile
|
idFile *clusterid.File
|
||||||
wantEndpoint string
|
wantEndpoint string
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
@ -79,7 +80,7 @@ func TestVerify(t *testing.T) {
|
|||||||
provider: cloudprovider.GCP,
|
provider: cloudprovider.GCP,
|
||||||
clusterIDFlag: zeroBase64,
|
clusterIDFlag: zeroBase64,
|
||||||
protoClient: &stubVerifyClient{},
|
protoClient: &stubVerifyClient{},
|
||||||
idFile: &clusterIDsFile{IP: "192.0.2.1"},
|
idFile: &clusterid.File{IP: "192.0.2.1"},
|
||||||
wantEndpoint: "192.0.2.1:" + strconv.Itoa(constants.VerifyServiceNodePortGRPC),
|
wantEndpoint: "192.0.2.1:" + strconv.Itoa(constants.VerifyServiceNodePortGRPC),
|
||||||
},
|
},
|
||||||
"override endpoint from details file": {
|
"override endpoint from details file": {
|
||||||
@ -87,7 +88,7 @@ func TestVerify(t *testing.T) {
|
|||||||
nodeEndpointFlag: "192.0.2.2:1234",
|
nodeEndpointFlag: "192.0.2.2:1234",
|
||||||
clusterIDFlag: zeroBase64,
|
clusterIDFlag: zeroBase64,
|
||||||
protoClient: &stubVerifyClient{},
|
protoClient: &stubVerifyClient{},
|
||||||
idFile: &clusterIDsFile{IP: "192.0.2.1"},
|
idFile: &clusterid.File{IP: "192.0.2.1"},
|
||||||
wantEndpoint: "192.0.2.2:1234",
|
wantEndpoint: "192.0.2.2:1234",
|
||||||
},
|
},
|
||||||
"invalid endpoint": {
|
"invalid endpoint": {
|
||||||
@ -106,7 +107,7 @@ func TestVerify(t *testing.T) {
|
|||||||
provider: cloudprovider.GCP,
|
provider: cloudprovider.GCP,
|
||||||
nodeEndpointFlag: "192.0.2.1:1234",
|
nodeEndpointFlag: "192.0.2.1:1234",
|
||||||
protoClient: &stubVerifyClient{},
|
protoClient: &stubVerifyClient{},
|
||||||
idFile: &clusterIDsFile{OwnerID: zeroBase64},
|
idFile: &clusterid.File{OwnerID: zeroBase64},
|
||||||
wantEndpoint: "192.0.2.1:1234",
|
wantEndpoint: "192.0.2.1:1234",
|
||||||
},
|
},
|
||||||
"config file not existing": {
|
"config file not existing": {
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
|
||||||
"github.com/hashicorp/go-version"
|
"github.com/hashicorp/go-version"
|
||||||
install "github.com/hashicorp/hc-install"
|
install "github.com/hashicorp/hc-install"
|
||||||
"github.com/hashicorp/hc-install/fs"
|
"github.com/hashicorp/hc-install/fs"
|
||||||
@ -36,7 +35,6 @@ type Client struct {
|
|||||||
provider cloudprovider.Provider
|
provider cloudprovider.Provider
|
||||||
|
|
||||||
file file.Handler
|
file file.Handler
|
||||||
state state.ConstellationState
|
|
||||||
remove func()
|
remove func()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,43 +56,38 @@ func New(ctx context.Context, provider cloudprovider.Provider) (*Client, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateCluster creates a Constellation cluster using Terraform.
|
// CreateCluster creates a Constellation cluster using Terraform.
|
||||||
func (c *Client) CreateCluster(ctx context.Context, name string, vars Variables) error {
|
func (c *Client) CreateCluster(ctx context.Context, name string, vars Variables) (string, error) {
|
||||||
if err := prepareWorkspace(c.file, c.provider); err != nil {
|
if err := prepareWorkspace(c.file, c.provider); err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.tf.Init(ctx); err != nil {
|
if err := c.tf.Init(ctx); err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.file.Write(terraformVarsFile, []byte(vars.String())); err != nil {
|
if err := c.file.Write(terraformVarsFile, []byte(vars.String())); err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.tf.Apply(ctx); err != nil {
|
if err := c.tf.Apply(ctx); err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
tfState, err := c.tf.Show(ctx)
|
tfState, err := c.tf.Show(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
ipOutput, ok := tfState.Values.Outputs["ip"]
|
ipOutput, ok := tfState.Values.Outputs["ip"]
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("no IP output found")
|
return "", errors.New("no IP output found")
|
||||||
}
|
}
|
||||||
ip, ok := ipOutput.Value.(string)
|
ip, ok := ipOutput.Value.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("invalid type in IP output: not a string")
|
return "", errors.New("invalid type in IP output: not a string")
|
||||||
}
|
|
||||||
c.state = state.ConstellationState{
|
|
||||||
Name: name,
|
|
||||||
CloudProvider: c.provider.String(),
|
|
||||||
LoadBalancerIP: ip,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return ip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DestroyInstances destroys a Constellation cluster using Terraform.
|
// DestroyInstances destroys a Constellation cluster using Terraform.
|
||||||
@ -132,11 +125,6 @@ func (c *Client) CleanUpWorkspace() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetState returns the state of the cluster.
|
|
||||||
func (c *Client) GetState() state.ConstellationState {
|
|
||||||
return c.state
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetExecutable returns a Terraform executable either from the local filesystem,
|
// GetExecutable returns a Terraform executable either from the local filesystem,
|
||||||
// or downloads the latest version fulfilling the version constraint.
|
// or downloads the latest version fulfilling the version constraint.
|
||||||
func GetExecutable(ctx context.Context, workingDir string) (terraform *tfexec.Terraform, remove func(), err error) {
|
func GetExecutable(ctx context.Context, workingDir string) (terraform *tfexec.Terraform, remove func(), err error) {
|
||||||
|
@ -127,13 +127,14 @@ func TestCreateCluster(t *testing.T) {
|
|||||||
file: file.NewHandler(tc.fs),
|
file: file.NewHandler(tc.fs),
|
||||||
}
|
}
|
||||||
|
|
||||||
err := c.CreateCluster(context.Background(), "test", tc.vars)
|
ip, err := c.CreateCluster(context.Background(), "test", tc.vars)
|
||||||
|
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
|
assert.Equal("192.0.2.100", ip)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
|
|
||||||
package cloudprovider
|
package cloudprovider
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
//go:generate stringer -type=Provider
|
//go:generate stringer -type=Provider
|
||||||
|
|
||||||
@ -21,6 +24,21 @@ const (
|
|||||||
QEMU
|
QEMU
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MarshalJSON marshals the Provider to JSON string.
|
||||||
|
func (p Provider) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(p.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals the Provider from JSON string.
|
||||||
|
func (p *Provider) UnmarshalJSON(b []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(b, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*p = FromString(s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// FromString returns cloud provider from string.
|
// FromString returns cloud provider from string.
|
||||||
func FromString(s string) Provider {
|
func FromString(s string) Provider {
|
||||||
s = strings.ToLower(s)
|
s = strings.ToLower(s)
|
||||||
|
143
internal/cloud/cloudprovider/cloudprovider_test.go
Normal file
143
internal/cloud/cloudprovider/cloudprovider_test.go
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) Edgeless Systems GmbH
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cloudprovider
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMarshalJSON(t *testing.T) {
|
||||||
|
testCases := map[string]struct {
|
||||||
|
input Provider
|
||||||
|
want []byte
|
||||||
|
}{
|
||||||
|
"unknown": {
|
||||||
|
input: Unknown,
|
||||||
|
want: []byte("\"Unknown\""),
|
||||||
|
},
|
||||||
|
"aws": {
|
||||||
|
input: AWS,
|
||||||
|
want: []byte("\"AWS\""),
|
||||||
|
},
|
||||||
|
"azure": {
|
||||||
|
input: Azure,
|
||||||
|
want: []byte("\"Azure\""),
|
||||||
|
},
|
||||||
|
"gcp": {
|
||||||
|
input: GCP,
|
||||||
|
want: []byte("\"GCP\""),
|
||||||
|
},
|
||||||
|
"qemu": {
|
||||||
|
input: QEMU,
|
||||||
|
want: []byte("\"QEMU\""),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
b, err := tc.input.MarshalJSON()
|
||||||
|
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal(tc.want, b)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalJSON(t *testing.T) {
|
||||||
|
testCases := map[string]struct {
|
||||||
|
input []byte
|
||||||
|
want Provider
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
"empty": {
|
||||||
|
input: []byte{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
input: []byte("\"unknown\""),
|
||||||
|
want: Unknown,
|
||||||
|
},
|
||||||
|
"aws": {
|
||||||
|
input: []byte("\"aws\""),
|
||||||
|
want: AWS,
|
||||||
|
},
|
||||||
|
"azure": {
|
||||||
|
input: []byte("\"azure\""),
|
||||||
|
want: Azure,
|
||||||
|
},
|
||||||
|
"gcp": {
|
||||||
|
input: []byte("\"gcp\""),
|
||||||
|
want: GCP,
|
||||||
|
},
|
||||||
|
"qemu": {
|
||||||
|
input: []byte("\"qemu\""),
|
||||||
|
want: QEMU,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
var p Provider
|
||||||
|
err := p.UnmarshalJSON(tc.input)
|
||||||
|
|
||||||
|
if tc.wantErr {
|
||||||
|
assert.Error(err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal(tc.want, p)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromString(t *testing.T) {
|
||||||
|
testCases := map[string]struct {
|
||||||
|
input string
|
||||||
|
want Provider
|
||||||
|
}{
|
||||||
|
"empty": {
|
||||||
|
input: "",
|
||||||
|
want: Unknown,
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
input: "unknown",
|
||||||
|
want: Unknown,
|
||||||
|
},
|
||||||
|
"aws": {
|
||||||
|
input: "aws",
|
||||||
|
want: AWS,
|
||||||
|
},
|
||||||
|
"azure": {
|
||||||
|
input: "azure",
|
||||||
|
want: Azure,
|
||||||
|
},
|
||||||
|
"gcp": {
|
||||||
|
input: "gcp",
|
||||||
|
want: GCP,
|
||||||
|
},
|
||||||
|
"qemu": {
|
||||||
|
input: "qemu",
|
||||||
|
want: QEMU,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
p := FromString(tc.input)
|
||||||
|
|
||||||
|
assert.Equal(tc.want, p)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -58,7 +58,6 @@ const (
|
|||||||
//
|
//
|
||||||
// Filenames.
|
// Filenames.
|
||||||
//
|
//
|
||||||
StateFilename = "constellation-state.json"
|
|
||||||
ClusterIDsFileName = "constellation-id.json"
|
ClusterIDsFileName = "constellation-id.json"
|
||||||
ConfigFilename = "constellation-conf.yaml"
|
ConfigFilename = "constellation-conf.yaml"
|
||||||
LicenseFilename = "constellation.license"
|
LicenseFilename = "constellation.license"
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (c) Edgeless Systems GmbH
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
package state
|
|
||||||
|
|
||||||
// ConstellationState is the state of a Constellation.
|
|
||||||
type ConstellationState struct {
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
UID string `json:"uid,omitempty"`
|
|
||||||
CloudProvider string `json:"cloudprovider,omitempty"`
|
|
||||||
LoadBalancerIP string `json:"bootstrapperhost,omitempty"`
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user