Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support keystone with certificate #224

Merged
merged 1 commit into from
Apr 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions cmd/clusterctl/examples/openstack/generate-yaml.sh
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ OPENSTACK_CLOUD_CONFIG_PLAIN=$(cat "$CLOUDS_PATH")

MACHINE_CONTROLLER_SSH_PRIVATE_FILE=openstack_tmp
MACHINE_CONTROLLER_SSH_HOME=${HOME}/.ssh/
CACERT="/etc/certs/cacert"

# Set up the output dir if it does not yet exist
mkdir -p $PWD/$OUTPUT
Expand All @@ -159,7 +160,7 @@ PASSWORD=$(echo "$OPENSTACK_CLOUD_CONFIG_PLAIN" | yq r - clouds.$CLOUD.auth.pass
REGION=$(echo "$OPENSTACK_CLOUD_CONFIG_PLAIN" | yq r - clouds.$CLOUD.region_name)
PROJECT_ID=$(echo "$OPENSTACK_CLOUD_CONFIG_PLAIN" | yq r - clouds.$CLOUD.auth.project_id)
DOMAIN_NAME=$(echo "$OPENSTACK_CLOUD_CONFIG_PLAIN" | yq r - clouds.$CLOUD.auth.user_domain_name)

CACERT_ORIGINAL=$(echo "$OPENSTACK_CLOUD_CONFIG_PLAIN" | yq r - clouds.$CLOUD.cacert)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be problem if cacert doesn't exist...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in the following commit: 192e70c


# Basic cloud.conf, no LB configuration as that data is not known yet.
OPENSTACK_CLOUD_PROVIDER_CONF_PLAIN="[Global]
Expand All @@ -171,11 +172,23 @@ tenant-id=\"$PROJECT_ID\"
domain-name=\"$DOMAIN_NAME\"
"

if [ "$CACERT_ORIGINAL" != "null" ]; then
OPENSTACK_CLOUD_PROVIDER_CONF_PLAIN="$OPENSTACK_CLOUD_PROVIDER_CONF_PLAIN
ca-file=\"$CACERT\"
"
fi

OS=$(uname)
if [[ "$OS" =~ "Linux" ]]; then
OPENSTACK_CLOUD_PROVIDER_CONF=$(echo "$OPENSTACK_CLOUD_PROVIDER_CONF_PLAIN"|base64 -w0)
if [ "$CACERT_ORIGINAL" != "null" ]; then
OPENSTACK_CLOUD_CACERT_CONFIG=$(cat "$CACERT_ORIGINAL"|base64 -w0)
Copy link
Contributor

@hchenxa hchenxa Apr 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the $CACERT_ORIGINAL was the value of cacert in clouds.yaml I think, so may be we should use echo but not cat here.

refer the test result below:

[root@hchenxa1 openstack]#
[root@hchenxa1 openstack]# OPENSTACK_CLOUD_CONFIG_PLAIN=$(cat "clouds.yaml")
[root@hchenxa1 openstack]# CLOUD=openstack
[root@hchenxa1 openstack]# CACERT_ORIGINAL=$(echo "$OPENSTACK_CLOUD_CONFIG_PLAIN" | yq r - clouds.$CLOUD.cacert)
[root@hchenxa1 openstack]# echo $CACERT_ORIGINAL
-----BEGIN CERTIFICATE----- MIID5TCCAs2gAwIBAgIBFDANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzE0 MDIGA1UEChMrSW50ZXJuYXRpb25hbCBCdXNpbmVzcyBNYWNoaW5lcyBDb3Jwb3Jh dGlvbjEdMBsGA1UEAxMUSUJNIEludGVybmFsIFJvb3QgQ0EwHhcNMTYwMjI0MDUw MDAwWhcNMzUwMTAzMDQ1OTU5WjBiMQswCQYDVQQGEwJVUzE0MDIGA1UEChMrSW50 ZXJuYXRpb25hbCBCdXNpbmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjEdMBsGA1UE AxMUSUJNIEludGVybmFsIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw ggEKAoIBAQDUKGuk9Tmri43R3SauS7gY9rQ9DXvRwklnbW+3Ts8/Meb4MPPxezdE cqVJtHVc3kinDpzVMeKJXlB8CABBpxMBSLApmIQywEKoVd0H0w62Yc3rYuhv03iY y6OozBV0BL6tzZE0UbvtLGuAQXMZ7ehzxqIta85JjfFN86AO2u7xrNF0FYyGH+E0 Rn6yNhb25VrqxE0OYbSMIGoWdvS11K4SgVDqrJ9OqIk8NHrIJ8Ed24P/YPMeAp3j U409Gev1zGcuLdRr09WckQ145FZVDbPq42gcl7qYICPhZ4/eDUUjFgxpipfMGkMb 1X+Y3kFDgb4BO8Xrdda2VQo1iDZs8A8bAgMBAAGjgaUwgaIwPwYJYIZIAYb4QgEN BDIWMEdlbmVyYXRlZCBieSB0aGUgU2VjdXJpdHkgU2VydmVyIGZvciB6L09TIChS QUNGKTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +d4Y5Z4wE2lRp/15hUiMfA5v2OMwHwYDVR0jBBgwFoAU+d4Y5Z4wE2lRp/15hUiM fA5v2OMwDQYJKoZIhvcNAQELBQADggEBAH87Ms8yFyAb9nXesaKjTHksLi1VKe2i zESWozYFXnRtOgOW7/0xXcfK+7PW6xwcOqvTk61fqTGxj+iRyZf2e3FNtIB+T/Lg 3SZF9sztPM0jEUELWycC8l6WPTvzQjZZBCsF+cWbU1nxvRNQluzCsTDUEIfThJIF cLu0WkoQclUrC3d2tM8jclLPssb6/OV8GaJ+4mx4ri7HbGaUAOtA/TXKR6AuhgkR NPKYhpPU0q/PRlGXdwJP8zXb8+CXMMTnI5Upur7Tc5T3I/x1Gqfz7n1sTRZfsuiQ J5uua4hz4te3oV2tm7LWcNItHD43zttBTTx/m5icg71JE2gcr2oincw= -----END CERTIFICATE-----
[root@hchenxa1 openstack]# cat $CACERT_ORIGINAL
cat: unrecognized option '-----BEGIN'
Try 'cat --help' for more information.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hchenxa Besides this issue, does there are any other issues that we need to raise with this PR?

@hidekazuna can you help update?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am ok now, I prefer to do some offline test before merge (http and https) ... thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think @hidekazuna still need some update for his PR.

Copy link
Contributor Author

@hidekazuna hidekazuna Apr 23, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$CACERT_ORIGINAL is the path of CA file. Could you provide your executed command? I tested the following.

./generate-yaml.sh /home/ubuntu/.config/openstack/clouds.yaml openstack ubuntu

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hchenxa can you help try

./generate-yaml.sh -f ./clouds.yaml openstack ubuntu

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found missed cmd/clusterctl/examples/openstack/provider-component/user-data/centos/templates/master-user-data.sh and worker-user-data.sh. I will update.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated by 0bf8a1b

Copy link
Contributor

@gyliu513 gyliu513 Apr 24, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hchenxa you should configure the cacert as the path to a CA Cert, but not the content of the cert. Can you help re-test?

https://github.com/kubernetes-sigs/cluster-api-provider-openstack/blob/master/vendor/github.com/gophercloud/utils/openstack/clientconfig/results.go#L33-L35

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure

fi
elif [[ "$OS" =~ "Darwin" ]]; then
OPENSTACK_CLOUD_PROVIDER_CONF=$(echo "$OPENSTACK_CLOUD_PROVIDER_CONF_PLAIN"|base64)
if [ "$CACERT_ORIGINAL" != "null" ]; then
OPENSTACK_CLOUD_CACERT_CONFIG=$(cat "$CACERT_ORIGINAL"|base64)
fi
else
echo "Unrecognized OS : $OS"
exit 1
Expand All @@ -184,24 +197,32 @@ fi
if [[ "$PROVIDER_OS" == "coreos" ]]; then
cat $COREOS_COMMON_SECTION \
| sed -e "s#\$OPENSTACK_CLOUD_PROVIDER_CONF#$OPENSTACK_CLOUD_PROVIDER_CONF#" \
| sed -e "s#\$OPENSTACK_CLOUD_CACERT_CONFIG#$OPENSTACK_CLOUD_CACERT_CONFIG#" \
| yq m -a - $COREOS_MASTER_SECTION \
> $COREOS_MASTER_USER_DATA
cat $COREOS_COMMON_SECTION \
| sed -e "s#\$OPENSTACK_CLOUD_PROVIDER_CONF#$OPENSTACK_CLOUD_PROVIDER_CONF#" \
| sed -e "s#\$OPENSTACK_CLOUD_CACERT_CONFIG#$OPENSTACK_CLOUD_CACERT_CONFIG#" \
| yq m -a - $COREOS_WORKER_SECTION \
> $COREOS_WORKER_USER_DATA
else
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you updated centos and ubuntu
but how about coreos ? I didn't see the change ,maybe a follow up??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I Updated files for coreos.

cat "$MASTER_USER_DATA" \
| sed -e "s#\$OPENSTACK_CLOUD_PROVIDER_CONF#$OPENSTACK_CLOUD_PROVIDER_CONF#" \
| sed -e "s#\$OPENSTACK_CLOUD_CACERT_CONFIG#$OPENSTACK_CLOUD_CACERT_CONFIG#" \
> $USERDATA/$PROVIDER_OS/master-user-data.sh
cat "$WORKER_USER_DATA" \
| sed -e "s#\$OPENSTACK_CLOUD_PROVIDER_CONF#$OPENSTACK_CLOUD_PROVIDER_CONF#" \
| sed -e "s#\$OPENSTACK_CLOUD_CACERT_CONFIG#$OPENSTACK_CLOUD_CACERT_CONFIG#" \
> $USERDATA/$PROVIDER_OS/worker-user-data.sh
fi

printf $CLOUD > $CONFIG_DIR/os_cloud.txt
echo "$OPENSTACK_CLOUD_CONFIG_PLAIN" > $CONFIG_DIR/clouds.yaml

if [ "$CACERT_ORIGINAL" != "null" ]; then
cat "$CACERT_ORIGINAL" > $CONFIG_DIR/cacert
else
echo "dummy" > $CONFIG_DIR/cacert
fi

# Build provider-components.yaml with kustomize
# Coreos has a different kubeadm path (/usr is read-only) so gets a different kustomization.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ secretGenerator:
- name: cloud-config
commands:
clouds.yaml: "cat configs/clouds.yaml"
cacert: "cat configs/cacert"
type: Opaque
- name: cloud-selector
commands:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ echo '1' > /proc/sys/net/bridge/bridge-nf-call-iptables
echo '1' > /proc/sys/net/ipv4/ip_forward

echo $OPENSTACK_CLOUD_PROVIDER_CONF | base64 -d > /etc/kubernetes/cloud.conf
mkdir /etc/certs
echo $OPENSTACK_CLOUD_CACERT_CONFIG | base64 -d > /etc/certs/cacert

# Set up kubeadm config file to pass parameters to kubeadm init.
cat > /etc/kubernetes/kubeadm_config.yaml <<EOF
Expand Down Expand Up @@ -122,6 +124,10 @@ apiServer:
mountPath: /etc/kubernetes/cloud.conf
name: cloud
readOnly: true
- hostPath: "/etc/certs/cacert"
mountPath: "/etc/certs/cacert"
name: cacert
readOnly: true
timeoutForControlPlane: 4m0s
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
Expand All @@ -138,6 +144,10 @@ controllerManager:
mountPath: /etc/kubernetes/cloud.conf
name: cloud
readOnly: true
- hostPath: "/etc/certs/cacert"
mountPath: "/etc/certs/cacert"
name: cacert
readOnly: true
dns:
type: CoreDNS
etcd:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ install_configure_docker

# Write the cloud.conf so that the kubelet can use it.
echo $OPENSTACK_CLOUD_PROVIDER_CONF | base64 -d > /etc/kubernetes/cloud.conf
mkdir /etc/certs
echo $OPENSTACK_CLOUD_CACERT_CONFIG | base64 -d > /etc/certs/cacert

# Set up kubeadm config file to pass to kubeadm join.
cat > /etc/kubernetes/kubeadm_config.yaml <<EOF
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,16 @@ storage:
id: 0
group:
id: 0
- path: /etc/certs/cacert
filesystem: root
contents:
inline: !!binary |
$OPENSTACK_CLOUD_CACERT_CONFIG
mode: 0600
user:
id: 0
group:
id: 0
- path: /etc/modules-load.d/ipvs.conf
filesystem: root
contents:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ storage:
- name: cloud
hostPath: "/etc/kubernetes/cloud.conf"
mountPath: "/etc/kubernetes/cloud.conf"
- name: cacert
hostPath: "/etc/certs/cacert"
mountPath: "/etc/certs/cacert"
controllerManager:
extraArgs:
cluster-cidr: {{ .PodCIDR }}
Expand All @@ -42,6 +45,9 @@ storage:
- name: cloud
hostPath: "/etc/kubernetes/cloud.conf"
mountPath: "/etc/kubernetes/cloud.conf"
- name: cacert
hostPath: "/etc/certs/cacert"
mountPath: "/etc/certs/cacert"
user:
id: 0
group:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ EOF

echo $OPENSTACK_CLOUD_PROVIDER_CONF | base64 -d > /etc/kubernetes/cloud.conf
chmod 600 /etc/kubernetes/cloud.conf
mkdir /etc/certs
echo $OPENSTACK_CLOUD_CACERT_CONFIG | base64 -d > /etc/certs/cacert

systemctl daemon-reload
systemctl restart kubelet.service
Expand Down Expand Up @@ -150,6 +152,10 @@ apiServer:
mountPath: /etc/kubernetes/cloud.conf
name: cloud
readOnly: true
- hostPath: "/etc/certs/cacert"
mountPath: "/etc/certs/cacert"
name: cacert
readOnly: true
timeoutForControlPlane: 4m0s
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
Expand All @@ -166,6 +172,10 @@ controllerManager:
mountPath: /etc/kubernetes/cloud.conf
name: cloud
readOnly: true
- hostPath: "/etc/certs/cacert"
mountPath: "/etc/certs/cacert"
name: cacert
readOnly: true
dns:
type: CoreDNS
etcd:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ CLUSTER_DNS_SERVER=$(prips ${SERVICE_CIDR} | head -n 11 | tail -n 1)

# Write the cloud.conf so that the kubelet can use it.
echo $OPENSTACK_CLOUD_PROVIDER_CONF | base64 -d > /etc/kubernetes/cloud.conf
mkdir /etc/certs
echo $OPENSTACK_CLOUD_CACERT_CONFIG | base64 -d > /etc/certs/cacert
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious
this is only for ubuntu, do we have a centos version? I didn't see it ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks added this for centos.


# Set up kubeadm config file to pass to kubeadm join.
cat > /etc/kubernetes/kubeadm_config.yaml <<EOF
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ spec:
properties:
apiVersion:
type: string
cloudName:
type: string
cloudsSecret:
type: object
disableServerTags:
type: boolean
dnsNameservers:
Expand All @@ -37,6 +41,8 @@ spec:
type: string
type: array
required:
- cloudsSecret
- cloudName
- managedSecurityGroups
version: v1alpha1
status:
Expand Down
6 changes: 6 additions & 0 deletions pkg/apis/openstackproviderconfig/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@ type OpenstackClusterProviderSpec struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

// The name of the secret containing the openstack credentials
CloudsSecret *corev1.SecretReference `json:"cloudsSecret"`

// The name of the cloud to use from the clouds secret
CloudName string `json:"cloudName"`

// NodeCIDR is the OpenStack Subnet to be created. Cluster actuator will create a
// network, a subnet with NodeCIDR, and a router connected to this subnet.
// If you leave this empty, no network will be created.
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 43 additions & 13 deletions pkg/cloud/openstack/clients/machineservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ limitations under the License.
package clients

import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
"fmt"
"net/http"
"time"

"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
Expand Down Expand Up @@ -53,6 +56,7 @@ import (

const (
CloudsSecretKey = "clouds.yaml"
CaSecretKey = "cacert"

TimeoutTrunkDelete = 3 * time.Minute
RetryIntervalTrunkDelete = 5 * time.Second
Expand Down Expand Up @@ -91,34 +95,40 @@ type InstanceListOpts struct {
Name string `q:"name"`
}

func GetCloudFromSecret(kubeClient kubernetes.Interface, namespace string, secretName string, cloudName string) (clientconfig.Cloud, error) {
func GetCloudFromSecret(kubeClient kubernetes.Interface, namespace string, secretName string, cloudName string) (clientconfig.Cloud, []byte, error) {
emptyCloud := clientconfig.Cloud{}

if secretName == "" {
return emptyCloud, nil
return emptyCloud, nil, nil
}

if secretName != "" && cloudName == "" {
return emptyCloud, fmt.Errorf("Secret name set to %v but no cloud was specified. Please set cloud_name in your machine spec.", secretName)
return emptyCloud, nil, fmt.Errorf("Secret name set to %v but no cloud was specified. Please set cloud_name in your machine spec.", secretName)
}

secret, err := kubeClient.CoreV1().Secrets(namespace).Get(secretName, metav1.GetOptions{})
if err != nil {
return emptyCloud, err
return emptyCloud, nil, err
}

content, ok := secret.Data[CloudsSecretKey]
if !ok {
return emptyCloud, fmt.Errorf("OpenStack credentials secret %v did not contain key %v",
return emptyCloud, nil, fmt.Errorf("OpenStack credentials secret %v did not contain key %v",
secretName, CloudsSecretKey)
}
var clouds clientconfig.Clouds
err = yaml.Unmarshal(content, &clouds)
if err != nil {
return emptyCloud, fmt.Errorf("failed to unmarshal clouds credentials stored in secret %v: %v", secretName, err)
return emptyCloud, nil, fmt.Errorf("failed to unmarshal clouds credentials stored in secret %v: %v", secretName, err)
}

return clouds.Clouds[cloudName], nil
// get cacert
cacert, ok := secret.Data[CaSecretKey]
if !ok {
return emptyCloud, nil, err
}

return clouds.Clouds[cloudName], cacert, nil
}

// TODO: Eventually we'll have a NewInstanceServiceFromCluster too
Expand All @@ -128,25 +138,28 @@ func NewInstanceServiceFromMachine(kubeClient kubernetes.Interface, machine *clu
return nil, err
}
cloud := clientconfig.Cloud{}
var cacert []byte

if machineSpec.CloudsSecret != nil && machineSpec.CloudsSecret.Name != "" {
namespace := machineSpec.CloudsSecret.Namespace
if namespace == "" {
namespace = machine.Namespace
}
cloud, err = GetCloudFromSecret(kubeClient, namespace, machineSpec.CloudsSecret.Name, machineSpec.CloudName)
cloud, cacert, err = GetCloudFromSecret(kubeClient, namespace, machineSpec.CloudsSecret.Name, machineSpec.CloudName)
if err != nil {
return nil, err
}
}
return NewInstanceServiceFromCloud(cloud)
return NewInstanceServiceFromCloud(cloud, cacert)
}

func NewInstanceService() (*InstanceService, error) {
cloud := clientconfig.Cloud{}
return NewInstanceServiceFromCloud(cloud)
var cacert []byte
return NewInstanceServiceFromCloud(cloud, cacert)
}

func NewInstanceServiceFromCloud(cloud clientconfig.Cloud) (*InstanceService, error) {
func NewInstanceServiceFromCloud(cloud clientconfig.Cloud, cacert []byte) (*InstanceService, error) {
clientOpts := new(clientconfig.ClientOpts)
var opts *gophercloud.AuthOptions

Expand All @@ -165,9 +178,26 @@ func NewInstanceServiceFromCloud(cloud clientconfig.Cloud) (*InstanceService, er

opts.AllowReauth = true

provider, err := openstack.AuthenticatedClient(*opts)
provider, err := openstack.NewClient(opts.IdentityEndpoint)
if err != nil {
return nil, fmt.Errorf("create providerClient err: %v", err)
}

config := &tls.Config{}
cloudFromYaml, err := clientconfig.GetCloudFromYAML(clientOpts)
if cloudFromYaml.CACertFile != "" {
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(cacert)
config.RootCAs = caCertPool
}

config.InsecureSkipVerify = *cloudFromYaml.Verify
transport := &http.Transport{Proxy: http.ProxyFromEnvironment, TLSClientConfig: config}
provider.HTTPClient.Transport = transport

err = openstack.Authenticate(provider, *opts)
if err != nil {
return nil, fmt.Errorf("Create providerClient err: %v", err)
return nil, fmt.Errorf("providerClient authentication err: %v", err)
}

identityClient, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{
Expand Down
Loading