func checkOpenStackOpts(openstackOpts *OpenStack) error {
	lbOpts := openstackOpts.lbOpts
	emptyDuration := MyDuration{}
	if lbOpts.CreateMonitor {
		if lbOpts.MonitorDelay == emptyDuration {
			return fmt.Errorf("monitor-delay not set in cloud provider config")
		}
		if lbOpts.MonitorTimeout == emptyDuration {
			return fmt.Errorf("monitor-timeout not set in cloud provider config")
		}
		if lbOpts.MonitorMaxRetries == uint(0) {
			return fmt.Errorf("monitor-max-retries not set in cloud provider config")
		}
	}
	return checkMetadataSearchOrder(openstackOpts.metadataOpts.SearchOrder)
}
check opts for OpenStack
package openstack
import (
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"reflect"
	"regexp"
	"strings"
	"time"
	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
	"github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts"
	tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
	"github.com/gophercloud/gophercloud/pagination"
	"github.com/mitchellh/mapstructure"
	"gopkg.in/gcfg.v1"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	netutil "k8s.io/apimachinery/pkg/util/net"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	certutil "k8s.io/client-go/util/cert"
	cloudprovider "k8s.io/cloud-provider"
	nodehelpers "k8s.io/cloud-provider/node/helpers"
	"k8s.io/klog/v2"
	netutils "k8s.io/utils/net"
)
const (
	ProviderName = "openstack"
	TypeHostName     = "hostname"
	availabilityZone = "availability_zone"
	defaultTimeOut   = 60 * time.Second
)
var ErrNotFound = errors.New("failed to find object")
var ErrMultipleResults = errors.New("multiple results where only one expected")
var ErrNoAddressFound = errors.New("no address found for host")
type MyDuration struct {
	time.Duration
}
func (d *MyDuration) UnmarshalText(text []byte) error {
	res, err := time.ParseDuration(string(text))
	if err != nil {
		return err
	}
	d.Duration = res
	return nil
}
type LoadBalancer struct {
	network *gophercloud.ServiceClient
	compute *gophercloud.ServiceClient
	lb      *gophercloud.ServiceClient
	opts    LoadBalancerOpts
}
type LoadBalancerOpts struct {
	LBVersion            string     `gcfg:"lb-version"`           
	UseOctavia           bool       `gcfg:"use-octavia"`          
	SubnetID             string     `gcfg:"subnet-id"`            
	FloatingNetworkID    string     `gcfg:"floating-network-id"`  
	LBMethod             string     `gcfg:"lb-method"`            
	LBProvider           string     `gcfg:"lb-provider"`
	CreateMonitor        bool       `gcfg:"create-monitor"`
	MonitorDelay         MyDuration `gcfg:"monitor-delay"`
	MonitorTimeout       MyDuration `gcfg:"monitor-timeout"`
	MonitorMaxRetries    uint       `gcfg:"monitor-max-retries"`
	ManageSecurityGroups bool       `gcfg:"manage-security-groups"`
	NodeSecurityGroupIDs []string    
}
type BlockStorageOpts struct {
	BSVersion             string `gcfg:"bs-version"`         
	TrustDevicePath       bool   `gcfg:"trust-device-path"`  
	IgnoreVolumeAZ        bool   `gcfg:"ignore-volume-az"`
	NodeVolumeAttachLimit int    `gcfg:"node-volume-attach-limit"`  
}
type RouterOpts struct {
	RouterID string `gcfg:"router-id"`  
}
type MetadataOpts struct {
	SearchOrder    string     `gcfg:"search-order"`
	RequestTimeout MyDuration `gcfg:"request-timeout"`
}
var _ cloudprovider.Interface = (*OpenStack)(nil)
var _ cloudprovider.Zones = (*OpenStack)(nil)
type OpenStack struct {
	provider     *gophercloud.ProviderClient
	region       string
	lbOpts       LoadBalancerOpts
	bsOpts       BlockStorageOpts
	routeOpts    RouterOpts
	metadataOpts MetadataOpts
	localInstanceID string
}
type Config struct {
	Global struct {
		AuthURL         string `gcfg:"auth-url"`
		Username        string
		UserID          string `gcfg:"user-id"`
		Password        string `datapolicy:"password"`
		TenantID        string `gcfg:"tenant-id"`
		TenantName      string `gcfg:"tenant-name"`
		TrustID         string `gcfg:"trust-id"`
		DomainID        string `gcfg:"domain-id"`
		DomainName      string `gcfg:"domain-name"`
		Region          string
		CAFile          string `gcfg:"ca-file"`
		SecretName      string `gcfg:"secret-name"`
		SecretNamespace string `gcfg:"secret-namespace"`
		KubeconfigPath  string `gcfg:"kubeconfig-path"`
	}
	LoadBalancer LoadBalancerOpts
	BlockStorage BlockStorageOpts
	Route        RouterOpts
	Metadata     MetadataOpts
}
func init() {
	registerMetrics()
	cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) {
		cfg, err := readConfig(config)
		if err != nil {
			return nil, err
		}
		return newOpenStack(cfg)
	})
}
func (cfg Config) toAuthOptions() gophercloud.AuthOptions {
	return gophercloud.AuthOptions{
		IdentityEndpoint: cfg.Global.AuthURL,
		Username:         cfg.Global.Username,
		UserID:           cfg.Global.UserID,
		Password:         cfg.Global.Password,
		TenantID:         cfg.Global.TenantID,
		TenantName:       cfg.Global.TenantName,
		DomainID:         cfg.Global.DomainID,
		DomainName:       cfg.Global.DomainName,
		AllowReauth: true,
	}
}
func (cfg Config) toAuth3Options() tokens3.AuthOptions {
	return tokens3.AuthOptions{
		IdentityEndpoint: cfg.Global.AuthURL,
		Username:         cfg.Global.Username,
		UserID:           cfg.Global.UserID,
		Password:         cfg.Global.Password,
		DomainID:         cfg.Global.DomainID,
		DomainName:       cfg.Global.DomainName,
		AllowReauth:      true,
	}
}
func configFromEnv() (cfg Config, ok bool) {
	cfg.Global.AuthURL = os.Getenv("OS_AUTH_URL")
	cfg.Global.Username = os.Getenv("OS_USERNAME")
	cfg.Global.Region = os.Getenv("OS_REGION_NAME")
	cfg.Global.UserID = os.Getenv("OS_USER_ID")
	cfg.Global.TrustID = os.Getenv("OS_TRUST_ID")
	cfg.Global.TenantID = os.Getenv("OS_TENANT_ID")
	if cfg.Global.TenantID == "" {
		cfg.Global.TenantID = os.Getenv("OS_PROJECT_ID")
	}
	cfg.Global.TenantName = os.Getenv("OS_TENANT_NAME")
	if cfg.Global.TenantName == "" {
		cfg.Global.TenantName = os.Getenv("OS_PROJECT_NAME")
	}
	cfg.Global.DomainID = os.Getenv("OS_DOMAIN_ID")
	if cfg.Global.DomainID == "" {
		cfg.Global.DomainID = os.Getenv("OS_USER_DOMAIN_ID")
	}
	cfg.Global.DomainName = os.Getenv("OS_DOMAIN_NAME")
	if cfg.Global.DomainName == "" {
		cfg.Global.DomainName = os.Getenv("OS_USER_DOMAIN_NAME")
	}
	cfg.Global.SecretName = os.Getenv("SECRET_NAME")
	cfg.Global.SecretNamespace = os.Getenv("SECRET_NAMESPACE")
	cfg.Global.KubeconfigPath = os.Getenv("KUBECONFIG_PATH")
	ok = cfg.Global.AuthURL != "" &&
		cfg.Global.Username != "" &&
		cfg.Global.Password != "" &&
		(cfg.Global.TenantID != "" || cfg.Global.TenantName != "" ||
			cfg.Global.DomainID != "" || cfg.Global.DomainName != "" ||
			cfg.Global.Region != "" || cfg.Global.UserID != "" ||
			cfg.Global.TrustID != "")
	cfg.Metadata.SearchOrder = fmt.Sprintf("%s,%s", configDriveID, metadataID)
	cfg.BlockStorage.BSVersion = "auto"
	return
}
func createKubernetesClient(kubeconfigPath string) (*kubernetes.Clientset, error) {
	klog.Info("Creating kubernetes API client.")
	cfg, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath)
	if err != nil {
		return nil, err
	}
	cfg.DisableCompression = true
	client, err := kubernetes.NewForConfig(cfg)
	if err != nil {
		return nil, err
	}
	v, err := client.Discovery().ServerVersion()
	if err != nil {
		return nil, err
	}
	klog.Infof("Kubernetes API client created, server version %s", fmt.Sprintf("v%v.%v", v.Major, v.Minor))
	return client, nil
}
func setConfigFromSecret(cfg *Config) error {
	secretName := cfg.Global.SecretName
	secretNamespace := cfg.Global.SecretNamespace
	kubeconfigPath := cfg.Global.KubeconfigPath
	k8sClient, err := createKubernetesClient(kubeconfigPath)
	if err != nil {
		return fmt.Errorf("failed to get kubernetes client: %v", err)
	}
	secret, err := k8sClient.CoreV1().Secrets(secretNamespace).Get(context.TODO(), secretName, metav1.GetOptions{})
	if err != nil {
		klog.Warningf("Cannot get secret %s in namespace %s. error: %q", secretName, secretNamespace, err)
		return err
	}
	if content, ok := secret.Data["clouds.conf"]; ok {
		err = gcfg.ReadStringInto(cfg, string(content))
		if err != nil {
			klog.Error("Cannot parse data from the secret.")
			return fmt.Errorf("cannot parse data from the secret")
		}
		return nil
	}
	klog.Error("Cannot find \"clouds.conf\" key in the secret.")
	return fmt.Errorf("cannot find \"clouds.conf\" key in the secret")
}
func readConfig(config io.Reader) (Config, error) {
	if config == nil {
		return Config{}, fmt.Errorf("no OpenStack cloud provider config file given")
	}
	cfg, _ := configFromEnv()
	cfg.BlockStorage.BSVersion = "auto"
	cfg.BlockStorage.TrustDevicePath = false
	cfg.BlockStorage.IgnoreVolumeAZ = false
	cfg.Metadata.SearchOrder = fmt.Sprintf("%s,%s", configDriveID, metadataID)
	err := gcfg.ReadInto(&cfg, config)
	if err != nil {
		return cfg, err
	}
	if cfg.Global.SecretName != "" && cfg.Global.SecretNamespace != "" {
		klog.Infof("Set credentials from secret %s in namespace %s", cfg.Global.SecretName, cfg.Global.SecretNamespace)
		err = setConfigFromSecret(&cfg)
		if err != nil {
			return cfg, err
		}
	}
	return cfg, nil
}
type caller bool
func newCaller() caller   { return caller(true) }
func (c *caller) disarm() { *c = false }
func (c *caller) call(f func()) {
	if *c {
		f()
	}
}
func readInstanceID(searchOrder string) (string, error) {
	const instanceIDFile = "/var/lib/cloud/data/instance-id"
	idBytes, err := ioutil.ReadFile(instanceIDFile)
	if err == nil {
		instanceID := string(idBytes)
		instanceID = strings.TrimSpace(instanceID)
		klog.V(3).Infof("Got instance id from %s: %s", instanceIDFile, instanceID)
		if instanceID != "" {
			return instanceID, nil
		}
	}
	md, err := getMetadata(searchOrder)
	if err != nil {
		return "", err
	}
	return md.UUID, nil
}