Skip to content

Commit

Permalink
openstack: read credentials from clouds.yaml
Browse files Browse the repository at this point in the history
  • Loading branch information
malt3 committed Mar 11, 2024
1 parent ca0c4c9 commit 4c44f48
Show file tree
Hide file tree
Showing 16 changed files with 167 additions and 152 deletions.
1 change: 1 addition & 0 deletions cli/internal/cloudcmd/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
25 changes: 17 additions & 8 deletions cli/internal/cloudcmd/serviceaccount.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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,
}

}
Expand Down
4 changes: 1 addition & 3 deletions cli/internal/cloudcmd/tfvars.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 3 additions & 7 deletions cli/internal/terraform/variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 2 additions & 6 deletions cli/internal/terraform/variables_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions internal/cloud/openstack/clouds/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -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",
],
)
59 changes: 59 additions & 0 deletions internal/cloud/openstack/clouds/read.go
Original file line number Diff line number Diff line change
@@ -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"
)

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)

confDir, err := os.UserConfigDir()
if err != nil {
return Clouds{}, fmt.Errorf("getting user config directory: %w", err)
}

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")
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)
}
38 changes: 9 additions & 29 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down Expand Up @@ -364,6 +346,12 @@ func Default() *Config {
UseMarketplaceImage: toPtr(false),
},
OpenStack: &OpenStackConfig{
CloudsYAMLPath: func() string {
if os.PathSeparator == '/' {
return "~/.config/openstack/clouds.yaml"
}
return ""
}(),
DeployYawolLoadBalancer: toPtr(true),
DeployCSIDriver: toPtr(true),
},
Expand Down Expand Up @@ -496,11 +484,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)
}

Expand Down Expand Up @@ -909,9 +892,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"
Expand Down
Loading

0 comments on commit 4c44f48

Please sign in to comment.