diff --git a/cli/internal/cloudcmd/BUILD.bazel b/cli/internal/cloudcmd/BUILD.bazel index 83b394338..946322495 100644 --- a/cli/internal/cloudcmd/BUILD.bazel +++ b/cli/internal/cloudcmd/BUILD.bazel @@ -25,6 +25,7 @@ go_library( "//internal/cloud/cloudprovider", "//internal/cloud/gcpshared", "//internal/cloud/openstack", + "//internal/cloud/openstack/clouds", "//internal/config", "//internal/constants", "//internal/constellation", diff --git a/cli/internal/cloudcmd/serviceaccount.go b/cli/internal/cloudcmd/serviceaccount.go index 994aaa5b0..7c54a0b9f 100644 --- a/cli/internal/cloudcmd/serviceaccount.go +++ b/cli/internal/cloudcmd/serviceaccount.go @@ -13,6 +13,7 @@ import ( "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/gcpshared" "github.com/edgelesssys/constellation/v2/internal/cloud/openstack" + "github.com/edgelesssys/constellation/v2/internal/cloud/openstack/clouds" "github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/constellation" "github.com/edgelesssys/constellation/v2/internal/file" @@ -38,15 +39,23 @@ func GetMarshaledServiceAccountURI(config *config.Config, fileHandler file.Handl } case cloudprovider.OpenStack: + cloudsYAML, err := clouds.ReadCloudsYAML(fileHandler, config.Provider.OpenStack.CloudsYAMLPath) + if err != nil { + return "", fmt.Errorf("reading clouds.yaml: %w", err) + } + cloud, ok := cloudsYAML.Clouds[config.Provider.OpenStack.Cloud] + if !ok { + return "", fmt.Errorf("cloud %q not found in clouds.yaml", config.Provider.OpenStack.Cloud) + } payload.OpenStack = openstack.AccountKey{ - AuthURL: config.Provider.OpenStack.AuthURL, - Username: config.Provider.OpenStack.Username, - Password: config.Provider.OpenStack.Password, - ProjectID: config.Provider.OpenStack.ProjectID, - ProjectName: config.Provider.OpenStack.ProjectName, - UserDomainName: config.Provider.OpenStack.UserDomainName, - ProjectDomainName: config.Provider.OpenStack.ProjectDomainName, - RegionName: config.Provider.OpenStack.RegionName, + AuthURL: cloud.AuthInfo.AuthURL, + Username: cloud.AuthInfo.Username, + Password: cloud.AuthInfo.Password, + ProjectID: cloud.AuthInfo.ProjectID, + ProjectName: cloud.AuthInfo.ProjectName, + UserDomainName: cloud.AuthInfo.UserDomainName, + ProjectDomainName: cloud.AuthInfo.ProjectDomainName, + RegionName: cloud.RegionName, } } diff --git a/cli/internal/cloudcmd/tfvars.go b/cli/internal/cloudcmd/tfvars.go index ea53cff5d..309632d98 100644 --- a/cli/internal/cloudcmd/tfvars.go +++ b/cli/internal/cloudcmd/tfvars.go @@ -257,11 +257,9 @@ func openStackTerraformVars(conf *config.Config, imageRef string) (*terraform.Op return &terraform.OpenStackClusterVariables{ Name: conf.Name, Cloud: toPtr(conf.Provider.OpenStack.Cloud), + OpenStackCloudsYAMLPath: conf.Provider.OpenStack.CloudsYAMLPath, FloatingIPPoolID: conf.Provider.OpenStack.FloatingIPPoolID, ImageID: imageRef, - OpenstackUserDomainName: conf.Provider.OpenStack.UserDomainName, - OpenstackUsername: conf.Provider.OpenStack.Username, - OpenstackPassword: conf.Provider.OpenStack.Password, Debug: conf.IsDebugCluster(), NodeGroups: nodeGroups, CustomEndpoint: conf.CustomEndpoint, diff --git a/cli/internal/terraform/variables.go b/cli/internal/terraform/variables.go index f48ae0d88..a83818260 100644 --- a/cli/internal/terraform/variables.go +++ b/cli/internal/terraform/variables.go @@ -278,20 +278,16 @@ type OpenStackClusterVariables struct { Name string `hcl:"name" cty:"name"` // NodeGroups is a map of node groups to create. NodeGroups map[string]OpenStackNodeGroup `hcl:"node_groups" cty:"node_groups"` - // Cloud is the (optional) name of the OpenStack cloud to use when reading the "clouds.yaml" configuration file. If empty, environment variables are used. + // Cloud is the name of the OpenStack cloud to use when reading the "clouds.yaml" configuration file. If empty, environment variables are used. Cloud *string `hcl:"cloud" cty:"cloud"` + // OpenStackCloudsYAMLPath is the path to the OpenStack clouds.yaml file + OpenStackCloudsYAMLPath string `hcl:"openstack_clouds_yaml_path" cty:"openstack_clouds_yaml_path"` // (STACKIT only) STACKITProjectID is the ID of the STACKIT project to use. STACKITProjectID string `hcl:"stackit_project_id" cty:"stackit_project_id"` // FloatingIPPoolID is the ID of the OpenStack floating IP pool to use for public IPs. FloatingIPPoolID string `hcl:"floating_ip_pool_id" cty:"floating_ip_pool_id"` // ImageID is the ID of the OpenStack image to use. ImageID string `hcl:"image_id" cty:"image_id"` - // OpenstackUserDomainName is the OpenStack user domain name to use. - OpenstackUserDomainName string `hcl:"openstack_user_domain_name" cty:"openstack_user_domain_name"` - // OpenstackUsername is the OpenStack user name to use. - OpenstackUsername string `hcl:"openstack_username" cty:"openstack_username"` - // OpenstackPassword is the OpenStack password to use. - OpenstackPassword string `hcl:"openstack_password" cty:"openstack_password"` // Debug is true if debug mode is enabled. Debug bool `hcl:"debug" cty:"debug"` // CustomEndpoint is the (optional) custom dns hostname for the kubernetes api server. diff --git a/cli/internal/terraform/variables_test.go b/cli/internal/terraform/variables_test.go index 56940e976..df27ddb59 100644 --- a/cli/internal/terraform/variables_test.go +++ b/cli/internal/terraform/variables_test.go @@ -254,11 +254,9 @@ func TestOpenStackClusterVariables(t *testing.T) { vars := OpenStackClusterVariables{ Name: "cluster-name", Cloud: toPtr("my-cloud"), + OpenStackCloudsYAMLPath: "~/.config/openstack/clouds.yaml", FloatingIPPoolID: "fip-pool-0123456789abcdef", ImageID: "8e10b92d-8f7a-458c-91c6-59b42f82ef81", - OpenstackUserDomainName: "my-user-domain", - OpenstackUsername: "my-username", - OpenstackPassword: "my-password", Debug: true, STACKITProjectID: "my-stackit-project-id", NodeGroups: map[string]OpenStackNodeGroup{ @@ -287,12 +285,10 @@ node_groups = { } } cloud = "my-cloud" +openstack_clouds_yaml_path = "~/.config/openstack/clouds.yaml" stackit_project_id = "my-stackit-project-id" floating_ip_pool_id = "fip-pool-0123456789abcdef" image_id = "8e10b92d-8f7a-458c-91c6-59b42f82ef81" -openstack_user_domain_name = "my-user-domain" -openstack_username = "my-username" -openstack_password = "my-password" debug = true custom_endpoint = "example.com" internal_load_balancer = false diff --git a/go.mod b/go.mod index e0b5c85b8..8d0f11fd4 100644 --- a/go.mod +++ b/go.mod @@ -300,7 +300,7 @@ require ( github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect diff --git a/internal/cloud/openstack/clouds/BUILD.bazel b/internal/cloud/openstack/clouds/BUILD.bazel new file mode 100644 index 000000000..153bed763 --- /dev/null +++ b/internal/cloud/openstack/clouds/BUILD.bazel @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "clouds", + srcs = [ + "clouds.go", + "read.go", + ], + importpath = "github.com/edgelesssys/constellation/v2/internal/cloud/openstack/clouds", + visibility = ["//:__subpackages__"], + deps = [ + "//internal/file", + "@com_github_mitchellh_go_homedir//:go-homedir", + ], +) diff --git a/internal/cloud/openstack/clouds/clouds.go b/internal/cloud/openstack/clouds/clouds.go index 5128eedc6..923325fe2 100644 --- a/internal/cloud/openstack/clouds/clouds.go +++ b/internal/cloud/openstack/clouds/clouds.go @@ -188,21 +188,21 @@ func (r *Region) UnmarshalYAML(unmarshal func(interface{}) error) error { type AuthType string const ( - // AuthPassword defines an unknown version of the password + // AuthPassword defines an unknown version of the password. AuthPassword AuthType = "password" - // AuthToken defined an unknown version of the token + // AuthToken defined an unknown version of the token. AuthToken AuthType = "token" - // AuthV2Password defines version 2 of the password + // AuthV2Password defines version 2 of the password. AuthV2Password AuthType = "v2password" - // AuthV2Token defines version 2 of the token + // AuthV2Token defines version 2 of the token. AuthV2Token AuthType = "v2token" - // AuthV3Password defines version 3 of the password + // AuthV3Password defines version 3 of the password. AuthV3Password AuthType = "v3password" - // AuthV3Token defines version 3 of the token + // AuthV3Token defines version 3 of the token. AuthV3Token AuthType = "v3token" - // AuthV3ApplicationCredential defines version 3 of the application credential + // AuthV3ApplicationCredential defines version 3 of the application credential. AuthV3ApplicationCredential AuthType = "v3applicationcredential" ) diff --git a/internal/cloud/openstack/clouds/read.go b/internal/cloud/openstack/clouds/read.go new file mode 100644 index 000000000..d4259c338 --- /dev/null +++ b/internal/cloud/openstack/clouds/read.go @@ -0,0 +1,59 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ +package clouds + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/mitchellh/go-homedir" + + "github.com/edgelesssys/constellation/v2/internal/file" +) + +// ReadCloudsYAML reads a clouds.yaml file and returns its contents. +func ReadCloudsYAML(fileHandler file.Handler, path string) (Clouds, error) { + // Order of operations as performed by the OpenStack CLI: + + // Define a search path for clouds.yaml: + // 1. If OS_CLIENT_CONFIG_FILE is set, use it as search path + // 2. Otherwise, use the following paths: + // - current directory + // - `openstack` directory under standard user config directory (e.g. ~/.config/openstack) + // - /etc/openstack (Unix only) + + var searchPaths []string + if path != "" { + expanded, err := homedir.Expand(path) + if err == nil { + searchPaths = append(searchPaths, expanded) + } else { + searchPaths = append(searchPaths, path) + } + } else if osClientConfigFile := os.Getenv("OS_CLIENT_CONFIG_FILE"); osClientConfigFile != "" { + searchPaths = append(searchPaths, filepath.Join(osClientConfigFile, "clouds.yaml")) + } else { + searchPaths = append(searchPaths, "clouds.yaml") + confDir, err := os.UserConfigDir() + if err != nil { + return Clouds{}, fmt.Errorf("getting user config directory: %w", err) + } + searchPaths = append(searchPaths, filepath.Join(confDir, "openstack", "clouds.yaml")) + if os.PathSeparator == '/' { + searchPaths = append(searchPaths, "/etc/openstack/clouds.yaml") + } + } + + var cloudsYAML Clouds + for _, path := range searchPaths { + if err := fileHandler.ReadYAML(path, &cloudsYAML); err == nil { + return cloudsYAML, nil + } + } + + return Clouds{}, fmt.Errorf("clouds.yaml not found in search paths: %v", searchPaths) +} diff --git a/internal/config/config.go b/internal/config/config.go index 753156dd3..10ac013d1 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -198,40 +198,22 @@ type OpenStackConfig struct { // OpenStack cloud name to select from "clouds.yaml". Only required if config file for OpenStack is used. Fallback authentication uses environment variables. For details see: https://docs.openstack.org/openstacksdk/latest/user/config/configuration.html. Cloud string `yaml:"cloud"` // description: | + // Path to OpenStack "clouds.yaml" file. Only required if automatic detection fails. + CloudsYAMLPath string `yaml:"cloudsYAMLPath"` + // description: | // Availability zone to place the VMs in. For details see: https://docs.openstack.org/nova/latest/admin/availability-zones.html AvailabilityZone string `yaml:"availabilityZone" validate:"required"` // description: | // Floating IP pool to use for the VMs. For details see: https://docs.openstack.org/ocata/user-guide/cli-manage-ip-addresses.html FloatingIPPoolID string `yaml:"floatingIPPoolID" validate:"required"` // description: | - // AuthURL is the OpenStack Identity endpoint to use inside the cluster. - AuthURL string `yaml:"authURL" validate:"required"` - // description: | - // ProjectID is the ID of the OpenStack project where a user resides. - ProjectID string `yaml:"projectID" validate:"required"` - // description: | // STACKITProjectID is the ID of the STACKIT project where a user resides. // Only used if cloud is "stackit". STACKITProjectID string `yaml:"stackitProjectID"` // description: | - // ProjectName is the name of the project where a user resides. - ProjectName string `yaml:"projectName" validate:"required"` - // description: | - // UserDomainName is the name of the domain where a user resides. - UserDomainName string `yaml:"userDomainName" validate:"required"` - // description: | - // ProjectDomainName is the name of the domain where a project resides. - ProjectDomainName string `yaml:"projectDomainName" validate:"required"` - // description: | // RegionName is the name of the region to use inside the cluster. RegionName string `yaml:"regionName" validate:"required"` // description: | - // Username to use inside the cluster. - Username string `yaml:"username" validate:"required"` - // description: | - // Password to use inside the cluster. You can instead use the environment variable "CONSTELL_OS_PASSWORD". - Password string `yaml:"password"` - // description: | // Deploy Yawol loadbalancer. For details see: https://github.com/stackitcloud/yawol DeployYawolLoadBalancer *bool `yaml:"deployYawolLoadBalancer" validate:"required"` // description: | @@ -496,11 +478,6 @@ func New(fileHandler file.Handler, name string, fetcher attestationconfigapi.Fet fmt.Fprintf(os.Stderr, "WARNING: the environment variable %s is no longer used %s", constants.EnvVarAzureClientSecretValue, appRegistrationErrStr) } - openstackPassword := os.Getenv(constants.EnvVarOpenStackPassword) - if openstackPassword != "" && c.Provider.OpenStack != nil { - c.Provider.OpenStack.Password = openstackPassword - } - return c, c.Validate(force) } @@ -909,9 +886,6 @@ func (c *Config) WithOpenStackProviderDefaults(csp cloudprovider.Provider, openS case "stackit": c.Provider.OpenStack.Cloud = "stackit" c.Provider.OpenStack.FloatingIPPoolID = "970ace5c-458f-484a-a660-0903bcfd91ad" - c.Provider.OpenStack.AuthURL = "https://keystone.api.iaas.eu01.stackit.cloud/v3" - c.Provider.OpenStack.UserDomainName = "portal_mvp" - c.Provider.OpenStack.ProjectDomainName = "portal_mvp" c.Provider.OpenStack.RegionName = "RegionOne" c.Provider.OpenStack.DeployYawolLoadBalancer = toPtr(true) c.Provider.OpenStack.YawolImageID = "bcd6c13e-75d1-4c3f-bf0f-8f83580cc1be" diff --git a/internal/config/config_doc.go b/internal/config/config_doc.go index 8665922ed..2168b7f98 100644 --- a/internal/config/config_doc.go +++ b/internal/config/config_doc.go @@ -276,87 +276,57 @@ func init() { FieldName: "openstack", }, } - OpenStackConfigDoc.Fields = make([]encoder.Doc, 16) + OpenStackConfigDoc.Fields = make([]encoder.Doc, 10) OpenStackConfigDoc.Fields[0].Name = "cloud" OpenStackConfigDoc.Fields[0].Type = "string" OpenStackConfigDoc.Fields[0].Note = "" OpenStackConfigDoc.Fields[0].Description = "OpenStack cloud name to select from \"clouds.yaml\". Only required if config file for OpenStack is used. Fallback authentication uses environment variables. For details see: https://docs.openstack.org/openstacksdk/latest/user/config/configuration.html." OpenStackConfigDoc.Fields[0].Comments[encoder.LineComment] = "OpenStack cloud name to select from \"clouds.yaml\". Only required if config file for OpenStack is used. Fallback authentication uses environment variables. For details see: https://docs.openstack.org/openstacksdk/latest/user/config/configuration.html." - OpenStackConfigDoc.Fields[1].Name = "availabilityZone" + OpenStackConfigDoc.Fields[1].Name = "cloudsYAMLPath" OpenStackConfigDoc.Fields[1].Type = "string" OpenStackConfigDoc.Fields[1].Note = "" - OpenStackConfigDoc.Fields[1].Description = "Availability zone to place the VMs in. For details see: https://docs.openstack.org/nova/latest/admin/availability-zones.html" - OpenStackConfigDoc.Fields[1].Comments[encoder.LineComment] = "Availability zone to place the VMs in. For details see: https://docs.openstack.org/nova/latest/admin/availability-zones.html" - OpenStackConfigDoc.Fields[2].Name = "floatingIPPoolID" + OpenStackConfigDoc.Fields[1].Description = "Path to OpenStack \"clouds.yaml\" file. Only required if automatic detection fails." + OpenStackConfigDoc.Fields[1].Comments[encoder.LineComment] = "Path to OpenStack \"clouds.yaml\" file. Only required if automatic detection fails." + OpenStackConfigDoc.Fields[2].Name = "availabilityZone" OpenStackConfigDoc.Fields[2].Type = "string" OpenStackConfigDoc.Fields[2].Note = "" - OpenStackConfigDoc.Fields[2].Description = "Floating IP pool to use for the VMs. For details see: https://docs.openstack.org/ocata/user-guide/cli-manage-ip-addresses.html" - OpenStackConfigDoc.Fields[2].Comments[encoder.LineComment] = "Floating IP pool to use for the VMs. For details see: https://docs.openstack.org/ocata/user-guide/cli-manage-ip-addresses.html" - OpenStackConfigDoc.Fields[3].Name = "authURL" + OpenStackConfigDoc.Fields[2].Description = "Availability zone to place the VMs in. For details see: https://docs.openstack.org/nova/latest/admin/availability-zones.html" + OpenStackConfigDoc.Fields[2].Comments[encoder.LineComment] = "Availability zone to place the VMs in. For details see: https://docs.openstack.org/nova/latest/admin/availability-zones.html" + OpenStackConfigDoc.Fields[3].Name = "floatingIPPoolID" OpenStackConfigDoc.Fields[3].Type = "string" OpenStackConfigDoc.Fields[3].Note = "" - OpenStackConfigDoc.Fields[3].Description = "description: |\nAuthURL is the OpenStack Identity endpoint to use inside the cluster.\n" - OpenStackConfigDoc.Fields[3].Comments[encoder.LineComment] = "description: |" - OpenStackConfigDoc.Fields[4].Name = "projectID" + OpenStackConfigDoc.Fields[3].Description = "Floating IP pool to use for the VMs. For details see: https://docs.openstack.org/ocata/user-guide/cli-manage-ip-addresses.html" + OpenStackConfigDoc.Fields[3].Comments[encoder.LineComment] = "Floating IP pool to use for the VMs. For details see: https://docs.openstack.org/ocata/user-guide/cli-manage-ip-addresses.html" + OpenStackConfigDoc.Fields[4].Name = "stackitProjectID" OpenStackConfigDoc.Fields[4].Type = "string" OpenStackConfigDoc.Fields[4].Note = "" - OpenStackConfigDoc.Fields[4].Description = "ProjectID is the ID of the OpenStack project where a user resides." - OpenStackConfigDoc.Fields[4].Comments[encoder.LineComment] = "ProjectID is the ID of the OpenStack project where a user resides." - OpenStackConfigDoc.Fields[5].Name = "stackitProjectID" + OpenStackConfigDoc.Fields[4].Description = "STACKITProjectID is the ID of the STACKIT project where a user resides.\nOnly used if cloud is \"stackit\"." + OpenStackConfigDoc.Fields[4].Comments[encoder.LineComment] = "STACKITProjectID is the ID of the STACKIT project where a user resides." + OpenStackConfigDoc.Fields[5].Name = "regionName" OpenStackConfigDoc.Fields[5].Type = "string" OpenStackConfigDoc.Fields[5].Note = "" - OpenStackConfigDoc.Fields[5].Description = "STACKITProjectID is the ID of the STACKIT project where a user resides.\nOnly used if cloud is \"stackit\"." - OpenStackConfigDoc.Fields[5].Comments[encoder.LineComment] = "STACKITProjectID is the ID of the STACKIT project where a user resides." - OpenStackConfigDoc.Fields[6].Name = "projectName" - OpenStackConfigDoc.Fields[6].Type = "string" + OpenStackConfigDoc.Fields[5].Description = "description: |\nRegionName is the name of the region to use inside the cluster.\n" + OpenStackConfigDoc.Fields[5].Comments[encoder.LineComment] = "description: |" + OpenStackConfigDoc.Fields[6].Name = "deployYawolLoadBalancer" + OpenStackConfigDoc.Fields[6].Type = "bool" OpenStackConfigDoc.Fields[6].Note = "" - OpenStackConfigDoc.Fields[6].Description = "ProjectName is the name of the project where a user resides." - OpenStackConfigDoc.Fields[6].Comments[encoder.LineComment] = "ProjectName is the name of the project where a user resides." - OpenStackConfigDoc.Fields[7].Name = "userDomainName" + OpenStackConfigDoc.Fields[6].Description = "Deploy Yawol loadbalancer. For details see: https://github.com/stackitcloud/yawol" + OpenStackConfigDoc.Fields[6].Comments[encoder.LineComment] = "Deploy Yawol loadbalancer. For details see: https://github.com/stackitcloud/yawol" + OpenStackConfigDoc.Fields[7].Name = "yawolImageID" OpenStackConfigDoc.Fields[7].Type = "string" OpenStackConfigDoc.Fields[7].Note = "" - OpenStackConfigDoc.Fields[7].Description = "UserDomainName is the name of the domain where a user resides." - OpenStackConfigDoc.Fields[7].Comments[encoder.LineComment] = "UserDomainName is the name of the domain where a user resides." - OpenStackConfigDoc.Fields[8].Name = "projectDomainName" + OpenStackConfigDoc.Fields[7].Description = "OpenStack OS image used by the yawollet. For details see: https://github.com/stackitcloud/yawol" + OpenStackConfigDoc.Fields[7].Comments[encoder.LineComment] = "OpenStack OS image used by the yawollet. For details see: https://github.com/stackitcloud/yawol" + OpenStackConfigDoc.Fields[8].Name = "yawolFlavorID" OpenStackConfigDoc.Fields[8].Type = "string" OpenStackConfigDoc.Fields[8].Note = "" - OpenStackConfigDoc.Fields[8].Description = "ProjectDomainName is the name of the domain where a project resides." - OpenStackConfigDoc.Fields[8].Comments[encoder.LineComment] = "ProjectDomainName is the name of the domain where a project resides." - OpenStackConfigDoc.Fields[9].Name = "regionName" - OpenStackConfigDoc.Fields[9].Type = "string" + OpenStackConfigDoc.Fields[8].Description = "OpenStack flavor id used for yawollets. For details see: https://github.com/stackitcloud/yawol" + OpenStackConfigDoc.Fields[8].Comments[encoder.LineComment] = "OpenStack flavor id used for yawollets. For details see: https://github.com/stackitcloud/yawol" + OpenStackConfigDoc.Fields[9].Name = "deployCSIDriver" + OpenStackConfigDoc.Fields[9].Type = "bool" OpenStackConfigDoc.Fields[9].Note = "" - OpenStackConfigDoc.Fields[9].Description = "description: |\nRegionName is the name of the region to use inside the cluster.\n" - OpenStackConfigDoc.Fields[9].Comments[encoder.LineComment] = "description: |" - OpenStackConfigDoc.Fields[10].Name = "username" - OpenStackConfigDoc.Fields[10].Type = "string" - OpenStackConfigDoc.Fields[10].Note = "" - OpenStackConfigDoc.Fields[10].Description = "Username to use inside the cluster." - OpenStackConfigDoc.Fields[10].Comments[encoder.LineComment] = "Username to use inside the cluster." - OpenStackConfigDoc.Fields[11].Name = "password" - OpenStackConfigDoc.Fields[11].Type = "string" - OpenStackConfigDoc.Fields[11].Note = "" - OpenStackConfigDoc.Fields[11].Description = "Password to use inside the cluster. You can instead use the environment variable \"CONSTELL_OS_PASSWORD\"." - OpenStackConfigDoc.Fields[11].Comments[encoder.LineComment] = "Password to use inside the cluster. You can instead use the environment variable \"CONSTELL_OS_PASSWORD\"." - OpenStackConfigDoc.Fields[12].Name = "deployYawolLoadBalancer" - OpenStackConfigDoc.Fields[12].Type = "bool" - OpenStackConfigDoc.Fields[12].Note = "" - OpenStackConfigDoc.Fields[12].Description = "Deploy Yawol loadbalancer. For details see: https://github.com/stackitcloud/yawol" - OpenStackConfigDoc.Fields[12].Comments[encoder.LineComment] = "Deploy Yawol loadbalancer. For details see: https://github.com/stackitcloud/yawol" - OpenStackConfigDoc.Fields[13].Name = "yawolImageID" - OpenStackConfigDoc.Fields[13].Type = "string" - OpenStackConfigDoc.Fields[13].Note = "" - OpenStackConfigDoc.Fields[13].Description = "OpenStack OS image used by the yawollet. For details see: https://github.com/stackitcloud/yawol" - OpenStackConfigDoc.Fields[13].Comments[encoder.LineComment] = "OpenStack OS image used by the yawollet. For details see: https://github.com/stackitcloud/yawol" - OpenStackConfigDoc.Fields[14].Name = "yawolFlavorID" - OpenStackConfigDoc.Fields[14].Type = "string" - OpenStackConfigDoc.Fields[14].Note = "" - OpenStackConfigDoc.Fields[14].Description = "OpenStack flavor id used for yawollets. For details see: https://github.com/stackitcloud/yawol" - OpenStackConfigDoc.Fields[14].Comments[encoder.LineComment] = "OpenStack flavor id used for yawollets. For details see: https://github.com/stackitcloud/yawol" - OpenStackConfigDoc.Fields[15].Name = "deployCSIDriver" - OpenStackConfigDoc.Fields[15].Type = "bool" - OpenStackConfigDoc.Fields[15].Note = "" - OpenStackConfigDoc.Fields[15].Description = "Deploy Cinder CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage" - OpenStackConfigDoc.Fields[15].Comments[encoder.LineComment] = "Deploy Cinder CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage" + OpenStackConfigDoc.Fields[9].Description = "Deploy Cinder CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage" + OpenStackConfigDoc.Fields[9].Comments[encoder.LineComment] = "Deploy Cinder CSI driver with on-node encryption. For details see: https://docs.edgeless.systems/constellation/architecture/encrypted-storage" QEMUConfigDoc.Type = "QEMUConfig" QEMUConfigDoc.Comments[encoder.LineComment] = "QEMUConfig holds config information for QEMU based Constellation deployments." diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 204ac52ef..013c50edc 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -328,7 +328,7 @@ func TestFromFile(t *testing.T) { } func TestValidate(t *testing.T) { - const defaultErrCount = 38 // expect this number of error messages by default because user-specific values are not set and multiple providers are defined by default + const defaultErrCount = 32 // expect this number of error messages by default because user-specific values are not set and multiple providers are defined by default const azErrCount = 7 const awsErrCount = 8 const gcpErrCount = 8 diff --git a/internal/config/migration/migration.go b/internal/config/migration/migration.go index 03bfb5b5c..54ca54335 100644 --- a/internal/config/migration/migration.go +++ b/internal/config/migration/migration.go @@ -381,14 +381,7 @@ func V3ToV4(path string, fileHandler file.Handler) error { Cloud: cfgV3.Provider.OpenStack.Cloud, AvailabilityZone: cfgV3.Provider.OpenStack.AvailabilityZone, FloatingIPPoolID: cfgV3.Provider.OpenStack.FloatingIPPoolID, - AuthURL: cfgV3.Provider.OpenStack.AuthURL, - ProjectID: cfgV3.Provider.OpenStack.ProjectID, - ProjectName: cfgV3.Provider.OpenStack.ProjectName, - UserDomainName: cfgV3.Provider.OpenStack.UserDomainName, - ProjectDomainName: cfgV3.Provider.OpenStack.ProjectDomainName, RegionName: cfgV3.Provider.OpenStack.RegionName, - Username: cfgV3.Provider.OpenStack.Username, - Password: cfgV3.Provider.OpenStack.Password, DeployYawolLoadBalancer: cfgV3.Provider.OpenStack.DeployYawolLoadBalancer, YawolImageID: cfgV3.Provider.OpenStack.YawolImageID, YawolFlavorID: cfgV3.Provider.OpenStack.YawolFlavorID, diff --git a/internal/constellation/helm/helm.go b/internal/constellation/helm/helm.go index d3f1e20f3..dcc994c6c 100644 --- a/internal/constellation/helm/helm.go +++ b/internal/constellation/helm/helm.go @@ -97,7 +97,7 @@ type Options struct { func (h Client) PrepareApply( flags Options, stateFile *state.State, serviceAccURI string, masterSecret uri.MasterSecret, ) (Applier, bool, error) { - releases, err := h.loadReleases(flags.CSP, flags.AttestationVariant, flags.K8sVersion, masterSecret, stateFile, flags, serviceAccURI) + releases, err := h.loadReleases(masterSecret, stateFile, flags, serviceAccURI) if err != nil { return nil, false, fmt.Errorf("loading Helm releases: %w", err) } @@ -110,10 +110,9 @@ func (h Client) PrepareApply( } func (h Client) loadReleases( - csp cloudprovider.Provider, attestationVariant variant.Variant, k8sVersion versions.ValidK8sVersion, secret uri.MasterSecret, - stateFile *state.State, flags Options, serviceAccURI string, + secret uri.MasterSecret, stateFile *state.State, flags Options, serviceAccURI string, ) ([]release, error) { - helmLoader := newLoader(csp, attestationVariant, k8sVersion, stateFile, h.cliVersion) + helmLoader := newLoader(flags.CSP, flags.AttestationVariant, flags.K8sVersion, stateFile, h.cliVersion) h.log.Debug("Created new Helm loader") return helmLoader.loadReleases(flags.Conformance, flags.DeployCSIDriver, flags.HelmWaitMode, secret, serviceAccURI, flags.OpenStackValues) } diff --git a/internal/constellation/helm/loader.go b/internal/constellation/helm/loader.go index 994575f6f..5634d03fa 100644 --- a/internal/constellation/helm/loader.go +++ b/internal/constellation/helm/loader.go @@ -176,18 +176,23 @@ func (i *chartLoader) loadReleases(conformanceMode, deployCSIDriver bool, helmWa } releases = append(releases, awsRelease) } - if i.csp == cloudprovider.OpenStack && openStackValues != nil && openStackValues.DeployYawolLoadBalancer { - yawolRelease, err := i.loadRelease(yawolLBControllerInfo, WaitModeNone) - if err != nil { - return nil, fmt.Errorf("loading yawol chart: %w", err) + if i.csp == cloudprovider.OpenStack { + if openStackValues == nil { + return nil, errors.New("provider is OpenStack but OpenStack config is missing") } + if openStackValues.DeployYawolLoadBalancer { + yawolRelease, err := i.loadRelease(yawolLBControllerInfo, WaitModeNone) + if err != nil { + return nil, fmt.Errorf("loading yawol chart: %w", err) + } - yawolVals, err := extraYawolValues(serviceAccURI, i.stateFile.Infrastructure, openStackValues) - if err != nil { - return nil, fmt.Errorf("extending yawol chart values: %w", err) + yawolVals, err := extraYawolValues(serviceAccURI, i.stateFile.Infrastructure, openStackValues) + if err != nil { + return nil, fmt.Errorf("extending yawol chart values: %w", err) + } + yawolRelease.values = mergeMaps(yawolRelease.values, yawolVals) + releases = append(releases, yawolRelease) } - yawolRelease.values = mergeMaps(yawolRelease.values, yawolVals) - releases = append(releases, yawolRelease) } return releases, nil diff --git a/terraform/infrastructure/openstack/main.tf b/terraform/infrastructure/openstack/main.tf index 797423654..bc3f2c0dd 100644 --- a/terraform/infrastructure/openstack/main.tf +++ b/terraform/infrastructure/openstack/main.tf @@ -56,6 +56,9 @@ locals { endpoint if(endpoint.interface == "public") ][0] identity_internal_url = local.identity_endpoint.url + cloudsyaml_path = length(var.openstack_clouds_yaml_path) > 0 ? var.openstack_clouds_yaml_path : "~/.config/openstack/clouds.yaml" + cloudsyaml = yamldecode(file(pathexpand(local.cloudsyaml_path))) + cloudyaml = local.cloudsyaml.clouds[var.cloud] } resource "random_id" "uid" { @@ -236,9 +239,9 @@ module "instance_group" { subnet_id = openstack_networking_subnet_v2.vpc_subnetwork.id init_secret_hash = local.init_secret_hash identity_internal_url = local.identity_internal_url - openstack_username = var.openstack_username - openstack_password = var.openstack_password - openstack_user_domain_name = var.openstack_user_domain_name + openstack_username = local.cloudyaml["auth"]["username"] + openstack_password = local.cloudyaml["auth"]["password"] + openstack_user_domain_name = local.cloudyaml["auth"]["user_domain_name"] openstack_load_balancer_endpoint = openstack_networking_floatingip_v2.public_ip.address } diff --git a/terraform/infrastructure/openstack/variables.tf b/terraform/infrastructure/openstack/variables.tf index 3b0983d68..98714a200 100644 --- a/terraform/infrastructure/openstack/variables.tf +++ b/terraform/infrastructure/openstack/variables.tf @@ -48,26 +48,17 @@ variable "cloud" { description = "Cloud to use within the OpenStack \"clouds.yaml\" file. Optional. If not set, environment variables are used." } +variable "openstack_clouds_yaml_path" { + type = string + default = "~/.config/openstack/clouds.yaml" + description = "Path to OpenStack clouds.yaml file" +} + variable "floating_ip_pool_id" { type = string description = "Pool (network name) to use for floating IPs." } -variable "openstack_user_domain_name" { - type = string - description = "OpenStack user domain name." -} - -variable "openstack_username" { - type = string - description = "OpenStack user name." -} - -variable "openstack_password" { - type = string - description = "OpenStack password." -} - # STACKIT-specific variables variable "stackit_project_id" {