diff --git a/Makefile b/Makefile index 2eaa96c35c..ed50e0e2f0 100644 --- a/Makefile +++ b/Makefile @@ -79,7 +79,7 @@ endif # Release variables -STAGING_REGISTRY := gcr.io/k8s-staging-capi-openstack +STAGING_REGISTRY := hub.ecns.io/test STAGING_BUCKET ?= artifacts.k8s-staging-capi-openstack.appspot.com BUCKET ?= $(STAGING_BUCKET) PROD_REGISTRY ?= k8s.gcr.io/capi-openstack diff --git a/controllers/openstackmachine_controller.go b/controllers/openstackmachine_controller.go index 2680ef6931..65b0e16d67 100644 --- a/controllers/openstackmachine_controller.go +++ b/controllers/openstackmachine_controller.go @@ -24,12 +24,19 @@ import ( "time" "github.com/go-logr/logr" + networkport "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/record" "k8s.io/utils/pointer" + infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha5" + "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/compute" + "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/loadbalancer" + "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking" + "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/provider" + "sigs.k8s.io/cluster-api-provider-openstack/pkg/scope" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" capierrors "sigs.k8s.io/cluster-api/errors" "sigs.k8s.io/cluster-api/util" @@ -46,13 +53,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" - - infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha5" - "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/compute" - "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/loadbalancer" - "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking" - "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/provider" - "sigs.k8s.io/cluster-api-provider-openstack/pkg/scope" ) // OpenStackMachineReconciler reconciles a OpenStackMachine object. @@ -388,6 +388,46 @@ func (r *OpenStackMachineReconciler) reconcileNormal(ctx context.Context, scope } } + // when machine have machinedeployment.clusters.x-k8s.io/fip=enable annotation + // we should give the fip to machine + if machine.Annotations["machinedeployment.clusters.x-k8s.io/fip"] == "enable" && len(addresses)==1 { + fp, err := networkingService.GetOrCreateFloatingIP(openStackMachine, openStackCluster, clusterName, "") + if err != nil { + handleUpdateMachineError(scope.Logger, openStackMachine, errors.Errorf("Floating IP cannot be created: %v", err)) + return ctrl.Result{}, nil + } + var port = new(networkport.Port) + // we should get machine network name if we define network + if len(openStackMachine.Spec.Networks) > 0 { + nets, err := instanceStatus.NetworkStatus() + if err != nil { + err = errors.Errorf("failed to get openstackmachine template: %v", err) + handleUpdateMachineError(scope.Logger, openStackMachine, err) + return ctrl.Result{}, nil + } + scope.Logger.Info(nets.Addresses()[0].Address) + pos, err := networkingService.GetPortFromInstanceIP(*openStackMachine.Spec.InstanceID, nets.Addresses()[0].Address) + if err != nil { + err = errors.Errorf("getting management port for machine deployment machine %s: %v", err) + handleUpdateMachineError(scope.Logger, openStackMachine, err) + return ctrl.Result{}, nil + } + port = &pos[0] + } else { + port, err = computeService.GetManagementPort(openStackCluster, instanceStatus) + if err != nil { + err = errors.Errorf("getting management port for control plane machine %s: %v", machine.Name, err) + handleUpdateMachineError(scope.Logger, openStackMachine, err) + return ctrl.Result{}, nil + } + } + err = networkingService.AssociateFloatingIP(openStackMachine, fp, port.ID) + if err != nil { + handleUpdateMachineError(scope.Logger, openStackMachine, errors.Errorf("Floating IP %s cannot be associated: %v by port id %s", fp,err,port.ID)) + return ctrl.Result{}, nil + } + } + scope.Logger.Info("Reconciled Machine create successfully") return ctrl.Result{}, nil } diff --git a/pkg/cloud/services/provider/provider.go b/pkg/cloud/services/provider/provider.go index 7cffea8f0e..489da2acca 100644 --- a/pkg/cloud/services/provider/provider.go +++ b/pkg/cloud/services/provider/provider.go @@ -21,6 +21,7 @@ import ( "crypto/tls" "crypto/x509" "fmt" + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts" "net/http" "github.com/gophercloud/gophercloud" @@ -37,13 +38,58 @@ import ( infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha5" ) +type NewAuthInfo struct { + clientconfig.AuthInfo + TrustID string `yaml:"trust_id,omitempty" json:"trust_id,omitempty"` +} + +// NewCloud represents an entry in a clouds.yaml/public-clouds.yaml/secure.yaml file. +type NewCloud struct { + Cloud string `yaml:"cloud,omitempty" json:"cloud,omitempty"` + Profile string `yaml:"profile,omitempty" json:"profile,omitempty"` + AuthInfo *NewAuthInfo `yaml:"auth,omitempty" json:"auth,omitempty"` + AuthType clientconfig.AuthType `yaml:"auth_type,omitempty" json:"auth_type,omitempty"` + RegionName string `yaml:"region_name,omitempty" json:"region_name,omitempty"` + Regions []clientconfig.Region `yaml:"regions,omitempty" json:"regions,omitempty"` + + // EndpointType and Interface both specify whether to use the public, internal, + // or admin interface of a service. They should be considered synonymous, but + // EndpointType will take precedence when both are specified. + EndpointType string `yaml:"endpoint_type,omitempty" json:"endpoint_type,omitempty"` + Interface string `yaml:"interface,omitempty" json:"interface,omitempty"` + + // API Version overrides. + IdentityAPIVersion string `yaml:"identity_api_version,omitempty" json:"identity_api_version,omitempty"` + VolumeAPIVersion string `yaml:"volume_api_version,omitempty" json:"volume_api_version,omitempty"` + + // Verify whether or not SSL API requests should be verified. + Verify *bool `yaml:"verify,omitempty" json:"verify,omitempty"` + + // CACertFile a path to a CA Cert bundle that can be used as part of + // verifying SSL API requests. + CACertFile string `yaml:"cacert,omitempty" json:"cacert,omitempty"` + + // ClientCertFile a path to a client certificate to use as part of the SSL + // transaction. + ClientCertFile string `yaml:"cert,omitempty" json:"cert,omitempty"` + + // ClientKeyFile a path to a client key to use as part of the SSL + // transaction. + ClientKeyFile string `yaml:"key,omitempty" json:"key,omitempty"` +} + +type NewClouds struct { + Clouds map[string]NewCloud `yaml:"clouds" json:"clouds"` +} + const ( cloudsSecretKey = "clouds.yaml" caSecretKey = "cacert" ) + func NewClientFromMachine(ctx context.Context, ctrlClient client.Client, openStackMachine *infrav1.OpenStackMachine) (*gophercloud.ProviderClient, *clientconfig.ClientOpts, string, error) { - var cloud clientconfig.Cloud + var cloud NewCloud var caCert []byte if openStackMachine.Spec.IdentityRef != nil { @@ -57,7 +103,7 @@ func NewClientFromMachine(ctx context.Context, ctrlClient client.Client, openSta } func NewClientFromCluster(ctx context.Context, ctrlClient client.Client, openStackCluster *infrav1.OpenStackCluster) (*gophercloud.ProviderClient, *clientconfig.ClientOpts, string, error) { - var cloud clientconfig.Cloud + var cloud NewCloud var caCert []byte if openStackCluster.Spec.IdentityRef != nil { @@ -70,10 +116,10 @@ func NewClientFromCluster(ctx context.Context, ctrlClient client.Client, openSta return NewClient(cloud, caCert) } -func NewClient(cloud clientconfig.Cloud, caCert []byte) (*gophercloud.ProviderClient, *clientconfig.ClientOpts, string, error) { +func NewClient(cloud NewCloud, caCert []byte) (*gophercloud.ProviderClient, *clientconfig.ClientOpts, string, error) { clientOpts := new(clientconfig.ClientOpts) if cloud.AuthInfo != nil { - clientOpts.AuthInfo = cloud.AuthInfo + clientOpts.AuthInfo = &cloud.AuthInfo.AuthInfo clientOpts.AuthType = cloud.AuthType clientOpts.RegionName = cloud.RegionName } @@ -84,11 +130,11 @@ func NewClient(cloud clientconfig.Cloud, caCert []byte) (*gophercloud.ProviderCl } opts.AllowReauth = true + provider, err := openstack.NewClient(opts.IdentityEndpoint) if err != nil { return nil, nil, "", fmt.Errorf("create providerClient err: %v", err) } - config := &tls.Config{ RootCAs: x509.NewCertPool(), MinVersion: tls.VersionTLS12, @@ -101,17 +147,46 @@ func NewClient(cloud clientconfig.Cloud, caCert []byte) (*gophercloud.ProviderCl } provider.HTTPClient.Transport = &http.Transport{Proxy: http.ProxyFromEnvironment, TLSClientConfig: config} - if klog.V(6).Enabled() { - provider.HTTPClient.Transport = &osclient.RoundTripper{ - Rt: provider.HTTPClient.Transport, - Logger: &defaultLogger{}, + provider.HTTPClient.Transport = &osclient.RoundTripper{ + Rt: provider.HTTPClient.Transport, + Logger: &defaultLogger{}, + } + if cloud.AuthInfo.TrustID!="" { + tokenauth:=tokens.AuthOptions{} + tokenauth.IdentityEndpoint=opts.IdentityEndpoint + tokenauth.UserID=opts.UserID + tokenauth.Username=opts.Username + tokenauth.Password=opts.Password + tokenauth.DomainID=opts.DomainID + tokenauth.DomainName=opts.DomainName + tokenauth.ApplicationCredentialID=opts.ApplicationCredentialID + tokenauth.ApplicationCredentialName=opts.ApplicationCredentialName + tokenauth.ApplicationCredentialSecret=opts.ApplicationCredentialSecret + tokenauth.AllowReauth=opts.AllowReauth + if opts.Scope!=nil { + tokenauth.Scope.ProjectID=opts.Scope.ProjectID + tokenauth.Scope.ProjectName=opts.Scope.ProjectName + tokenauth.Scope.DomainName=opts.Scope.DomainName + tokenauth.Scope.DomainID=opts.Scope.DomainID + } + authOptsExt := trusts.AuthOptsExt{ + TrustID: cloud.AuthInfo.TrustID, + AuthOptionsBuilder: &tokenauth, } + err = openstack.AuthenticateV3(provider, authOptsExt, gophercloud.EndpointOpts{}) + if err != nil { + return nil, nil, "", fmt.Errorf("providerClient authentication err: %v", err) + } + projectID, err := getProjectIDFromAuthResult(provider.GetAuthResult()) + if err != nil { + return nil, nil, "", err + } + return provider,clientOpts,projectID,nil } err = openstack.Authenticate(provider, *opts) if err != nil { return nil, nil, "", fmt.Errorf("providerClient authentication err: %v", err) } - projectID, err := getProjectIDFromAuthResult(provider.GetAuthResult()) if err != nil { return nil, nil, "", err @@ -128,8 +203,8 @@ func (defaultLogger) Printf(format string, args ...interface{}) { } // getCloudFromSecret extract a Cloud from the given namespace:secretName. -func getCloudFromSecret(ctx context.Context, ctrlClient client.Client, secretNamespace string, secretName string, cloudName string) (clientconfig.Cloud, []byte, error) { - emptyCloud := clientconfig.Cloud{} +func getCloudFromSecret(ctx context.Context, ctrlClient client.Client, secretNamespace string, secretName string, cloudName string) (NewCloud, []byte, error) { + emptyCloud := NewCloud{} if secretName == "" { return emptyCloud, nil, nil @@ -153,7 +228,7 @@ func getCloudFromSecret(ctx context.Context, ctrlClient client.Client, secretNam return emptyCloud, nil, fmt.Errorf("OpenStack credentials secret %v did not contain key %v", secretName, cloudsSecretKey) } - var clouds clientconfig.Clouds + var clouds NewClouds if err = yaml.Unmarshal(content, &clouds); err != nil { return emptyCloud, nil, fmt.Errorf("failed to unmarshal clouds credentials stored in secret %v: %v", secretName, err) }