mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-25 07:29:38 -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
|
||||
<!-- For now removed features. -->
|
||||
- `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
|
||||
|
||||
|
@ -10,12 +10,10 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||
)
|
||||
|
||||
type terraformClient interface {
|
||||
GetState() state.ConstellationState
|
||||
CreateCluster(ctx context.Context, name string, input terraform.Variables) error
|
||||
CreateCluster(ctx context.Context, name string, input terraform.Variables) (string, error)
|
||||
DestroyCluster(ctx context.Context) error
|
||||
CleanUpWorkspace() error
|
||||
RemoveInstaller()
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||
"go.uber.org/goleak"
|
||||
)
|
||||
|
||||
@ -23,7 +22,7 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
|
||||
type stubTerraformClient struct {
|
||||
state state.ConstellationState
|
||||
ip string
|
||||
cleanUpWorkspaceCalled bool
|
||||
removeInstallerCalled bool
|
||||
destroyClusterCalled bool
|
||||
@ -32,12 +31,8 @@ type stubTerraformClient struct {
|
||||
cleanUpWorkspaceErr error
|
||||
}
|
||||
|
||||
func (c *stubTerraformClient) GetState() state.ConstellationState {
|
||||
return c.state
|
||||
}
|
||||
|
||||
func (c *stubTerraformClient) CreateCluster(ctx context.Context, name string, input terraform.Variables) error {
|
||||
return c.createClusterErr
|
||||
func (c *stubTerraformClient) CreateCluster(ctx context.Context, name string, input terraform.Variables) (string, error) {
|
||||
return c.ip, c.createClusterErr
|
||||
}
|
||||
|
||||
func (c *stubTerraformClient) DestroyCluster(ctx context.Context) error {
|
||||
|
@ -15,11 +15,11 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||
)
|
||||
|
||||
// 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.
|
||||
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 {
|
||||
case cloudprovider.GCP:
|
||||
cl, err := c.newTerraformClient(ctx, provider)
|
||||
if err != nil {
|
||||
return state.ConstellationState{}, err
|
||||
return clusterid.File{}, err
|
||||
}
|
||||
defer cl.RemoveInstaller()
|
||||
return c.createGCP(ctx, cl, config, name, insType, controlPlaneCount, workerCount)
|
||||
case cloudprovider.Azure:
|
||||
cl, err := c.newTerraformClient(ctx, provider)
|
||||
if err != nil {
|
||||
return state.ConstellationState{}, err
|
||||
return clusterid.File{}, err
|
||||
}
|
||||
defer cl.RemoveInstaller()
|
||||
return c.createAzure(ctx, cl, config, name, insType, controlPlaneCount, workerCount)
|
||||
case cloudprovider.QEMU:
|
||||
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)
|
||||
if err != nil {
|
||||
return state.ConstellationState{}, err
|
||||
return clusterid.File{}, err
|
||||
}
|
||||
defer cl.RemoveInstaller()
|
||||
lv := c.newLibvirtRunner()
|
||||
return c.createQEMU(ctx, cl, lv, name, config, controlPlaneCount, workerCount)
|
||||
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,
|
||||
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})
|
||||
|
||||
vars := &terraform.GCPVariables{
|
||||
@ -98,16 +98,20 @@ func (c *Creator) createGCP(ctx context.Context, cl terraformClient, config *con
|
||||
Debug: config.IsDebugCluster(),
|
||||
}
|
||||
|
||||
if err := cl.CreateCluster(ctx, name, vars); err != nil {
|
||||
return state.ConstellationState{}, err
|
||||
ip, err := cl.CreateCluster(ctx, name, vars)
|
||||
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,
|
||||
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})
|
||||
|
||||
vars := &terraform.AzureVariables{
|
||||
@ -127,16 +131,20 @@ func (c *Creator) createAzure(ctx context.Context, cl terraformClient, config *c
|
||||
Debug: config.IsDebugCluster(),
|
||||
}
|
||||
|
||||
if err := cl.CreateCluster(ctx, name, vars); err != nil {
|
||||
return state.ConstellationState{}, err
|
||||
ip, err := cl.CreateCluster(ctx, name, vars)
|
||||
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,
|
||||
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})
|
||||
|
||||
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
|
||||
case libvirtURI == "":
|
||||
if err := lv.Start(ctx, name, config.Provider.QEMU.LibvirtContainerImage); err != nil {
|
||||
return state.ConstellationState{}, err
|
||||
return clusterid.File{}, err
|
||||
}
|
||||
libvirtURI = libvirt.LibvirtTCPConnectURI
|
||||
|
||||
@ -162,11 +170,11 @@ func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirt
|
||||
case strings.HasPrefix(libvirtURI, "qemu+unix://"):
|
||||
unixURI, err := url.Parse(strings.TrimPrefix(libvirtURI, "qemu+unix://"))
|
||||
if err != nil {
|
||||
return state.ConstellationState{}, err
|
||||
return clusterid.File{}, err
|
||||
}
|
||||
libvirtSocketPath = unixURI.Query().Get("socket")
|
||||
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,
|
||||
}
|
||||
|
||||
if err := cl.CreateCluster(ctx, name, vars); err != nil {
|
||||
return state.ConstellationState{}, err
|
||||
ip, err := cl.CreateCluster(ctx, name, vars)
|
||||
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/config"
|
||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCreator(t *testing.T) {
|
||||
failOnNonAMD64 := (runtime.GOARCH != "amd64") || (runtime.GOOS != "linux")
|
||||
|
||||
wantGCPState := state.ConstellationState{
|
||||
CloudProvider: cloudprovider.GCP.String(),
|
||||
LoadBalancerIP: "192.0.2.1",
|
||||
}
|
||||
|
||||
wantQEMUState := state.ConstellationState{
|
||||
CloudProvider: cloudprovider.QEMU.String(),
|
||||
LoadBalancerIP: "192.0.2.1",
|
||||
}
|
||||
|
||||
ip := "192.0.2.1"
|
||||
someErr := errors.New("failed")
|
||||
|
||||
testCases := map[string]struct {
|
||||
@ -40,15 +29,13 @@ func TestCreator(t *testing.T) {
|
||||
libvirt *stubLibvirtRunner
|
||||
provider cloudprovider.Provider
|
||||
config *config.Config
|
||||
wantState state.ConstellationState
|
||||
wantErr bool
|
||||
wantRollback bool // Use only together with stubClients.
|
||||
}{
|
||||
"gcp": {
|
||||
tfClient: &stubTerraformClient{state: wantGCPState},
|
||||
tfClient: &stubTerraformClient{ip: ip},
|
||||
provider: cloudprovider.GCP,
|
||||
config: config.Default(),
|
||||
wantState: wantGCPState,
|
||||
},
|
||||
"gcp newTerraformClient error": {
|
||||
newTfClientErr: someErr,
|
||||
@ -64,11 +51,10 @@ func TestCreator(t *testing.T) {
|
||||
wantRollback: true,
|
||||
},
|
||||
"qemu": {
|
||||
tfClient: &stubTerraformClient{state: wantQEMUState},
|
||||
tfClient: &stubTerraformClient{ip: ip},
|
||||
libvirt: &stubLibvirtRunner{},
|
||||
provider: cloudprovider.QEMU,
|
||||
config: config.Default(),
|
||||
wantState: wantQEMUState,
|
||||
wantErr: failOnNonAMD64,
|
||||
},
|
||||
"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
|
||||
},
|
||||
"qemu start libvirt error": {
|
||||
tfClient: &stubTerraformClient{state: wantQEMUState},
|
||||
tfClient: &stubTerraformClient{ip: ip},
|
||||
libvirt: &stubLibvirtRunner{startErr: someErr},
|
||||
provider: cloudprovider.QEMU,
|
||||
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 {
|
||||
assert.Error(err)
|
||||
@ -129,7 +115,8 @@ func TestCreator(t *testing.T) {
|
||||
}
|
||||
} else {
|
||||
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 (
|
||||
"context"
|
||||
"fmt"
|
||||
"errors"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||
)
|
||||
|
||||
// Terminator deletes cloud provider resources.
|
||||
@ -35,10 +34,18 @@ func NewTerminator() *Terminator {
|
||||
}
|
||||
|
||||
// Terminate deletes the could provider resources defined in the constellation state.
|
||||
func (t *Terminator) Terminate(ctx context.Context, state state.ConstellationState) error {
|
||||
provider := cloudprovider.FromString(state.CloudProvider)
|
||||
func (t *Terminator) Terminate(ctx context.Context, provider cloudprovider.Provider) (retErr error) {
|
||||
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)
|
||||
@ -47,11 +54,6 @@ func (t *Terminator) Terminate(ctx context.Context, state state.ConstellationSta
|
||||
}
|
||||
defer cl.RemoveInstaller()
|
||||
|
||||
if provider == cloudprovider.QEMU {
|
||||
libvirt := t.newLibvirtRunner()
|
||||
return t.terminateQEMU(ctx, cl, libvirt)
|
||||
}
|
||||
|
||||
return t.terminateTerraform(ctx, cl)
|
||||
}
|
||||
|
||||
@ -61,13 +63,3 @@ func (t *Terminator) terminateTerraform(ctx context.Context, cl terraformClient)
|
||||
}
|
||||
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"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -23,53 +22,52 @@ func TestTerminator(t *testing.T) {
|
||||
tfClient terraformClient
|
||||
newTfClientErr error
|
||||
libvirt *stubLibvirtRunner
|
||||
state state.ConstellationState
|
||||
provider cloudprovider.Provider
|
||||
wantErr bool
|
||||
}{
|
||||
"gcp": {
|
||||
tfClient: &stubTerraformClient{},
|
||||
state: state.ConstellationState{CloudProvider: cloudprovider.GCP.String()},
|
||||
provider: cloudprovider.GCP,
|
||||
},
|
||||
"gcp newTfClientErr": {
|
||||
newTfClientErr: someErr,
|
||||
state: state.ConstellationState{CloudProvider: cloudprovider.GCP.String()},
|
||||
provider: cloudprovider.GCP,
|
||||
wantErr: true,
|
||||
},
|
||||
"gcp destroy cluster error": {
|
||||
tfClient: &stubTerraformClient{destroyClusterErr: someErr},
|
||||
state: state.ConstellationState{CloudProvider: cloudprovider.GCP.String()},
|
||||
provider: cloudprovider.GCP,
|
||||
wantErr: true,
|
||||
},
|
||||
"gcp clean up workspace error": {
|
||||
tfClient: &stubTerraformClient{cleanUpWorkspaceErr: someErr},
|
||||
state: state.ConstellationState{CloudProvider: cloudprovider.GCP.String()},
|
||||
provider: cloudprovider.GCP,
|
||||
wantErr: true,
|
||||
},
|
||||
"qemu": {
|
||||
tfClient: &stubTerraformClient{},
|
||||
libvirt: &stubLibvirtRunner{},
|
||||
state: state.ConstellationState{CloudProvider: cloudprovider.QEMU.String()},
|
||||
provider: cloudprovider.QEMU,
|
||||
},
|
||||
"qemu destroy cluster error": {
|
||||
tfClient: &stubTerraformClient{destroyClusterErr: someErr},
|
||||
libvirt: &stubLibvirtRunner{},
|
||||
state: state.ConstellationState{CloudProvider: cloudprovider.QEMU.String()},
|
||||
provider: cloudprovider.QEMU,
|
||||
wantErr: true,
|
||||
},
|
||||
"qemu clean up workspace error": {
|
||||
tfClient: &stubTerraformClient{cleanUpWorkspaceErr: someErr},
|
||||
libvirt: &stubLibvirtRunner{},
|
||||
state: state.ConstellationState{CloudProvider: cloudprovider.QEMU.String()},
|
||||
provider: cloudprovider.QEMU,
|
||||
wantErr: true,
|
||||
},
|
||||
"qemu stop libvirt error": {
|
||||
tfClient: &stubTerraformClient{},
|
||||
libvirt: &stubLibvirtRunner{stopErr: someErr},
|
||||
state: state.ConstellationState{CloudProvider: cloudprovider.QEMU.String()},
|
||||
provider: cloudprovider.QEMU,
|
||||
wantErr: true,
|
||||
},
|
||||
"unknown cloud provider": {
|
||||
state: state.ConstellationState{},
|
||||
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 {
|
||||
assert.Error(err)
|
||||
@ -96,7 +94,7 @@ func TestTerminator(t *testing.T) {
|
||||
cl := tc.tfClient.(*stubTerraformClient)
|
||||
assert.True(cl.destroyClusterCalled)
|
||||
assert.True(cl.removeInstallerCalled)
|
||||
if cloudprovider.FromString(tc.state.CloudProvider) == cloudprovider.QEMU {
|
||||
if tc.provider == cloudprovider.QEMU {
|
||||
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 (
|
||||
"context"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||
)
|
||||
|
||||
type cloudCreator interface {
|
||||
@ -21,9 +21,9 @@ type cloudCreator interface {
|
||||
config *config.Config,
|
||||
name, insType string,
|
||||
coordCount, nodeCount int,
|
||||
) (state.ConstellationState, error)
|
||||
) (clusterid.File, error)
|
||||
}
|
||||
|
||||
type cloudTerminator interface {
|
||||
Terminate(context.Context, state.ConstellationState) error
|
||||
Terminate(context.Context, cloudprovider.Provider) error
|
||||
}
|
||||
|
@ -10,9 +10,9 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||
"go.uber.org/goleak"
|
||||
)
|
||||
|
||||
@ -25,7 +25,7 @@ func TestMain(m *testing.M) {
|
||||
|
||||
type stubCloudCreator struct {
|
||||
createCalled bool
|
||||
state state.ConstellationState
|
||||
id clusterid.File
|
||||
createErr error
|
||||
}
|
||||
|
||||
@ -35,9 +35,10 @@ func (c *stubCloudCreator) Create(
|
||||
config *config.Config,
|
||||
name, insType string,
|
||||
coordCount, nodeCount int,
|
||||
) (state.ConstellationState, error) {
|
||||
) (clusterid.File, error) {
|
||||
c.createCalled = true
|
||||
return c.state, c.createErr
|
||||
c.id.CloudProvider = provider
|
||||
return c.id, c.createErr
|
||||
}
|
||||
|
||||
type stubCloudTerminator struct {
|
||||
@ -45,7 +46,7 @@ type stubCloudTerminator struct {
|
||||
terminateErr error
|
||||
}
|
||||
|
||||
func (c *stubCloudTerminator) Terminate(context.Context, state.ConstellationState) error {
|
||||
func (c *stubCloudTerminator) Terminate(context.Context, cloudprovider.Provider) error {
|
||||
c.called = true
|
||||
return c.terminateErr
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ import (
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@ -118,17 +117,13 @@ func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler,
|
||||
}
|
||||
|
||||
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()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := fileHandler.WriteJSON(constants.StateFilename, state, file.OptNone); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := writeIPtoIDFile(fileHandler, state); err != nil {
|
||||
if err := fileHandler.WriteJSON(constants.ClusterIDsFileName, idFile, file.OptNone); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -195,9 +190,6 @@ type createFlags struct {
|
||||
|
||||
// checkDirClean checks if files of a previous Constellation are left in the current working dir.
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -13,11 +13,11 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"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)))
|
||||
return fs
|
||||
}
|
||||
testState := state.ConstellationState{Name: "test", LoadBalancerIP: "192.0.2.1"}
|
||||
idFile := clusterid.File{IP: "192.0.2.1"}
|
||||
someErr := errors.New("failed")
|
||||
|
||||
testCases := map[string]struct {
|
||||
@ -48,7 +48,7 @@ func TestCreate(t *testing.T) {
|
||||
}{
|
||||
"create": {
|
||||
setupFs: fsWithDefaultConfig,
|
||||
creator: &stubCloudCreator{state: testState},
|
||||
creator: &stubCloudCreator{id: idFile},
|
||||
provider: cloudprovider.GCP,
|
||||
controllerCountFlag: intPtr(1),
|
||||
workerCountFlag: intPtr(2),
|
||||
@ -56,7 +56,7 @@ func TestCreate(t *testing.T) {
|
||||
},
|
||||
"interactive": {
|
||||
setupFs: fsWithDefaultConfig,
|
||||
creator: &stubCloudCreator{state: testState},
|
||||
creator: &stubCloudCreator{id: idFile},
|
||||
provider: cloudprovider.Azure,
|
||||
controllerCountFlag: intPtr(2),
|
||||
workerCountFlag: intPtr(1),
|
||||
@ -119,21 +119,6 @@ func TestCreate(t *testing.T) {
|
||||
controllerCountFlag: intPtr(3),
|
||||
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": {
|
||||
setupFs: func(require *require.Assertions, csp cloudprovider.Provider) afero.Fs {
|
||||
fs := afero.NewMemMapFs()
|
||||
@ -237,11 +222,12 @@ func TestCreate(t *testing.T) {
|
||||
assert.False(tc.creator.createCalled)
|
||||
} else {
|
||||
assert.True(tc.creator.createCalled)
|
||||
var state state.ConstellationState
|
||||
require.NoError(fileHandler.ReadJSON(constants.StateFilename, &state))
|
||||
var idFile clusterIDsFile
|
||||
require.NoError(fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile))
|
||||
assert.Equal(state, testState)
|
||||
var gotIDFile clusterid.File
|
||||
require.NoError(fileHandler.ReadJSON(constants.ClusterIDsFileName, &gotIDFile))
|
||||
assert.Equal(gotIDFile, clusterid.File{
|
||||
IP: idFile.IP,
|
||||
CloudProvider: tc.provider,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -267,14 +253,9 @@ func TestCheckDirClean(t *testing.T) {
|
||||
existingFiles: []string{constants.MasterSecretFilename},
|
||||
wantErr: true,
|
||||
},
|
||||
"state file exists": {
|
||||
fileHandler: file.NewHandler(afero.NewMemMapFs()),
|
||||
existingFiles: []string{constants.StateFilename},
|
||||
wantErr: true,
|
||||
},
|
||||
"multiple exist": {
|
||||
fileHandler: file.NewHandler(afero.NewMemMapFs()),
|
||||
existingFiles: []string{constants.AdminConfFilename, constants.MasterSecretFilename, constants.StateFilename},
|
||||
existingFiles: []string{constants.AdminConfFilename, constants.MasterSecretFilename},
|
||||
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/cli/internal/cloudcmd"
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/helm"
|
||||
"github.com/edgelesssys/constellation/v2/internal/azureshared"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
@ -48,7 +49,6 @@ func NewInitCmd() *cobra.Command {
|
||||
RunE: runInitialize,
|
||||
}
|
||||
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")
|
||||
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,
|
||||
fileHandler file.Handler, helmLoader helmLoader, quotaChecker license.QuotaChecker, spinner spinnerInterf,
|
||||
) error {
|
||||
flags, err := evalFlagArgs(cmd, fileHandler)
|
||||
flags, err := evalFlagArgs(cmd)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
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),
|
||||
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()
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
@ -190,7 +196,7 @@ func (d *initDoer) Do(ctx context.Context) error {
|
||||
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")
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
idFile := clusterIDsFile{
|
||||
ClusterID: clusterID,
|
||||
OwnerID: ownerID,
|
||||
IP: ip,
|
||||
}
|
||||
idFile.OwnerID = ownerID
|
||||
idFile.ClusterID = clusterID
|
||||
|
||||
if err := fileHandler.WriteJSON(constants.ClusterIDsFileName, idFile, file.OptOverwrite); err != nil {
|
||||
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
|
||||
// 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")
|
||||
if err != nil {
|
||||
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")
|
||||
if err != nil {
|
||||
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{
|
||||
configPath: configPath,
|
||||
endpoint: endpoint,
|
||||
conformance: conformance,
|
||||
masterSecretPath: masterSecretPath,
|
||||
}, nil
|
||||
@ -285,7 +278,6 @@ func evalFlagArgs(cmd *cobra.Command, fileHandler file.Handler) (initFlags, erro
|
||||
type initFlags struct {
|
||||
configPath string
|
||||
masterSecretPath string
|
||||
endpoint string
|
||||
conformance bool
|
||||
}
|
||||
|
||||
@ -334,7 +326,7 @@ func readOrGenerateMasterSecret(writer io.Writer, fileHandler file.Handler, file
|
||||
}
|
||||
|
||||
func readIPFromIDFile(fileHandler file.Handler) (string, error) {
|
||||
var idFile clusterIDsFile
|
||||
var idFile clusterid.File
|
||||
if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -20,8 +20,8 @@ import (
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/bootstrapper/initproto"
|
||||
"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/cloudtypes"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"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/license"
|
||||
"github.com/edgelesssys/constellation/v2/internal/oid"
|
||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -48,18 +47,9 @@ func TestInitArgumentValidation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInitialize(t *testing.T) {
|
||||
testGcpState := &state.ConstellationState{
|
||||
CloudProvider: "GCP",
|
||||
}
|
||||
gcpServiceAccKey := &gcpshared.ServiceAccountKey{
|
||||
Type: "service_account",
|
||||
}
|
||||
testAzureState := &state.ConstellationState{
|
||||
CloudProvider: "Azure",
|
||||
}
|
||||
testQemuState := &state.ConstellationState{
|
||||
CloudProvider: "QEMU",
|
||||
}
|
||||
testInitResp := &initproto.InitResponse{
|
||||
Kubeconfig: []byte("kubeconfig"),
|
||||
OwnerId: []byte("ownerID"),
|
||||
@ -69,59 +59,51 @@ func TestInitialize(t *testing.T) {
|
||||
someErr := errors.New("failed")
|
||||
|
||||
testCases := map[string]struct {
|
||||
state *state.ConstellationState
|
||||
idFile *clusterIDsFile
|
||||
provider cloudprovider.Provider
|
||||
idFile *clusterid.File
|
||||
configMutator func(*config.Config)
|
||||
serviceAccKey *gcpshared.ServiceAccountKey
|
||||
helmLoader stubHelmLoader
|
||||
initServerAPI *stubInitServer
|
||||
endpointFlag string
|
||||
masterSecretShouldExist bool
|
||||
wantErr bool
|
||||
}{
|
||||
"initialize some gcp instances": {
|
||||
state: testGcpState,
|
||||
idFile: &clusterIDsFile{IP: "192.0.2.1"},
|
||||
provider: cloudprovider.GCP,
|
||||
idFile: &clusterid.File{IP: "192.0.2.1"},
|
||||
configMutator: func(c *config.Config) { c.Provider.GCP.ServiceAccountKeyPath = serviceAccPath },
|
||||
serviceAccKey: gcpServiceAccKey,
|
||||
initServerAPI: &stubInitServer{initResp: testInitResp},
|
||||
},
|
||||
"initialize some azure instances": {
|
||||
state: testAzureState,
|
||||
idFile: &clusterIDsFile{IP: "192.0.2.1"},
|
||||
provider: cloudprovider.Azure,
|
||||
idFile: &clusterid.File{IP: "192.0.2.1"},
|
||||
initServerAPI: &stubInitServer{initResp: testInitResp},
|
||||
},
|
||||
"initialize some qemu instances": {
|
||||
state: testQemuState,
|
||||
idFile: &clusterIDsFile{IP: "192.0.2.1"},
|
||||
provider: cloudprovider.QEMU,
|
||||
idFile: &clusterid.File{IP: "192.0.2.1"},
|
||||
initServerAPI: &stubInitServer{initResp: testInitResp},
|
||||
},
|
||||
"initialize with endpoint flag": {
|
||||
state: testGcpState,
|
||||
configMutator: func(c *config.Config) { c.Provider.GCP.ServiceAccountKeyPath = serviceAccPath },
|
||||
serviceAccKey: gcpServiceAccKey,
|
||||
initServerAPI: &stubInitServer{initResp: testInitResp},
|
||||
endpointFlag: "192.0.2.1",
|
||||
},
|
||||
"empty state": {
|
||||
state: &state.ConstellationState{},
|
||||
idFile: &clusterIDsFile{IP: "192.0.2.1"},
|
||||
"empty id file": {
|
||||
provider: cloudprovider.GCP,
|
||||
idFile: &clusterid.File{},
|
||||
initServerAPI: &stubInitServer{},
|
||||
wantErr: true,
|
||||
},
|
||||
"neither endpoint flag nor id file": {
|
||||
state: &state.ConstellationState{},
|
||||
"no id file": {
|
||||
provider: cloudprovider.GCP,
|
||||
wantErr: true,
|
||||
},
|
||||
"init call fails": {
|
||||
state: testGcpState,
|
||||
idFile: &clusterIDsFile{IP: "192.0.2.1"},
|
||||
provider: cloudprovider.GCP,
|
||||
idFile: &clusterid.File{IP: "192.0.2.1"},
|
||||
initServerAPI: &stubInitServer{initErr: someErr},
|
||||
wantErr: true,
|
||||
},
|
||||
"fail missing enforced PCR": {
|
||||
state: testGcpState,
|
||||
idFile: &clusterIDsFile{IP: "192.0.2.1"},
|
||||
provider: cloudprovider.GCP,
|
||||
idFile: &clusterid.File{IP: "192.0.2.1"},
|
||||
configMutator: func(c *config.Config) {
|
||||
c.Provider.GCP.EnforcedMeasurements = append(c.Provider.GCP.EnforcedMeasurements, 10)
|
||||
},
|
||||
@ -158,22 +140,17 @@ func TestInitialize(t *testing.T) {
|
||||
|
||||
// Flags
|
||||
cmd.Flags().String("config", constants.ConfigFilename, "") // register persistent flag manually
|
||||
if tc.endpointFlag != "" {
|
||||
require.NoError(cmd.Flags().Set("endpoint", tc.endpointFlag))
|
||||
}
|
||||
|
||||
// File system preparation
|
||||
fs := afero.NewMemMapFs()
|
||||
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 {
|
||||
tc.configMutator(config)
|
||||
}
|
||||
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 {
|
||||
tc.idFile.CloudProvider = tc.provider
|
||||
require.NoError(fileHandler.WriteJSON(constants.ClusterIDsFileName, tc.idFile, file.OptNone))
|
||||
}
|
||||
if tc.serviceAccKey != nil {
|
||||
@ -218,17 +195,22 @@ func TestWriteOutput(t *testing.T) {
|
||||
ownerID := base64.StdEncoding.EncodeToString(resp.OwnerId)
|
||||
clusterID := base64.StdEncoding.EncodeToString(resp.ClusterId)
|
||||
|
||||
expectedIDFile := clusterIDsFile{
|
||||
expectedIDFile := clusterid.File{
|
||||
ClusterID: clusterID,
|
||||
OwnerID: ownerID,
|
||||
IP: "cluster-ip",
|
||||
UID: "test-uid",
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
testFs := afero.NewMemMapFs()
|
||||
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.Contains(out.String(), ownerID)
|
||||
assert.Contains(out.String(), clusterID)
|
||||
@ -241,7 +223,7 @@ func TestWriteOutput(t *testing.T) {
|
||||
|
||||
idsFile, err := afs.ReadFile(constants.ClusterIDsFileName)
|
||||
assert.NoError(err)
|
||||
var testIDFile clusterIDsFile
|
||||
var testIDFile clusterid.File
|
||||
err = json.Unmarshal(idsFile, &testIDFile)
|
||||
assert.NoError(err)
|
||||
assert.Equal(expectedIDFile, testIDFile)
|
||||
@ -366,8 +348,7 @@ func TestAttestation(t *testing.T) {
|
||||
OwnerId: []byte("ownerID"),
|
||||
ClusterId: []byte("clusterID"),
|
||||
}}
|
||||
existingState := state.ConstellationState{CloudProvider: "QEMU"}
|
||||
existingIDFile := &clusterIDsFile{IP: "192.0.2.4"}
|
||||
existingIDFile := &clusterid.File{IP: "192.0.2.4", CloudProvider: cloudprovider.QEMU}
|
||||
|
||||
netDialer := testdialer.NewBufconnDialer()
|
||||
newDialer := func(v *cloudcmd.Validator) *dialer.Dialer {
|
||||
@ -404,7 +385,6 @@ func TestAttestation(t *testing.T) {
|
||||
|
||||
fs := afero.NewMemMapFs()
|
||||
fileHandler := file.NewHandler(fs)
|
||||
require.NoError(fileHandler.WriteJSON(constants.StateFilename, existingState, file.OptNone))
|
||||
require.NoError(fileHandler.WriteJSON(constants.ClusterIDsFileName, existingIDFile, file.OptNone))
|
||||
|
||||
cfg := config.Default()
|
||||
|
@ -11,10 +11,10 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"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/file"
|
||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/cobra"
|
||||
"go.uber.org/multierr"
|
||||
@ -45,14 +45,14 @@ func runDown(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
func checkForMiniCluster(fileHandler file.Handler) error {
|
||||
var state state.ConstellationState
|
||||
if err := fileHandler.ReadJSON(constants.StateFilename, &state); err != nil {
|
||||
var idFile clusterid.File
|
||||
if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err != nil {
|
||||
return err
|
||||
}
|
||||
if cloudprovider.FromString(state.CloudProvider) != cloudprovider.QEMU {
|
||||
if idFile.CloudProvider != cloudprovider.QEMU {
|
||||
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")
|
||||
}
|
||||
|
||||
|
@ -199,15 +199,14 @@ func prepareConfig(cmd *cobra.Command, fileHandler file.Handler) (*config.Config
|
||||
|
||||
// createMiniCluster creates a new cluster using the given config.
|
||||
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 {
|
||||
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.
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"time"
|
||||
|
||||
"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/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
@ -226,7 +227,7 @@ func TestParseRecoverFlags(t *testing.T) {
|
||||
|
||||
fileHandler := file.NewHandler(afero.NewMemMapFs())
|
||||
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)
|
||||
|
@ -16,9 +16,9 @@ import (
|
||||
"go.uber.org/multierr"
|
||||
|
||||
"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/file"
|
||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||
)
|
||||
|
||||
// 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,
|
||||
) error {
|
||||
var stat state.ConstellationState
|
||||
if err := fileHandler.ReadJSON(constants.StateFilename, &stat); err != nil {
|
||||
return fmt.Errorf("reading Constellation state: %w", err)
|
||||
var idFile clusterid.File
|
||||
if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
spinner.Start("Terminating", false)
|
||||
err := terminator.Terminate(cmd.Context(), stat)
|
||||
err := terminator.Terminate(cmd.Context(), idFile.CloudProvider)
|
||||
spinner.Stop()
|
||||
if err != nil {
|
||||
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.")
|
||||
|
||||
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) {
|
||||
retErr = multierr.Append(err, fmt.Errorf("failed to remove file: '%s', please remove it manually", constants.AdminConfFilename))
|
||||
}
|
||||
|
@ -11,9 +11,10 @@ import (
|
||||
"errors"
|
||||
"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/file"
|
||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -46,47 +47,46 @@ func TestTerminateCmdArgumentValidation(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()
|
||||
fileHandler := file.NewHandler(fs)
|
||||
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.ClusterIDsFileName, []byte{1, 2}, file.OptNone))
|
||||
require.NoError(fileHandler.WriteJSON(constants.StateFilename, state, file.OptNone))
|
||||
require.NoError(fileHandler.WriteJSON(constants.ClusterIDsFileName, idFile, file.OptNone))
|
||||
return fs
|
||||
}
|
||||
someErr := errors.New("failed")
|
||||
|
||||
testCases := map[string]struct {
|
||||
state state.ConstellationState
|
||||
setupFs func(*require.Assertions, state.ConstellationState) afero.Fs
|
||||
idFile clusterid.File
|
||||
setupFs func(*require.Assertions, clusterid.File) afero.Fs
|
||||
terminator spyCloudTerminator
|
||||
wantErr bool
|
||||
}{
|
||||
"success": {
|
||||
state: state.ConstellationState{CloudProvider: "gcp"},
|
||||
idFile: clusterid.File{CloudProvider: cloudprovider.GCP},
|
||||
setupFs: setupFs,
|
||||
terminator: &stubCloudTerminator{},
|
||||
},
|
||||
"files to remove do not exist": {
|
||||
state: state.ConstellationState{CloudProvider: "gcp"},
|
||||
setupFs: func(require *require.Assertions, state state.ConstellationState) afero.Fs {
|
||||
idFile: clusterid.File{CloudProvider: cloudprovider.GCP},
|
||||
setupFs: func(require *require.Assertions, idFile clusterid.File) afero.Fs {
|
||||
fs := afero.NewMemMapFs()
|
||||
fileHandler := file.NewHandler(fs)
|
||||
require.NoError(fileHandler.WriteJSON(constants.StateFilename, state, file.OptNone))
|
||||
require.NoError(fileHandler.WriteJSON(constants.ClusterIDsFileName, idFile, file.OptNone))
|
||||
return fs
|
||||
},
|
||||
terminator: &stubCloudTerminator{},
|
||||
},
|
||||
"terminate error": {
|
||||
state: state.ConstellationState{CloudProvider: "gcp"},
|
||||
idFile: clusterid.File{CloudProvider: cloudprovider.GCP},
|
||||
setupFs: setupFs,
|
||||
terminator: &stubCloudTerminator{terminateErr: someErr},
|
||||
wantErr: true,
|
||||
},
|
||||
"missing state file": {
|
||||
state: state.ConstellationState{CloudProvider: "gcp"},
|
||||
setupFs: func(require *require.Assertions, state state.ConstellationState) afero.Fs {
|
||||
idFile: clusterid.File{CloudProvider: cloudprovider.GCP},
|
||||
setupFs: func(require *require.Assertions, idFile clusterid.File) afero.Fs {
|
||||
fs := afero.NewMemMapFs()
|
||||
fileHandler := file.NewHandler(fs)
|
||||
require.NoError(fileHandler.Write(constants.AdminConfFilename, []byte{1, 2}, file.OptNone))
|
||||
@ -97,9 +97,9 @@ func TestTerminate(t *testing.T) {
|
||||
wantErr: true,
|
||||
},
|
||||
"remove file fails": {
|
||||
state: state.ConstellationState{CloudProvider: "gcp"},
|
||||
setupFs: func(require *require.Assertions, state state.ConstellationState) afero.Fs {
|
||||
fs := setupFs(require, state)
|
||||
idFile: clusterid.File{CloudProvider: cloudprovider.GCP},
|
||||
setupFs: func(require *require.Assertions, idFile clusterid.File) afero.Fs {
|
||||
fs := setupFs(require, idFile)
|
||||
return afero.NewReadOnlyFs(fs)
|
||||
},
|
||||
terminator: &stubCloudTerminator{},
|
||||
@ -117,7 +117,7 @@ func TestTerminate(t *testing.T) {
|
||||
cmd.SetErr(&bytes.Buffer{})
|
||||
|
||||
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{})
|
||||
|
||||
@ -126,8 +126,6 @@ func TestTerminate(t *testing.T) {
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
assert.True(tc.terminator.Called())
|
||||
_, err := fileHandler.Stat(constants.StateFilename)
|
||||
assert.Error(err)
|
||||
_, err = fileHandler.Stat(constants.AdminConfFilename)
|
||||
assert.Error(err)
|
||||
_, err = fileHandler.Stat(constants.WGQuickConfigFilename)
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"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/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/crypto"
|
||||
@ -117,7 +118,7 @@ func parseVerifyFlags(cmd *cobra.Command, fileHandler file.Handler) (verifyFlags
|
||||
emptyEndpoint := endpoint == ""
|
||||
emptyIDs := ownerID == "" && clusterID == ""
|
||||
if emptyEndpoint || emptyIDs {
|
||||
var idFile clusterIDsFile
|
||||
var idFile clusterid.File
|
||||
if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err == nil {
|
||||
if emptyEndpoint {
|
||||
cmd.Printf("Using endpoint from %q. Specify --node-endpoint to override this.\n", constants.ClusterIDsFileName)
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||
"github.com/edgelesssys/constellation/v2/internal/atls"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
@ -44,7 +45,7 @@ func TestVerify(t *testing.T) {
|
||||
configFlag string
|
||||
ownerIDFlag string
|
||||
clusterIDFlag string
|
||||
idFile *clusterIDsFile
|
||||
idFile *clusterid.File
|
||||
wantEndpoint string
|
||||
wantErr bool
|
||||
}{
|
||||
@ -79,7 +80,7 @@ func TestVerify(t *testing.T) {
|
||||
provider: cloudprovider.GCP,
|
||||
clusterIDFlag: zeroBase64,
|
||||
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),
|
||||
},
|
||||
"override endpoint from details file": {
|
||||
@ -87,7 +88,7 @@ func TestVerify(t *testing.T) {
|
||||
nodeEndpointFlag: "192.0.2.2:1234",
|
||||
clusterIDFlag: zeroBase64,
|
||||
protoClient: &stubVerifyClient{},
|
||||
idFile: &clusterIDsFile{IP: "192.0.2.1"},
|
||||
idFile: &clusterid.File{IP: "192.0.2.1"},
|
||||
wantEndpoint: "192.0.2.2:1234",
|
||||
},
|
||||
"invalid endpoint": {
|
||||
@ -106,7 +107,7 @@ func TestVerify(t *testing.T) {
|
||||
provider: cloudprovider.GCP,
|
||||
nodeEndpointFlag: "192.0.2.1:1234",
|
||||
protoClient: &stubVerifyClient{},
|
||||
idFile: &clusterIDsFile{OwnerID: zeroBase64},
|
||||
idFile: &clusterid.File{OwnerID: zeroBase64},
|
||||
wantEndpoint: "192.0.2.1:1234",
|
||||
},
|
||||
"config file not existing": {
|
||||
|
@ -12,7 +12,6 @@ import (
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||
"github.com/edgelesssys/constellation/v2/internal/state"
|
||||
"github.com/hashicorp/go-version"
|
||||
install "github.com/hashicorp/hc-install"
|
||||
"github.com/hashicorp/hc-install/fs"
|
||||
@ -36,7 +35,6 @@ type Client struct {
|
||||
provider cloudprovider.Provider
|
||||
|
||||
file file.Handler
|
||||
state state.ConstellationState
|
||||
remove func()
|
||||
}
|
||||
|
||||
@ -58,43 +56,38 @@ func New(ctx context.Context, provider cloudprovider.Provider) (*Client, error)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := c.tf.Init(ctx); err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := c.file.Write(terraformVarsFile, []byte(vars.String())); err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := c.tf.Apply(ctx); err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
tfState, err := c.tf.Show(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
ipOutput, ok := tfState.Values.Outputs["ip"]
|
||||
if !ok {
|
||||
return errors.New("no IP output found")
|
||||
return "", errors.New("no IP output found")
|
||||
}
|
||||
ip, ok := ipOutput.Value.(string)
|
||||
if !ok {
|
||||
return errors.New("invalid type in IP output: not a string")
|
||||
}
|
||||
c.state = state.ConstellationState{
|
||||
Name: name,
|
||||
CloudProvider: c.provider.String(),
|
||||
LoadBalancerIP: ip,
|
||||
return "", errors.New("invalid type in IP output: not a string")
|
||||
}
|
||||
|
||||
return nil
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
// DestroyInstances destroys a Constellation cluster using Terraform.
|
||||
@ -132,11 +125,6 @@ func (c *Client) CleanUpWorkspace() error {
|
||||
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,
|
||||
// or downloads the latest version fulfilling the version constraint.
|
||||
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),
|
||||
}
|
||||
|
||||
err := c.CreateCluster(context.Background(), "test", tc.vars)
|
||||
ip, err := c.CreateCluster(context.Background(), "test", tc.vars)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
assert.NoError(err)
|
||||
assert.Equal("192.0.2.100", ip)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
package cloudprovider
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//go:generate stringer -type=Provider
|
||||
|
||||
@ -21,6 +24,21 @@ const (
|
||||
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.
|
||||
func FromString(s string) Provider {
|
||||
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.
|
||||
//
|
||||
StateFilename = "constellation-state.json"
|
||||
ClusterIDsFileName = "constellation-id.json"
|
||||
ConfigFilename = "constellation-conf.yaml"
|
||||
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