Skip to content

Commit

Permalink
cli: remove TF ApplyOutput dependency in CLI (#2323)
Browse files Browse the repository at this point in the history
  • Loading branch information
elchead authored Sep 25, 2023
1 parent 322c4aa commit 4680882
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 174 deletions.
4 changes: 2 additions & 2 deletions cli/internal/cloudcmd/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type tfCommonClient interface {

type tfResourceClient interface {
tfCommonClient
ApplyCluster(ctx context.Context, provider cloudprovider.Provider, logLevel terraform.LogLevel) (terraform.ApplyOutput, error)
ApplyCluster(ctx context.Context, provider cloudprovider.Provider, logLevel terraform.LogLevel) (state.Infrastructure, error)
ShowInfrastructure(ctx context.Context, provider cloudprovider.Provider) (state.Infrastructure, error)
}

Expand All @@ -56,7 +56,7 @@ type tfIAMUpgradeClient interface {

type tfClusterUpgradeClient interface {
tfUpgradePlanner
ApplyCluster(ctx context.Context, provider cloudprovider.Provider, logLevel terraform.LogLevel) (terraform.ApplyOutput, error)
ApplyCluster(ctx context.Context, provider cloudprovider.Provider, logLevel terraform.LogLevel) (state.Infrastructure, error)
}

type libvirtRunner interface {
Expand Down
12 changes: 6 additions & 6 deletions cli/internal/cloudcmd/clients_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ type stubTerraformClient struct {
showErr error
}

func (c *stubTerraformClient) ApplyCluster(_ context.Context, _ cloudprovider.Provider, _ terraform.LogLevel) (terraform.ApplyOutput, error) {
return terraform.ApplyOutput{
IP: c.ip,
Secret: c.initSecret,
UID: c.uid,
Azure: &terraform.AzureApplyOutput{
func (c *stubTerraformClient) ApplyCluster(_ context.Context, _ cloudprovider.Provider, _ terraform.LogLevel) (state.Infrastructure, error) {
return state.Infrastructure{
ClusterEndpoint: c.ip,
InitSecret: c.initSecret,
UID: c.uid,
Azure: &state.Azure{
AttestationURL: c.attestationURL,
},
}, c.createClusterErr
Expand Down
15 changes: 8 additions & 7 deletions cli/internal/cloudcmd/clusterupgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"path/filepath"
"strings"

"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/constants"
Expand Down Expand Up @@ -70,16 +71,16 @@ func (u *ClusterUpgrader) RestoreClusterWorkspace() error {

// ApplyClusterUpgrade applies the Terraform migrations planned by PlanClusterUpgrade.
// On success, the workspace of the Upgrader replaces the existing Terraform workspace.
func (u *ClusterUpgrader) ApplyClusterUpgrade(ctx context.Context, csp cloudprovider.Provider) (terraform.ApplyOutput, error) {
tfOutput, err := u.tf.ApplyCluster(ctx, csp, u.logLevel)
func (u *ClusterUpgrader) ApplyClusterUpgrade(ctx context.Context, csp cloudprovider.Provider) (state.Infrastructure, error) {
infraState, err := u.tf.ApplyCluster(ctx, csp, u.logLevel)
if err != nil {
return tfOutput, fmt.Errorf("terraform apply: %w", err)
return infraState, fmt.Errorf("terraform apply: %w", err)
}
if tfOutput.Azure != nil {
if err := u.policyPatcher.Patch(ctx, tfOutput.Azure.AttestationURL); err != nil {
return tfOutput, fmt.Errorf("patching policies: %w", err)
if infraState.Azure != nil {
if err := u.policyPatcher.Patch(ctx, infraState.Azure.AttestationURL); err != nil {
return infraState, fmt.Errorf("patching policies: %w", err)
}
}

return tfOutput, nil
return infraState, nil
}
5 changes: 3 additions & 2 deletions cli/internal/cloudcmd/clusterupgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"path/filepath"
"testing"

"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/constants"
Expand Down Expand Up @@ -194,8 +195,8 @@ func (t *tfClusterUpgradeStub) ShowPlan(_ context.Context, _ terraform.LogLevel,
return t.showErr
}

func (t *tfClusterUpgradeStub) ApplyCluster(_ context.Context, _ cloudprovider.Provider, _ terraform.LogLevel) (terraform.ApplyOutput, error) {
return terraform.ApplyOutput{}, t.applyErr
func (t *tfClusterUpgradeStub) ApplyCluster(_ context.Context, _ cloudprovider.Provider, _ terraform.LogLevel) (state.Infrastructure, error) {
return state.Infrastructure{}, t.applyErr
}

func (t *tfClusterUpgradeStub) PrepareUpgradeWorkspace(_, _ string, _ terraform.Variables) error {
Expand Down
62 changes: 31 additions & 31 deletions cli/internal/cloudcmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,20 @@ func (c *Creator) Create(ctx context.Context, opts CreateOptions) (state.Infrast
}
defer cl.RemoveInstaller()

var tfOutput terraform.ApplyOutput
var infraState state.Infrastructure
switch opts.Provider {
case cloudprovider.AWS:

tfOutput, err = c.createAWS(ctx, cl, opts)
infraState, err = c.createAWS(ctx, cl, opts)
case cloudprovider.GCP:

tfOutput, err = c.createGCP(ctx, cl, opts)
infraState, err = c.createGCP(ctx, cl, opts)
case cloudprovider.Azure:

tfOutput, err = c.createAzure(ctx, cl, opts)
infraState, err = c.createAzure(ctx, cl, opts)
case cloudprovider.OpenStack:

tfOutput, err = c.createOpenStack(ctx, cl, opts)
infraState, err = c.createOpenStack(ctx, cl, opts)
case cloudprovider.QEMU:
if runtime.GOARCH != "amd64" || runtime.GOOS != "linux" {
return state.Infrastructure{}, fmt.Errorf("creation of a QEMU based Constellation is not supported for %s/%s", runtime.GOOS, runtime.GOARCH)
Expand All @@ -104,54 +104,54 @@ func (c *Creator) Create(ctx context.Context, opts CreateOptions) (state.Infrast
CreateOptions: opts,
}

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

if err != nil {
return state.Infrastructure{}, fmt.Errorf("creating cluster: %w", err)
}
return terraform.ConvertToInfrastructure(tfOutput), nil
return infraState, nil
}

func (c *Creator) createAWS(ctx context.Context, cl tfResourceClient, opts CreateOptions) (tfOutput terraform.ApplyOutput, retErr error) {
func (c *Creator) createAWS(ctx context.Context, cl tfResourceClient, opts CreateOptions) (tfOutput state.Infrastructure, retErr error) {
vars := awsTerraformVars(opts.Config, opts.image)

tfOutput, err := runTerraformCreate(ctx, cl, cloudprovider.AWS, vars, c.out, opts.TFLogLevel)
if err != nil {
return terraform.ApplyOutput{}, err
return state.Infrastructure{}, err
}

return tfOutput, nil
}

func (c *Creator) createGCP(ctx context.Context, cl tfResourceClient, opts CreateOptions) (tfOutput terraform.ApplyOutput, retErr error) {
func (c *Creator) createGCP(ctx context.Context, cl tfResourceClient, opts CreateOptions) (tfOutput state.Infrastructure, retErr error) {
vars := gcpTerraformVars(opts.Config, opts.image)

tfOutput, err := runTerraformCreate(ctx, cl, cloudprovider.GCP, vars, c.out, opts.TFLogLevel)
if err != nil {
return terraform.ApplyOutput{}, err
return state.Infrastructure{}, err
}

return tfOutput, nil
}

func (c *Creator) createAzure(ctx context.Context, cl tfResourceClient, opts CreateOptions) (tfOutput terraform.ApplyOutput, retErr error) {
func (c *Creator) createAzure(ctx context.Context, cl tfResourceClient, opts CreateOptions) (tfOutput state.Infrastructure, retErr error) {
vars := azureTerraformVars(opts.Config, opts.image)

tfOutput, err := runTerraformCreate(ctx, cl, cloudprovider.Azure, vars, c.out, opts.TFLogLevel)
if err != nil {
return terraform.ApplyOutput{}, err
return state.Infrastructure{}, err
}

if vars.GetCreateMAA() {
// Patch the attestation policy to allow the cluster to boot while having secure boot disabled.
if tfOutput.Azure == nil {
return terraform.ApplyOutput{}, errors.New("no Terraform Azure output found")
return state.Infrastructure{}, errors.New("no Terraform Azure output found")
}
if err := c.policyPatcher.Patch(ctx, tfOutput.Azure.AttestationURL); err != nil {
return terraform.ApplyOutput{}, err
return state.Infrastructure{}, err
}
}

Expand Down Expand Up @@ -189,12 +189,12 @@ func normalizeAzureURIs(vars *terraform.AzureClusterVariables) *terraform.AzureC
return vars
}

func (c *Creator) createOpenStack(ctx context.Context, cl tfResourceClient, opts CreateOptions) (tfOutput terraform.ApplyOutput, retErr error) {
func (c *Creator) createOpenStack(ctx context.Context, cl tfResourceClient, opts CreateOptions) (infraState state.Infrastructure, retErr error) {
if os.Getenv("CONSTELLATION_OPENSTACK_DEV") != "1" {
return terraform.ApplyOutput{}, errors.New("Constellation must be fine-tuned to your OpenStack deployment. Please create an issue or contact Edgeless Systems at https://edgeless.systems/contact/")
return state.Infrastructure{}, errors.New("Constellation must be fine-tuned to your OpenStack deployment. Please create an issue or contact Edgeless Systems at https://edgeless.systems/contact/")
}
if _, hasOSAuthURL := os.LookupEnv("OS_AUTH_URL"); !hasOSAuthURL && opts.Config.Provider.OpenStack.Cloud == "" {
return terraform.ApplyOutput{}, errors.New(
return state.Infrastructure{}, errors.New(
"neither environment variable OS_AUTH_URL nor cloud name for \"clouds.yaml\" is set. OpenStack authentication requires a set of " +
"OS_* environment variables that are typically sourced into the current shell with an openrc file " +
"or a cloud name for \"clouds.yaml\". " +
Expand All @@ -204,23 +204,23 @@ func (c *Creator) createOpenStack(ctx context.Context, cl tfResourceClient, opts

vars := openStackTerraformVars(opts.Config, opts.image)

tfOutput, err := runTerraformCreate(ctx, cl, cloudprovider.OpenStack, vars, c.out, opts.TFLogLevel)
infraState, err := runTerraformCreate(ctx, cl, cloudprovider.OpenStack, vars, c.out, opts.TFLogLevel)
if err != nil {
return terraform.ApplyOutput{}, err
return state.Infrastructure{}, err
}

return tfOutput, nil
return infraState, nil
}

func runTerraformCreate(ctx context.Context, cl tfResourceClient, provider cloudprovider.Provider, vars terraform.Variables, outWriter io.Writer, loglevel terraform.LogLevel) (output terraform.ApplyOutput, retErr error) {
func runTerraformCreate(ctx context.Context, cl tfResourceClient, provider cloudprovider.Provider, vars terraform.Variables, outWriter io.Writer, loglevel terraform.LogLevel) (output state.Infrastructure, retErr error) {
if err := cl.PrepareWorkspace(path.Join("terraform", strings.ToLower(provider.String())), vars); err != nil {
return terraform.ApplyOutput{}, err
return state.Infrastructure{}, err
}

defer rollbackOnError(outWriter, &retErr, &rollbackerTerraform{client: cl}, loglevel)
tfOutput, err := cl.ApplyCluster(ctx, provider, loglevel)
if err != nil {
return terraform.ApplyOutput{}, err
return state.Infrastructure{}, err
}

return tfOutput, nil
Expand All @@ -231,15 +231,15 @@ type qemuCreateOptions struct {
CreateOptions
}

func (c *Creator) createQEMU(ctx context.Context, cl tfResourceClient, lv libvirtRunner, opts qemuCreateOptions) (tfOutput terraform.ApplyOutput, retErr error) {
func (c *Creator) createQEMU(ctx context.Context, cl tfResourceClient, lv libvirtRunner, opts qemuCreateOptions) (tfOutput state.Infrastructure, retErr error) {
qemuRollbacker := &rollbackerQEMU{client: cl, libvirt: lv, createdWorkspace: false}
defer rollbackOnError(c.out, &retErr, qemuRollbacker, opts.TFLogLevel)

// TODO(malt3): render progress bar
downloader := c.newRawDownloader()
imagePath, err := downloader.Download(ctx, c.out, false, opts.source, opts.Config.Image)
if err != nil {
return terraform.ApplyOutput{}, fmt.Errorf("download raw image: %w", err)
return state.Infrastructure{}, fmt.Errorf("download raw image: %w", err)
}

libvirtURI := opts.Config.Provider.QEMU.LibvirtURI
Expand All @@ -249,7 +249,7 @@ func (c *Creator) createQEMU(ctx context.Context, cl tfResourceClient, lv libvir
// if no libvirt URI is specified, start a libvirt container
case libvirtURI == "":
if err := lv.Start(ctx, opts.Config.Name, opts.Config.Provider.QEMU.LibvirtContainerImage); err != nil {
return terraform.ApplyOutput{}, fmt.Errorf("start libvirt container: %w", err)
return state.Infrastructure{}, fmt.Errorf("start libvirt container: %w", err)
}
libvirtURI = libvirt.LibvirtTCPConnectURI

Expand All @@ -265,11 +265,11 @@ func (c *Creator) createQEMU(ctx context.Context, cl tfResourceClient, lv libvir
case strings.HasPrefix(libvirtURI, "qemu+unix://"):
unixURI, err := url.Parse(strings.TrimPrefix(libvirtURI, "qemu+unix://"))
if err != nil {
return terraform.ApplyOutput{}, err
return state.Infrastructure{}, err
}
libvirtSocketPath = unixURI.Query().Get("socket")
if libvirtSocketPath == "" {
return terraform.ApplyOutput{}, fmt.Errorf("socket path not specified in qemu+unix URI: %s", libvirtURI)
return state.Infrastructure{}, fmt.Errorf("socket path not specified in qemu+unix URI: %s", libvirtURI)
}
}

Expand All @@ -285,15 +285,15 @@ func (c *Creator) createQEMU(ctx context.Context, cl tfResourceClient, lv libvir
}

if err := cl.PrepareWorkspace(path.Join("terraform", strings.ToLower(cloudprovider.QEMU.String())), vars); err != nil {
return terraform.ApplyOutput{}, fmt.Errorf("prepare workspace: %w", err)
return state.Infrastructure{}, fmt.Errorf("prepare workspace: %w", err)
}

// Allow rollback of QEMU Terraform workspace from this point on
qemuRollbacker.createdWorkspace = true

tfOutput, err = cl.ApplyCluster(ctx, opts.Provider, opts.TFLogLevel)
if err != nil {
return terraform.ApplyOutput{}, fmt.Errorf("create cluster: %w", err)
return state.Infrastructure{}, fmt.Errorf("create cluster: %w", err)
}

return tfOutput, nil
Expand Down
25 changes: 12 additions & 13 deletions cli/internal/cmd/upgradeapply.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,15 +318,14 @@ func (u *upgradeApplyCmd) migrateTerraform(cmd *cobra.Command, conf *config.Conf
}
}
u.log.Debugf("Applying Terraform migrations")
tfOutput, err := u.clusterUpgrader.ApplyClusterUpgrade(cmd.Context(), conf.GetProvider())
res = terraform.ConvertToInfrastructure(tfOutput)
infraState, err := u.clusterUpgrader.ApplyClusterUpgrade(cmd.Context(), conf.GetProvider())
if err != nil {
return res, fmt.Errorf("applying terraform migrations: %w", err)
return infraState, fmt.Errorf("applying terraform migrations: %w", err)
}

// Apply possible updates to cluster ID file
if err := updateClusterIDFile(tfOutput, u.fileHandler); err != nil {
return res, fmt.Errorf("merging cluster ID files: %w", err)
if err := updateClusterIDFile(infraState, u.fileHandler); err != nil {
return infraState, fmt.Errorf("merging cluster ID files: %w", err)
}

cmd.Printf("Terraform migrations applied successfully and output written to: %s\n"+
Expand Down Expand Up @@ -588,15 +587,15 @@ func parseUpgradeApplyFlags(cmd *cobra.Command) (upgradeApplyFlags, error) {
}, nil
}

func updateClusterIDFile(tfOutput terraform.ApplyOutput, fileHandler file.Handler) error {
func updateClusterIDFile(infraState state.Infrastructure, fileHandler file.Handler) error {
newIDFile := clusterid.File{
InitSecret: []byte(tfOutput.Secret),
IP: tfOutput.IP,
APIServerCertSANs: tfOutput.APIServerCertSANs,
UID: tfOutput.UID,
InitSecret: []byte(infraState.InitSecret),
IP: infraState.ClusterEndpoint,
APIServerCertSANs: infraState.APIServerCertSANs,
UID: infraState.UID,
}
if tfOutput.Azure != nil {
newIDFile.AttestationURL = tfOutput.Azure.AttestationURL
if infraState.Azure != nil {
newIDFile.AttestationURL = infraState.Azure.AttestationURL
}

idFile := &clusterid.File{}
Expand Down Expand Up @@ -650,6 +649,6 @@ type kubernetesUpgrader interface {

type clusterUpgrader interface {
PlanClusterUpgrade(ctx context.Context, outWriter io.Writer, vars terraform.Variables, csp cloudprovider.Provider) (bool, error)
ApplyClusterUpgrade(ctx context.Context, csp cloudprovider.Provider) (terraform.ApplyOutput, error)
ApplyClusterUpgrade(ctx context.Context, csp cloudprovider.Provider) (state.Infrastructure, error)
RestoreClusterWorkspace() error
}
8 changes: 4 additions & 4 deletions cli/internal/cmd/upgradeapply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,8 @@ func (u stubTerraformUpgrader) PlanClusterUpgrade(_ context.Context, _ io.Writer
return u.terraformDiff, u.planTerraformErr
}

func (u stubTerraformUpgrader) ApplyClusterUpgrade(_ context.Context, _ cloudprovider.Provider) (terraform.ApplyOutput, error) {
return terraform.ApplyOutput{}, u.applyTerraformErr
func (u stubTerraformUpgrader) ApplyClusterUpgrade(_ context.Context, _ cloudprovider.Provider) (state.Infrastructure, error) {
return state.Infrastructure{}, u.applyTerraformErr
}

func (u stubTerraformUpgrader) RestoreClusterWorkspace() error {
Expand All @@ -294,9 +294,9 @@ func (m *mockTerraformUpgrader) PlanClusterUpgrade(ctx context.Context, w io.Wri
return args.Bool(0), args.Error(1)
}

func (m *mockTerraformUpgrader) ApplyClusterUpgrade(ctx context.Context, provider cloudprovider.Provider) (terraform.ApplyOutput, error) {
func (m *mockTerraformUpgrader) ApplyClusterUpgrade(ctx context.Context, provider cloudprovider.Provider) (state.Infrastructure, error) {
args := m.Called(ctx, provider)
return args.Get(0).(terraform.ApplyOutput), args.Error(1)
return args.Get(0).(state.Infrastructure), args.Error(1)
}

func (m *mockTerraformUpgrader) RestoreClusterWorkspace() error {
Expand Down
Loading

0 comments on commit 4680882

Please sign in to comment.