From 468088270849029207cc684e3119a6ae756cda38 Mon Sep 17 00:00:00 2001 From: Adrian Stobbe Date: Mon, 25 Sep 2023 17:10:23 +0200 Subject: [PATCH] cli: remove TF ApplyOutput dependency in CLI (#2323) --- cli/internal/cloudcmd/clients.go | 4 +- cli/internal/cloudcmd/clients_test.go | 12 +- cli/internal/cloudcmd/clusterupgrade.go | 15 +- cli/internal/cloudcmd/clusterupgrade_test.go | 5 +- cli/internal/cloudcmd/create.go | 62 ++++---- cli/internal/cmd/upgradeapply.go | 25 ++-- cli/internal/cmd/upgradeapply_test.go | 8 +- cli/internal/terraform/terraform.go | 141 +++++-------------- cli/internal/terraform/terraform_test.go | 10 +- 9 files changed, 108 insertions(+), 174 deletions(-) diff --git a/cli/internal/cloudcmd/clients.go b/cli/internal/cloudcmd/clients.go index 9875d8b6c..a05f2e811 100644 --- a/cli/internal/cloudcmd/clients.go +++ b/cli/internal/cloudcmd/clients.go @@ -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) } @@ -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 { diff --git a/cli/internal/cloudcmd/clients_test.go b/cli/internal/cloudcmd/clients_test.go index b7efc67aa..99b792dc1 100644 --- a/cli/internal/cloudcmd/clients_test.go +++ b/cli/internal/cloudcmd/clients_test.go @@ -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 diff --git a/cli/internal/cloudcmd/clusterupgrade.go b/cli/internal/cloudcmd/clusterupgrade.go index ca5073bb2..0227c3686 100644 --- a/cli/internal/cloudcmd/clusterupgrade.go +++ b/cli/internal/cloudcmd/clusterupgrade.go @@ -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" @@ -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 } diff --git a/cli/internal/cloudcmd/clusterupgrade_test.go b/cli/internal/cloudcmd/clusterupgrade_test.go index 8378ca59d..07c011fe7 100644 --- a/cli/internal/cloudcmd/clusterupgrade_test.go +++ b/cli/internal/cloudcmd/clusterupgrade_test.go @@ -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" @@ -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 { diff --git a/cli/internal/cloudcmd/create.go b/cli/internal/cloudcmd/create.go index 3228d9b36..0c9a43d5e 100644 --- a/cli/internal/cloudcmd/create.go +++ b/cli/internal/cloudcmd/create.go @@ -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) @@ -104,7 +104,7 @@ 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) } @@ -112,46 +112,46 @@ func (c *Creator) Create(ctx context.Context, opts CreateOptions) (state.Infrast 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 } } @@ -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\". " + @@ -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 @@ -231,7 +231,7 @@ 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) @@ -239,7 +239,7 @@ func (c *Creator) createQEMU(ctx context.Context, cl tfResourceClient, lv libvir 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 @@ -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 @@ -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) } } @@ -285,7 +285,7 @@ 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 @@ -293,7 +293,7 @@ func (c *Creator) createQEMU(ctx context.Context, cl tfResourceClient, lv libvir 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 diff --git a/cli/internal/cmd/upgradeapply.go b/cli/internal/cmd/upgradeapply.go index 5ec8fa98d..364d5580b 100644 --- a/cli/internal/cmd/upgradeapply.go +++ b/cli/internal/cmd/upgradeapply.go @@ -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"+ @@ -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{} @@ -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 } diff --git a/cli/internal/cmd/upgradeapply_test.go b/cli/internal/cmd/upgradeapply_test.go index 88fbdefad..37d772480 100644 --- a/cli/internal/cmd/upgradeapply_test.go +++ b/cli/internal/cmd/upgradeapply_test.go @@ -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 { @@ -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 { diff --git a/cli/internal/terraform/terraform.go b/cli/internal/terraform/terraform.go index 3a7202a19..89c1fb32e 100644 --- a/cli/internal/terraform/terraform.go +++ b/cli/internal/terraform/terraform.go @@ -173,96 +173,58 @@ func (c *Client) ShowIAM(ctx context.Context, provider cloudprovider.Provider) ( // ShowInfrastructure reads the state of Constellation cluster resources from Terraform. func (c *Client) ShowInfrastructure(ctx context.Context, provider cloudprovider.Provider) (state.Infrastructure, error) { - tfOutput, err := c.ShowCluster(ctx, provider) - if err != nil { - return state.Infrastructure{}, err - } - return ConvertToInfrastructure(tfOutput), nil -} - -// ConvertToInfrastructure converts the Terraform output of a cluster creation or apply operation to a state.Infrastructure. -func ConvertToInfrastructure(applyOutput ApplyOutput) state.Infrastructure { - var infra state.Infrastructure - infra.UID = applyOutput.UID - infra.ClusterEndpoint = applyOutput.IP - infra.InitSecret = applyOutput.Secret - infra.APIServerCertSANs = applyOutput.APIServerCertSANs - - if applyOutput.Azure != nil { - infra.Azure = &state.Azure{ - ResourceGroup: applyOutput.Azure.ResourceGroup, - SubscriptionID: applyOutput.Azure.SubscriptionID, - UserAssignedIdentity: applyOutput.Azure.UserAssignedIdentity, - NetworkSecurityGroupName: applyOutput.Azure.NetworkSecurityGroupName, - LoadBalancerName: applyOutput.Azure.LoadBalancerName, - AttestationURL: applyOutput.Azure.AttestationURL, - } - } - - if applyOutput.GCP != nil { - infra.GCP = &state.GCP{ - ProjectID: applyOutput.GCP.ProjectID, - IPCidrNode: applyOutput.GCP.IPCidrNode, - IPCidrPod: applyOutput.GCP.IPCidrPod, - } - } - return infra -} - -// ShowCluster reads the state of Constellation cluster resources from Terraform. -func (c *Client) ShowCluster(ctx context.Context, provider cloudprovider.Provider) (ApplyOutput, error) { tfState, err := c.tf.Show(ctx) if err != nil { - return ApplyOutput{}, fmt.Errorf("terraform show: %w", err) + return state.Infrastructure{}, fmt.Errorf("terraform show: %w", err) } if tfState.Values == nil { - return ApplyOutput{}, errors.New("terraform show: no values returned") + return state.Infrastructure{}, errors.New("terraform show: no values returned") } ipOutput, ok := tfState.Values.Outputs["ip"] if !ok { - return ApplyOutput{}, errors.New("no IP output found") + return state.Infrastructure{}, errors.New("no IP output found") } ip, ok := ipOutput.Value.(string) if !ok { - return ApplyOutput{}, errors.New("invalid type in IP output: not a string") + return state.Infrastructure{}, errors.New("invalid type in IP output: not a string") } apiServerCertSANsOutput, ok := tfState.Values.Outputs["api_server_cert_sans"] if !ok { - return ApplyOutput{}, errors.New("no api_server_cert_sans output found") + return state.Infrastructure{}, errors.New("no api_server_cert_sans output found") } apiServerCertSANsUntyped, ok := apiServerCertSANsOutput.Value.([]any) if !ok { - return ApplyOutput{}, fmt.Errorf("invalid type in api_server_cert_sans output: %s is not a list of elements", apiServerCertSANsOutput.Type.FriendlyName()) + return state.Infrastructure{}, fmt.Errorf("invalid type in api_server_cert_sans output: %s is not a list of elements", apiServerCertSANsOutput.Type.FriendlyName()) } apiServerCertSANs, err := toStringSlice(apiServerCertSANsUntyped) if err != nil { - return ApplyOutput{}, fmt.Errorf("convert api_server_cert_sans output: %w", err) + return state.Infrastructure{}, fmt.Errorf("convert api_server_cert_sans output: %w", err) } secretOutput, ok := tfState.Values.Outputs["initSecret"] if !ok { - return ApplyOutput{}, errors.New("no initSecret output found") + return state.Infrastructure{}, errors.New("no initSecret output found") } secret, ok := secretOutput.Value.(string) if !ok { - return ApplyOutput{}, errors.New("invalid type in initSecret output: not a string") + return state.Infrastructure{}, errors.New("invalid type in initSecret output: not a string") } uidOutput, ok := tfState.Values.Outputs["uid"] if !ok { - return ApplyOutput{}, errors.New("no uid output found") + return state.Infrastructure{}, errors.New("no uid output found") } uid, ok := uidOutput.Value.(string) if !ok { - return ApplyOutput{}, errors.New("invalid type in uid output: not a string") + return state.Infrastructure{}, errors.New("invalid type in uid output: not a string") } - res := ApplyOutput{ - IP: ip, + res := state.Infrastructure{ + ClusterEndpoint: ip, APIServerCertSANs: apiServerCertSANs, - Secret: secret, + InitSecret: secret, UID: uid, } @@ -270,32 +232,32 @@ func (c *Client) ShowCluster(ctx context.Context, provider cloudprovider.Provide case cloudprovider.GCP: gcpProjectOutput, ok := tfState.Values.Outputs["project"] if !ok { - return ApplyOutput{}, errors.New("no project output found") + return state.Infrastructure{}, errors.New("no project output found") } gcpProject, ok := gcpProjectOutput.Value.(string) if !ok { - return ApplyOutput{}, errors.New("invalid type in project output: not a string") + return state.Infrastructure{}, errors.New("invalid type in project output: not a string") } cidrNodesOutput, ok := tfState.Values.Outputs["ip_cidr_nodes"] if !ok { - return ApplyOutput{}, errors.New("no ip_cidr_nodes output found") + return state.Infrastructure{}, errors.New("no ip_cidr_nodes output found") } cidrNodes, ok := cidrNodesOutput.Value.(string) if !ok { - return ApplyOutput{}, errors.New("invalid type in ip_cidr_nodes output: not a string") + return state.Infrastructure{}, errors.New("invalid type in ip_cidr_nodes output: not a string") } cidrPodsOutput, ok := tfState.Values.Outputs["ip_cidr_pods"] if !ok { - return ApplyOutput{}, errors.New("no ip_cidr_pods output found") + return state.Infrastructure{}, errors.New("no ip_cidr_pods output found") } cidrPods, ok := cidrPodsOutput.Value.(string) if !ok { - return ApplyOutput{}, errors.New("invalid type in ip_cidr_pods output: not a string") + return state.Infrastructure{}, errors.New("invalid type in ip_cidr_pods output: not a string") } - res.GCP = &GCPApplyOutput{ + res.GCP = &state.GCP{ ProjectID: gcpProject, IPCidrNode: cidrNodes, IPCidrPod: cidrPods, @@ -303,57 +265,57 @@ func (c *Client) ShowCluster(ctx context.Context, provider cloudprovider.Provide case cloudprovider.Azure: attestationURLOutput, ok := tfState.Values.Outputs["attestationURL"] if !ok { - return ApplyOutput{}, errors.New("no attestationURL output found") + return state.Infrastructure{}, errors.New("no attestationURL output found") } attestationURL, ok := attestationURLOutput.Value.(string) if !ok { - return ApplyOutput{}, errors.New("invalid type in attestationURL output: not a string") + return state.Infrastructure{}, errors.New("invalid type in attestationURL output: not a string") } azureUAMIOutput, ok := tfState.Values.Outputs["user_assigned_identity_client_id"] if !ok { - return ApplyOutput{}, errors.New("no user_assigned_identity_client_id output found") + return state.Infrastructure{}, errors.New("no user_assigned_identity_client_id output found") } azureUAMI, ok := azureUAMIOutput.Value.(string) if !ok { - return ApplyOutput{}, errors.New("invalid type in user_assigned_identity_client_id output: not a string") + return state.Infrastructure{}, errors.New("invalid type in user_assigned_identity_client_id output: not a string") } rgOutput, ok := tfState.Values.Outputs["resource_group"] if !ok { - return ApplyOutput{}, errors.New("no resource_group output found") + return state.Infrastructure{}, errors.New("no resource_group output found") } rg, ok := rgOutput.Value.(string) if !ok { - return ApplyOutput{}, errors.New("invalid type in resource_group output: not a string") + return state.Infrastructure{}, errors.New("invalid type in resource_group output: not a string") } subscriptionOutput, ok := tfState.Values.Outputs["subscription_id"] if !ok { - return ApplyOutput{}, errors.New("no subscription_id output found") + return state.Infrastructure{}, errors.New("no subscription_id output found") } subscriptionID, ok := subscriptionOutput.Value.(string) if !ok { - return ApplyOutput{}, errors.New("invalid type in subscription_id output: not a string") + return state.Infrastructure{}, errors.New("invalid type in subscription_id output: not a string") } networkSGNameOutput, ok := tfState.Values.Outputs["network_security_group_name"] if !ok { - return ApplyOutput{}, errors.New("no network_security_group_name output found") + return state.Infrastructure{}, errors.New("no network_security_group_name output found") } networkSGName, ok := networkSGNameOutput.Value.(string) if !ok { - return ApplyOutput{}, errors.New("invalid type in network_security_group_name output: not a string") + return state.Infrastructure{}, errors.New("invalid type in network_security_group_name output: not a string") } loadBalancerNameOutput, ok := tfState.Values.Outputs["loadbalancer_name"] if !ok { - return ApplyOutput{}, errors.New("no loadbalancer_name output found") + return state.Infrastructure{}, errors.New("no loadbalancer_name output found") } loadBalancerName, ok := loadBalancerNameOutput.Value.(string) if !ok { - return ApplyOutput{}, errors.New("invalid type in loadbalancer_name output: not a string") + return state.Infrastructure{}, errors.New("invalid type in loadbalancer_name output: not a string") } - res.Azure = &AzureApplyOutput{ + res.Azure = &state.Azure{ ResourceGroup: rg, SubscriptionID: subscriptionID, UserAssignedIdentity: azureUAMI, @@ -386,11 +348,11 @@ func (c *Client) PrepareUpgradeWorkspace(path, backupDir string, vars Variables) } // ApplyCluster applies the Terraform configuration of the workspace to create or upgrade a Constellation cluster. -func (c *Client) ApplyCluster(ctx context.Context, provider cloudprovider.Provider, logLevel LogLevel) (ApplyOutput, error) { +func (c *Client) ApplyCluster(ctx context.Context, provider cloudprovider.Provider, logLevel LogLevel) (state.Infrastructure, error) { if err := c.apply(ctx, logLevel); err != nil { - return ApplyOutput{}, err + return state.Infrastructure{}, err } - return c.ShowCluster(ctx, provider) + return c.ShowInfrastructure(ctx, provider) } // ApplyIAM applies the Terraform configuration of the workspace to create or upgrade an IAM configuration. @@ -547,35 +509,6 @@ type StateMigration struct { Hook func(ctx context.Context, tfClient TFMigrator) error } -// ApplyOutput contains the Terraform output values of a cluster creation -// or apply operation. -type ApplyOutput struct { - IP string - APIServerCertSANs []string - Secret string - UID string - GCP *GCPApplyOutput - Azure *AzureApplyOutput -} - -// AzureApplyOutput contains the Terraform output values of a terraform apply operation on Microsoft Azure. -type AzureApplyOutput struct { - ResourceGroup string - SubscriptionID string - NetworkSecurityGroupName string - LoadBalancerName string - UserAssignedIdentity string - // AttestationURL is the URL of the attestation provider. - AttestationURL string -} - -// GCPApplyOutput contains the Terraform output values of a terraform apply operation on GCP. -type GCPApplyOutput struct { - ProjectID string - IPCidrNode string - IPCidrPod string -} - // IAMOutput contains the output information of the Terraform IAM operations. type IAMOutput struct { GCP GCPIAMOutput diff --git a/cli/internal/terraform/terraform_test.go b/cli/internal/terraform/terraform_test.go index 54393d980..d2ff72da9 100644 --- a/cli/internal/terraform/terraform_test.go +++ b/cli/internal/terraform/terraform_test.go @@ -449,18 +449,18 @@ func TestCreateCluster(t *testing.T) { path := path.Join(tc.pathBase, strings.ToLower(tc.provider.String())) require.NoError(c.PrepareWorkspace(path, tc.vars)) - tfOutput, err := c.ApplyCluster(context.Background(), tc.provider, LogLevelDebug) + infraState, err := c.ApplyCluster(context.Background(), tc.provider, LogLevelDebug) if tc.wantErr { assert.Error(err) return } assert.NoError(err) - assert.Equal("192.0.2.100", tfOutput.IP) - assert.Equal("initSecret", tfOutput.Secret) - assert.Equal("12345abc", tfOutput.UID) + assert.Equal("192.0.2.100", infraState.ClusterEndpoint) + assert.Equal("initSecret", infraState.InitSecret) + assert.Equal("12345abc", infraState.UID) if tc.provider == cloudprovider.Azure { - assert.Equal(tc.expectedAttestationURL, tfOutput.Azure.AttestationURL) + assert.Equal(tc.expectedAttestationURL, infraState.Azure.AttestationURL) } }) }