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 }