github.com/openshift/installer@v1.4.17/pkg/asset/manifests/openstack/cloudproviderconfig.go (about)

     1  package openstack
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/gophercloud/gophercloud/v2"
    10  	"github.com/gophercloud/utils/v2/openstack/clientconfig"
    11  	networkutils "github.com/gophercloud/utils/v2/openstack/networking/v2/networks"
    12  
    13  	"github.com/openshift/installer/pkg/asset/installconfig/openstack"
    14  	"github.com/openshift/installer/pkg/types"
    15  	openstackdefaults "github.com/openshift/installer/pkg/types/openstack/defaults"
    16  )
    17  
    18  // Error represents a failure while generating OpenStack provider
    19  // configuration.
    20  type Error struct {
    21  	err error
    22  	msg string
    23  }
    24  
    25  func (e Error) Error() string { return e.msg + ": " + e.err.Error() }
    26  func (e Error) Unwrap() error { return e.err }
    27  
    28  // CloudProviderConfigSecret generates the cloud provider config for the OpenStack
    29  // platform, that will be stored in the system secret.
    30  func CloudProviderConfigSecret(cloud *clientconfig.Cloud) ([]byte, error) {
    31  	domainID := cloud.AuthInfo.DomainID
    32  	if domainID == "" {
    33  		domainID = cloud.AuthInfo.UserDomainID
    34  	}
    35  
    36  	domainName := cloud.AuthInfo.DomainName
    37  	if domainName == "" {
    38  		domainName = cloud.AuthInfo.UserDomainName
    39  	}
    40  
    41  	// We have to generate this config manually without "go-ini" library, because its
    42  	// output data is incompatible with "gcfg".
    43  	// For instance, if there is a string with a # character, then "go-ini" wraps it in bacticks,
    44  	// like `aaa#bbb`, but gcfg doesn't recognize it and  parses the data as `aaa, skipping
    45  	// everything after the #.
    46  	// For more information: https://bugzilla.redhat.com/show_bug.cgi?id=1771358
    47  	var res strings.Builder
    48  	res.WriteString("[Global]\n")
    49  	if cloud.AuthInfo.AuthURL != "" {
    50  		res.WriteString("auth-url = " + strconv.Quote(cloud.AuthInfo.AuthURL) + "\n")
    51  	}
    52  	if cloud.AuthInfo.Username != "" {
    53  		res.WriteString("username = " + strconv.Quote(cloud.AuthInfo.Username) + "\n")
    54  	}
    55  	if cloud.AuthInfo.Password != "" {
    56  		res.WriteString("password = " + strconv.Quote(cloud.AuthInfo.Password) + "\n")
    57  	}
    58  	if cloud.AuthInfo.ProjectID != "" {
    59  		res.WriteString("tenant-id = " + strconv.Quote(cloud.AuthInfo.ProjectID) + "\n")
    60  	}
    61  	if cloud.AuthInfo.ProjectName != "" {
    62  		res.WriteString("tenant-name = " + strconv.Quote(cloud.AuthInfo.ProjectName) + "\n")
    63  	}
    64  	if domainID != "" {
    65  		res.WriteString("domain-id = " + strconv.Quote(domainID) + "\n")
    66  	}
    67  	if domainName != "" {
    68  		res.WriteString("domain-name = " + strconv.Quote(domainName) + "\n")
    69  	}
    70  	if cloud.RegionName != "" {
    71  		res.WriteString("region = " + strconv.Quote(cloud.RegionName) + "\n")
    72  	}
    73  	if cloud.CACertFile != "" {
    74  		res.WriteString("ca-file = /etc/kubernetes/static-pod-resources/configmaps/cloud-config/ca-bundle.pem\n")
    75  	}
    76  
    77  	return []byte(res.String()), nil
    78  }
    79  
    80  func generateCloudProviderConfig(ctx context.Context, networkClient *gophercloud.ServiceClient, cloudConfig *clientconfig.Cloud, installConfig types.InstallConfig) (cloudProviderConfigData, cloudProviderConfigCABundleData string, err error) {
    81  	cloudProviderConfigData = `[Global]
    82  secret-name = openstack-credentials
    83  secret-namespace = kube-system
    84  `
    85  	if regionName := cloudConfig.RegionName; regionName != "" {
    86  		cloudProviderConfigData += "region = " + regionName + "\n"
    87  	}
    88  
    89  	if caCertFile := cloudConfig.CACertFile; caCertFile != "" {
    90  		cloudProviderConfigData += "ca-file = /etc/kubernetes/static-pod-resources/configmaps/cloud-config/ca-bundle.pem\n"
    91  		caFile, err := os.ReadFile(caCertFile)
    92  		if err != nil {
    93  			return "", "", Error{err, "failed to read clouds.yaml ca-cert from disk"}
    94  		}
    95  		cloudProviderConfigCABundleData = string(caFile)
    96  	}
    97  
    98  	if installConfig.OpenStack.ExternalNetwork != "" {
    99  		networkName := installConfig.OpenStack.ExternalNetwork // Yes, we use a name in install-config.yaml :/
   100  		networkID, err := networkutils.IDFromName(ctx, networkClient, networkName)
   101  		if err != nil {
   102  			return "", "", Error{err, "failed to fetch external network " + networkName}
   103  		}
   104  		// If set get the ID and configure CCM to use that network for LB FIPs.
   105  		cloudProviderConfigData += "\n[LoadBalancer]\n"
   106  		cloudProviderConfigData += "floating-network-id = " + networkID + "\n"
   107  	}
   108  
   109  	return cloudProviderConfigData, cloudProviderConfigCABundleData, nil
   110  }
   111  
   112  // GenerateCloudProviderConfig adds the cloud provider config for the OpenStack
   113  // platform in the provided configmap.
   114  func GenerateCloudProviderConfig(ctx context.Context, installConfig types.InstallConfig) (cloudProviderConfigData, cloudProviderConfigCABundleData string, err error) {
   115  	session, err := openstack.GetSession(installConfig.Platform.OpenStack.Cloud)
   116  	if err != nil {
   117  		return "", "", Error{err, "failed to get cloud config for openstack"}
   118  	}
   119  
   120  	networkClient, err := openstackdefaults.NewServiceClient(ctx, "network", session.ClientOpts)
   121  	if err != nil {
   122  		return "", "", Error{err, "failed to create a network client"}
   123  	}
   124  
   125  	return generateCloudProviderConfig(ctx, networkClient, session.CloudConfig, installConfig)
   126  }