Skip to content

Commit

Permalink
write infrastructure state during create
Browse files Browse the repository at this point in the history
  • Loading branch information
elchead committed Sep 8, 2023
1 parent 9765003 commit f4d7938
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 32 deletions.
2 changes: 1 addition & 1 deletion cli/internal/cloudcmd/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd",
visibility = ["//cli:__subpackages__"],
deps = [
"//cli/internal/clusterid",
"//cli/internal/cmd/pathprefix",
"//cli/internal/libvirt",
"//cli/internal/state",
"//cli/internal/terraform",
"//internal/atls",
"//internal/attestation/choose",
Expand Down
46 changes: 30 additions & 16 deletions cli/internal/cloudcmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ 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/state"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config"
Expand Down Expand Up @@ -64,19 +64,19 @@ type CreateOptions struct {
}

// Create creates the handed amount of instances and all the needed resources.
func (c *Creator) Create(ctx context.Context, opts CreateOptions) (clusterid.File, error) {
func (c *Creator) Create(ctx context.Context, opts CreateOptions) (state.Infrastructure, error) {
provider := opts.Config.GetProvider()
attestationVariant := opts.Config.GetAttestationConfig().GetVariant()
region := opts.Config.GetRegion()
image, err := c.image.FetchReference(ctx, provider, attestationVariant, opts.Config.Image, region)
if err != nil {
return clusterid.File{}, fmt.Errorf("fetching image reference: %w", err)
return state.Infrastructure{}, fmt.Errorf("fetching image reference: %w", err)
}
opts.image = image

cl, err := c.newTerraformClient(ctx, opts.TFWorkspace)
if err != nil {
return clusterid.File{}, err
return state.Infrastructure{}, err
}
defer cl.RemoveInstaller()

Expand All @@ -96,7 +96,7 @@ func (c *Creator) Create(ctx context.Context, opts CreateOptions) (clusterid.Fil
tfOutput, err = c.createOpenStack(ctx, cl, opts)
case cloudprovider.QEMU:
if runtime.GOARCH != "amd64" || runtime.GOOS != "linux" {
return clusterid.File{}, fmt.Errorf("creation of a QEMU based Constellation is not supported for %s/%s", runtime.GOOS, runtime.GOARCH)
return state.Infrastructure{}, fmt.Errorf("creation of a QEMU based Constellation is not supported for %s/%s", runtime.GOOS, runtime.GOARCH)
}
lv := c.newLibvirtRunner()
qemuOpts := qemuCreateOptions{
Expand All @@ -106,23 +106,37 @@ func (c *Creator) Create(ctx context.Context, opts CreateOptions) (clusterid.Fil

tfOutput, err = c.createQEMU(ctx, cl, lv, qemuOpts)
default:
return clusterid.File{}, fmt.Errorf("unsupported cloud provider: %s", opts.Provider)
return state.Infrastructure{}, fmt.Errorf("unsupported cloud provider: %s", opts.Provider)
}

if err != nil {
return clusterid.File{}, fmt.Errorf("creating cluster: %w", err)
return state.Infrastructure{}, fmt.Errorf("creating cluster: %w", err)
}
res := clusterid.File{
CloudProvider: opts.Provider,
IP: tfOutput.IP,
APIServerCertSANs: tfOutput.APIServerCertSANs,
InitSecret: []byte(tfOutput.Secret),
UID: tfOutput.UID,
return convertToInfrastructure(tfOutput), nil
}

func convertToInfrastructure(applyOutput terraform.ApplyOutput) state.Infrastructure {
var infra state.Infrastructure
infra.UID = applyOutput.UID
infra.PublicIP = applyOutput.IP
infra.InitSecret = applyOutput.Secret
infra.APIServerCertSANs = applyOutput.APIServerCertSANs

if applyOutput.Azure != nil {
infra.Azure.ResourceGroup = applyOutput.Azure.ResourceGroup
infra.Azure.SubscriptionID = applyOutput.Azure.SubscriptionID
infra.Azure.NetworkSecurityGroupName = applyOutput.Azure.NetworkSecurityGroupName
infra.Azure.LoadBalancerName = applyOutput.Azure.LoadBalancerName
infra.Azure.UserAssignedIdentity = applyOutput.Azure.UserAssignedIdentity
infra.Azure.AttestationURL = applyOutput.Azure.AttestationURL
}
if tfOutput.Azure != nil {
res.AttestationURL = tfOutput.Azure.AttestationURL

if applyOutput.GCP != nil {
infra.GCP.ProjectID = applyOutput.GCP.ProjectID
infra.GCP.IPCidrNode = applyOutput.GCP.IPCidrNode
infra.GCP.IPCidrPod = applyOutput.GCP.IPCidrPod
}
return res, nil
return infra
}

func (c *Creator) createAWS(ctx context.Context, cl tfResourceClient, opts CreateOptions) (tfOutput terraform.ApplyOutput, retErr error) {
Expand Down
3 changes: 1 addition & 2 deletions cli/internal/cloudcmd/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,7 @@ func TestCreator(t *testing.T) {
}
} else {
assert.NoError(err)
assert.Equal(tc.provider, idFile.CloudProvider)
assert.Equal(ip, idFile.IP)
assert.Equal(ip, idFile.PublicIP)
}
})
}
Expand Down
2 changes: 2 additions & 0 deletions cli/internal/cmd/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ go_library(
"//cli/internal/helm",
"//cli/internal/kubecmd",
"//cli/internal/libvirt",
"//cli/internal/state",
"//cli/internal/terraform",
"//disk-mapper/recoverproto",
"//internal/api/attestationconfigapi",
Expand Down Expand Up @@ -136,6 +137,7 @@ go_test(
"//cli/internal/cmd/pathprefix",
"//cli/internal/helm",
"//cli/internal/kubecmd",
"//cli/internal/state",
"//cli/internal/terraform",
"//disk-mapper/recoverproto",
"//internal/api/attestationconfigapi",
Expand Down
4 changes: 2 additions & 2 deletions cli/internal/cmd/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"context"

"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
"github.com/edgelesssys/constellation/v2/cli/internal/state"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/cloud/gcpshared"
Expand All @@ -20,7 +20,7 @@ type cloudCreator interface {
Create(
ctx context.Context,
opts cloudcmd.CreateOptions,
) (clusterid.File, error)
) (state.Infrastructure, error)
}

type cloudIAMCreator interface {
Expand Down
11 changes: 5 additions & 6 deletions cli/internal/cmd/cloud_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"testing"

"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
"github.com/edgelesssys/constellation/v2/cli/internal/state"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/cloud/gcpshared"
Expand All @@ -27,17 +27,16 @@ func TestMain(m *testing.M) {

type stubCloudCreator struct {
createCalled bool
id clusterid.File
state state.Infrastructure
createErr error
}

func (c *stubCloudCreator) Create(
_ context.Context,
opts cloudcmd.CreateOptions,
) (clusterid.File, error) {
_ cloudcmd.CreateOptions,
) (state.Infrastructure, error) {
c.createCalled = true
c.id.CloudProvider = opts.Provider
return c.id, c.createErr
return c.state, c.createErr
}

type stubCloudTerminator struct {
Expand Down
23 changes: 22 additions & 1 deletion cli/internal/cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ import (
"io/fs"

"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
"github.com/edgelesssys/constellation/v2/cli/internal/cmd/pathprefix"
"github.com/edgelesssys/constellation/v2/cli/internal/state"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"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"
Expand Down Expand Up @@ -167,21 +170,39 @@ func (c *createCmd) create(cmd *cobra.Command, creator cloudCreator, fileHandler
TFLogLevel: flags.tfLogLevel,
TFWorkspace: constants.TerraformWorkingDir,
}
idFile, err := creator.Create(cmd.Context(), opts)
infraState, err := creator.Create(cmd.Context(), opts)
spinner.Stop()
if err != nil {
return translateCreateErrors(cmd, c.pf, err)
}
c.log.Debugf("Successfully created the cloud resources for the cluster")

idFile := convertToIDFile(infraState, provider)
if err := fileHandler.WriteJSON(constants.ClusterIDsFilename, idFile, file.OptNone); err != nil {
return err
}
state := state.State{
Version: "v1",
Infrastructure: infraState,
}
if err := fileHandler.WriteYAML(constants.StateFilename, state, file.OptNone); err != nil {
return err
}

cmd.Println("Your Constellation cluster was created successfully.")
return nil
}

func convertToIDFile(infra state.Infrastructure, provider cloudprovider.Provider) clusterid.File {
var file clusterid.File
file.CloudProvider = provider
file.IP = infra.PublicIP
file.APIServerCertSANs = infra.APIServerCertSANs
file.InitSecret = []byte(infra.InitSecret) // Convert string to []byte
file.UID = infra.UID
return file
}

// parseCreateFlags parses the flags of the create command.
func (c *createCmd) parseCreateFlags(cmd *cobra.Command) (createFlags, error) {
yes, err := cmd.Flags().GetBool("yes")
Expand Down
19 changes: 15 additions & 4 deletions cli/internal/cmd/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"testing"

"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
"github.com/edgelesssys/constellation/v2/cli/internal/state"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants"
Expand All @@ -31,7 +32,7 @@ func TestCreate(t *testing.T) {
require.NoError(file.WriteYAML(constants.ConfigFilename, defaultConfigWithExpectedMeasurements(t, config.Default(), provider)))
return fs
}
idFile := clusterid.File{IP: "192.0.2.1"}
idFile := state.Infrastructure{PublicIP: "192.0.2.1"}
someErr := errors.New("failed")

testCases := map[string]struct {
Expand All @@ -47,15 +48,15 @@ func TestCreate(t *testing.T) {
}{
"create": {
setupFs: fsWithDefaultConfig,
creator: &stubCloudCreator{id: idFile},
creator: &stubCloudCreator{state: idFile},
provider: cloudprovider.GCP,
controllerCountFlag: intPtr(1),
workerCountFlag: intPtr(2),
yesFlag: true,
},
"interactive": {
setupFs: fsWithDefaultConfig,
creator: &stubCloudCreator{id: idFile},
creator: &stubCloudCreator{state: idFile},
provider: cloudprovider.Azure,
controllerCountFlag: intPtr(2),
workerCountFlag: intPtr(1),
Expand Down Expand Up @@ -211,9 +212,19 @@ func TestCreate(t *testing.T) {
var gotIDFile clusterid.File
require.NoError(fileHandler.ReadJSON(constants.ClusterIDsFilename, &gotIDFile))
assert.Equal(gotIDFile, clusterid.File{
IP: idFile.IP,
IP: idFile.PublicIP,
CloudProvider: tc.provider,
})

var gotState state.State
expectedState := state.Infrastructure{
PublicIP: "192.0.2.1",
APIServerCertSANs: []string{},
}
require.NoError(fileHandler.ReadYAML(constants.StateFilename, &gotState))
assert.Equal("v1", gotState.Version)
assert.Equal(expectedState, gotState.Infrastructure)

}
}
})
Expand Down
8 changes: 8 additions & 0 deletions cli/internal/state/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")

go_library(
name = "state",
srcs = ["state.go"],
importpath = "github.com/edgelesssys/constellation/v2/cli/internal/state",
visibility = ["//cli:__subpackages__"],
)
41 changes: 41 additions & 0 deletions cli/internal/state/state.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/

// package state defines the structure of the Constellation state file.
package state

// State describe the entire state to describe a Constellation cluster.
type State struct {
Version string `yaml:"version"`
Infrastructure `yaml:"infrastructure"`
}

// Infrastructure describe the state related to the cloud resources of the cluster.
type Infrastructure struct {
UID string `yaml:"uid"`
PublicIP string `yaml:"publicIP"`
InitSecret string `yaml:"initSecret"`
APIServerCertSANs []string `yaml:"apiServerCertSANs"`
Azure AzureState `yaml:"azure"`
GCP GCPState `yaml:"gcp"`
}

// GCPState describes the infra state related to GCP.
type GCPState struct {
ProjectID string `yaml:"projectID"`
IPCidrNode string `yaml:"ipCidrNode"`
IPCidrPod string `yaml:"ipCidrPod"`
}

// AzureState describes the infra state related to Azure.
type AzureState struct {
ResourceGroup string `yaml:"resourceGroup"`
SubscriptionID string `yaml:"subscriptionID"`
NetworkSecurityGroupName string `yaml:"networkSecurityGroupName"`
LoadBalancerName string `yaml:"loadBalancerName"`
UserAssignedIdentity string `yaml:"userAssignedIdentity"`
AttestationURL string `yaml:"attestationURL"`
}
2 changes: 2 additions & 0 deletions internal/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ const (

// ClusterIDsFilename filename that contains Constellation clusterID and IP.
ClusterIDsFilename = "constellation-id.json"
// StateFilename filename that contains the entire state of the Constellation cluster.
StateFilename = "constellation-state.yaml"
// ConfigFilename filename of Constellation config file.
ConfigFilename = "constellation-conf.yaml"
// LicenseFilename filename of Constellation license file.
Expand Down

0 comments on commit f4d7938

Please sign in to comment.