From 870195ad59b76cc011ae956e61a19e7ee888b84e Mon Sep 17 00:00:00 2001 From: Richard Gonzalez Date: Sun, 17 Sep 2017 23:32:28 -0400 Subject: [PATCH] Added openstack instance metadata search order --- .../providers/openstack/metadata.go | 51 +++++++++++----- .../providers/openstack/openstack.go | 60 +++++++++++++++---- .../openstack/openstack_instances.go | 10 +++- 3 files changed, 91 insertions(+), 30 deletions(-) diff --git a/pkg/cloudprovider/providers/openstack/metadata.go b/pkg/cloudprovider/providers/openstack/metadata.go index 9ab0e09189..5cc8fc04f6 100644 --- a/pkg/cloudprovider/providers/openstack/metadata.go +++ b/pkg/cloudprovider/providers/openstack/metadata.go @@ -32,17 +32,25 @@ import ( "k8s.io/utils/exec" ) -// metadataUrl is URL to OpenStack metadata server. It's hardcoded IPv4 -// link-local address as documented in "OpenStack Cloud Administrator Guide", -// chapter Compute - Networking with nova-network. -// https://docs.openstack.org/admin-guide/compute-networking-nova.html#metadata-service -const metadataUrl = "http://169.254.169.254/openstack/2012-08-10/meta_data.json" +const ( + // metadataUrl is URL to OpenStack metadata server. It's hardcoded IPv4 + // link-local address as documented in "OpenStack Cloud Administrator Guide", + // chapter Compute - Networking with nova-network. + // https://docs.openstack.org/admin-guide/compute-networking-nova.html#metadata-service + metadataUrl = "http://169.254.169.254/openstack/2012-08-10/meta_data.json" -// Config drive is defined as an iso9660 or vfat (deprecated) drive -// with the "config-2" label. -// http://docs.openstack.org/user-guide/cli-config-drive.html -const configDriveLabel = "config-2" -const configDrivePath = "openstack/2012-08-10/meta_data.json" + // metadataID is used as an identifier on the metadata search order configuration. + metadataID = "metadataService" + + // Config drive is defined as an iso9660 or vfat (deprecated) drive + // with the "config-2" label. + // http://docs.openstack.org/user-guide/cli-config-drive.html + configDriveLabel = "config-2" + configDrivePath = "openstack/2012-08-10/meta_data.json" + + // configDriveID is used as an identifier on the metadata search order configuration. + configDriveID = "configDrive" +) var ErrBadMetadata = errors.New("Invalid OpenStack metadata, got empty uuid") @@ -141,12 +149,27 @@ func getMetadataFromMetadataService() (*Metadata, error) { // Metadata is fixed for the current host, so cache the value process-wide var metadataCache *Metadata -func getMetadata() (*Metadata, error) { +func getMetadata(order string) (*Metadata, error) { if metadataCache == nil { - md, err := getMetadataFromConfigDrive() - if err != nil { - md, err = getMetadataFromMetadataService() + var md *Metadata + var err error + + elements := strings.Split(order, ",") + for _, id := range elements { + switch id { + case configDriveID: + md, err = getMetadataFromConfigDrive() + case metadataID: + md, err = getMetadataFromMetadataService() + default: + err = fmt.Errorf("%s is not a valid metadata search order option. Supported options are %s and %s", id, configDriveID, metadataID) + } + + if err == nil { + break + } } + if err != nil { return nil, err } diff --git a/pkg/cloudprovider/providers/openstack/openstack.go b/pkg/cloudprovider/providers/openstack/openstack.go index 0233892321..c48b373c6b 100644 --- a/pkg/cloudprovider/providers/openstack/openstack.go +++ b/pkg/cloudprovider/providers/openstack/openstack.go @@ -100,13 +100,18 @@ type RouterOpts struct { RouterId string `gcfg:"router-id"` // required } +type MetadataOpts struct { + SearchOrder string `gcfg:"search-order"` +} + // OpenStack is an implementation of cloud provider Interface for OpenStack. type OpenStack struct { - provider *gophercloud.ProviderClient - region string - lbOpts LoadBalancerOpts - bsOpts BlockStorageOpts - routeOpts RouterOpts + provider *gophercloud.ProviderClient + region string + lbOpts LoadBalancerOpts + bsOpts BlockStorageOpts + routeOpts RouterOpts + metadataOpts MetadataOpts // InstanceID of the server where this OpenStack object is instantiated. localInstanceID string } @@ -128,6 +133,7 @@ type Config struct { LoadBalancer LoadBalancerOpts BlockStorage BlockStorageOpts Route RouterOpts + Metadata MetadataOpts } func init() { @@ -181,6 +187,7 @@ func readConfig(config io.Reader) (Config, error) { // Set default values for config params cfg.BlockStorage.BSVersion = "auto" cfg.BlockStorage.TrustDevicePath = false + cfg.Metadata.SearchOrder = fmt.Sprintf("%s,%s", configDriveID, metadataID) err := gcfg.ReadInto(&cfg, config) return cfg, err @@ -198,7 +205,7 @@ func (c *Caller) Call(f func()) { } } -func readInstanceID() (string, error) { +func readInstanceID(searchOrder string) (string, error) { // Try to find instance ID on the local filesystem (created by cloud-init) const instanceIDFile = "/var/lib/cloud/data/instance-id" idBytes, err := ioutil.ReadFile(instanceIDFile) @@ -212,7 +219,7 @@ func readInstanceID() (string, error) { // Fall through to metadata server lookup } - md, err := getMetadata() + md, err := getMetadata(searchOrder) if err != nil { return "", err } @@ -275,16 +282,19 @@ func newOpenStack(cfg Config) (*OpenStack, error) { err = openstack.Authenticate(provider, cfg.toAuthOptions()) } + err = validateMetadataSearchOrder(cfg.Metadata.SearchOrder) + if err != nil { return nil, err } os := OpenStack{ - provider: provider, - region: cfg.Global.Region, - lbOpts: cfg.LoadBalancer, - bsOpts: cfg.BlockStorage, - routeOpts: cfg.Route, + provider: provider, + region: cfg.Global.Region, + lbOpts: cfg.LoadBalancer, + bsOpts: cfg.BlockStorage, + routeOpts: cfg.Route, + metadataOpts: cfg.Metadata, } err = checkOpenStackOpts(&os) @@ -539,7 +549,7 @@ func (os *OpenStack) Zones() (cloudprovider.Zones, bool) { } func (os *OpenStack) GetZone() (cloudprovider.Zone, error) { - md, err := getMetadata() + md, err := getMetadata(os.metadataOpts.SearchOrder) if err != nil { return cloudprovider.Zone{}, err } @@ -742,3 +752,27 @@ func (os *OpenStack) volumeService(forceVersion string) (volumeService, error) { return nil, errors.New(err_txt) } } + +func validateMetadataSearchOrder(order string) error { + if order == "" { + return errors.New("Invalid value in section [Metadata] with key `search-order`. Value cannot be empty") + } + + elements := strings.Split(order, ",") + if len(elements) > 2 { + return errors.New("Invalid value in section [Metadata] with key `search-order`. Value cannot contain more than 2 elements") + } + + for _, o := range elements { + switch o { + case configDriveID: + case metadataID: + default: + errTxt := "Invalid element '%s' found in section [Metadata] with key `search-order`." + + "Supported elements include '%s' and '%s'" + return fmt.Errorf(errTxt, o, configDriveID, metadataID) + } + } + + return nil +} diff --git a/pkg/cloudprovider/providers/openstack/openstack_instances.go b/pkg/cloudprovider/providers/openstack/openstack_instances.go index a9037117b2..1020ad7228 100644 --- a/pkg/cloudprovider/providers/openstack/openstack_instances.go +++ b/pkg/cloudprovider/providers/openstack/openstack_instances.go @@ -32,6 +32,7 @@ import ( type Instances struct { compute *gophercloud.ServiceClient + opts MetadataOpts } // Instances returns an implementation of Instances for OpenStack. @@ -45,13 +46,16 @@ func (os *OpenStack) Instances() (cloudprovider.Instances, bool) { glog.V(1).Info("Claiming to support Instances") - return &Instances{compute}, true + return &Instances{ + compute: compute, + opts: os.metadataOpts, + }, true } // Implementation of Instances.CurrentNodeName // Note this is *not* necessarily the same as hostname. func (i *Instances) CurrentNodeName(hostname string) (types.NodeName, error) { - md, err := getMetadata() + md, err := getMetadata(i.opts.SearchOrder) if err != nil { return "", err } @@ -119,7 +123,7 @@ func (i *Instances) InstanceExistsByProviderID(providerID string) (bool, error) // InstanceID returns the kubelet's cloud provider ID. func (os *OpenStack) InstanceID() (string, error) { if len(os.localInstanceID) == 0 { - id, err := readInstanceID() + id, err := readInstanceID(os.metadataOpts.SearchOrder) if err != nil { return "", err }