mirror of
				https://github.com/go-acme/lego.git
				synced 2025-10-31 08:27:38 +02:00 
			
		
		
		
	Adds support for Openstack Designate as a DNS provider (#786)
This commit is contained in:
		
				
					committed by
					
						 Ludovic Fernandez
						Ludovic Fernandez
					
				
			
			
				
	
			
			
			
						parent
						
							6fdf45c474
						
					
				
				
					commit
					62e0e54f23
				
			| @@ -87,6 +87,7 @@ git push -u origin my-feature | ||||
| | Cloudflare                | `cloudflare`   | [documentation](https://api.cloudflare.com/)                                                                 | [Go client](https://github.com/cloudflare/cloudflare-go)          | | ||||
| | CloudXNS                  | `cloudxns`     | [documentation](https://www.cloudxns.net/Public/Doc/CloudXNS_api2.0_doc_zh-cn.zip)                           | -                                                                 | | ||||
| | ConoHa                    | `conoha`       | [documentation](https://www.conoha.jp/docs/)                                                                 | -                                                                 | | ||||
| | Openstack Designate       | `designate`    | [documentation](https://docs.openstack.org/designate/latest/)                                                | [Go client](https://godoc.org/github.com/gophercloud/gophercloud/openstack/dns/v2) | | ||||
| | Digital Ocean             | `digitalocean` | [documentation](https://developers.digitalocean.com/documentation/v2/#domain-records)                        | -                                                                 | | ||||
| | DNSimple                  | `dnsimple`     | [documentation](https://developer.dnsimple.com/v2/)                                                          | [Go client](https://github.com/dnsimple/dnsimple-go)              | | ||||
| | DNS Made Easy             | `dnsmadeeasy`  | [documentation](https://api-docs.dnsmadeeasy.com/)                                                           | -                                                                 | | ||||
|   | ||||
							
								
								
									
										22
									
								
								Gopkg.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										22
									
								
								Gopkg.lock
									
									
									
										generated
									
									
									
								
							| @@ -239,6 +239,24 @@ | ||||
|   revision = "064e2069ce9c359c118179501254f67d7d37ba24" | ||||
|   version = "0.2" | ||||
|  | ||||
| [[projects]] | ||||
|   branch = "master" | ||||
|   digest = "1:de9a3291ce7afad12af8b11f3e9972edca4b77ca1374eacd0c9fa1ff1bddcc7c" | ||||
|   name = "github.com/gophercloud/gophercloud" | ||||
|   packages = [ | ||||
|     ".", | ||||
|     "openstack", | ||||
|     "openstack/dns/v2/recordsets", | ||||
|     "openstack/dns/v2/zones", | ||||
|     "openstack/identity/v2/tenants", | ||||
|     "openstack/identity/v2/tokens", | ||||
|     "openstack/identity/v3/tokens", | ||||
|     "openstack/utils", | ||||
|     "pagination", | ||||
|   ] | ||||
|   pruneopts = "NUT" | ||||
|   revision = "a2b0ad6ce68c8302027db1a5f9dbb03b0c8ab072" | ||||
|  | ||||
| [[projects]] | ||||
|   branch = "master" | ||||
|   digest = "1:c45e4f1755487478216e06437e161d56299d963282ce109555091bc4c7a57343" | ||||
| @@ -654,6 +672,10 @@ | ||||
|     "github.com/decker502/dnspod-go", | ||||
|     "github.com/dnsimple/dnsimple-go/dnsimple", | ||||
|     "github.com/exoscale/egoscale", | ||||
|     "github.com/gophercloud/gophercloud", | ||||
|     "github.com/gophercloud/gophercloud/openstack", | ||||
|     "github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets", | ||||
|     "github.com/gophercloud/gophercloud/openstack/dns/v2/zones", | ||||
|     "github.com/iij/doapi", | ||||
|     "github.com/iij/doapi/protocol", | ||||
|     "github.com/linode/linodego", | ||||
|   | ||||
| @@ -39,6 +39,7 @@ Here is an example bash command using the CloudFlare DNS provider: | ||||
| 	fmt.Fprintln(w, "\tcloudflare:\tCLOUDFLARE_EMAIL, CLOUDFLARE_API_KEY") | ||||
| 	fmt.Fprintln(w, "\tcloudxns:\tCLOUDXNS_API_KEY, CLOUDXNS_SECRET_KEY") | ||||
| 	fmt.Fprintln(w, "\tconoha:\tCONOHA_TENANT_ID, CONOHA_API_USERNAME, CONOHA_API_PASSWORD") | ||||
| 	fmt.Fprintln(w, "\tdesignate:\tOS_AUTH_URL, OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_REGION_NAME") | ||||
| 	fmt.Fprintln(w, "\tdigitalocean:\tDO_AUTH_TOKEN") | ||||
| 	fmt.Fprintln(w, "\tdnsimple:\tDNSIMPLE_EMAIL, DNSIMPLE_OAUTH_TOKEN") | ||||
| 	fmt.Fprintln(w, "\tdnsmadeeasy:\tDNSMADEEASY_API_KEY, DNSMADEEASY_API_SECRET") | ||||
| @@ -92,6 +93,7 @@ Here is an example bash command using the CloudFlare DNS provider: | ||||
| 	fmt.Fprintln(w, "\tcloudflare:\tCLOUDFLARE_POLLING_INTERVAL, CLOUDFLARE_PROPAGATION_TIMEOUT, CLOUDFLARE_TTL, CLOUDFLARE_HTTP_TIMEOUT") | ||||
| 	fmt.Fprintln(w, "\tcloudxns:\tCLOUDXNS_POLLING_INTERVAL, CLOUDXNS_PROPAGATION_TIMEOUT, CLOUDXNS_TTL, CLOUDXNS_HTTP_TIMEOUT") | ||||
| 	fmt.Fprintln(w, "\tconoha:\tCONOHA_POLLING_INTERVAL, CONOHA_PROPAGATION_TIMEOUT, CONOHA_TTL, CONOHA_HTTP_TIMEOUT, CONOHA_REGION") | ||||
| 	fmt.Fprintln(w, "\tdesignate:\tDESIGNATE_POLLING_INTERVAL, DESIGNATE_PROPAGATION_TIMEOUT, DESIGNATE_TTL") | ||||
| 	fmt.Fprintln(w, "\tdigitalocean:\tDO_POLLING_INTERVAL, DO_PROPAGATION_TIMEOUT, DO_TTL, DO_HTTP_TIMEOUT") | ||||
| 	fmt.Fprintln(w, "\tdnsimple:\tDNSIMPLE_TTL, DNSIMPLE_POLLING_INTERVAL, DNSIMPLE_PROPAGATION_TIMEOUT") | ||||
| 	fmt.Fprintln(w, "\tdnsmadeeasy:\tDNSMADEEASY_POLLING_INTERVAL, DNSMADEEASY_PROPAGATION_TIMEOUT, DNSMADEEASY_TTL, DNSMADEEASY_HTTP_TIMEOUT") | ||||
|   | ||||
							
								
								
									
										249
									
								
								providers/dns/designate/designate.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								providers/dns/designate/designate.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,249 @@ | ||||
| // Package designate implements a DNS provider for solving the DNS-01 challenge using the Designate DNSaaS for Openstack. | ||||
| package designate | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/gophercloud/gophercloud" | ||||
| 	"github.com/gophercloud/gophercloud/openstack" | ||||
| 	"github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets" | ||||
| 	"github.com/gophercloud/gophercloud/openstack/dns/v2/zones" | ||||
| 	"github.com/xenolf/lego/challenge/dns01" | ||||
| 	"github.com/xenolf/lego/platform/config/env" | ||||
| ) | ||||
|  | ||||
| // Config is used to configure the creation of the DNSProvider | ||||
| type Config struct { | ||||
| 	PropagationTimeout time.Duration | ||||
| 	PollingInterval    time.Duration | ||||
| 	TTL                int | ||||
| 	opts               gophercloud.AuthOptions | ||||
| } | ||||
|  | ||||
| // NewDefaultConfig returns a default configuration for the DNSProvider | ||||
| func NewDefaultConfig() *Config { | ||||
| 	return &Config{ | ||||
| 		TTL:                env.GetOrDefaultInt("DESIGNATE_TTL", 10), | ||||
| 		PropagationTimeout: env.GetOrDefaultSecond("DESIGNATE_PROPAGATION_TIMEOUT", 10*time.Minute), | ||||
| 		PollingInterval:    env.GetOrDefaultSecond("DESIGNATE_POLLING_INTERVAL", 10*time.Second), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // DNSProvider describes a provider for Designate | ||||
| type DNSProvider struct { | ||||
| 	config       *Config | ||||
| 	client       *gophercloud.ServiceClient | ||||
| 	dnsEntriesMu sync.Mutex | ||||
| } | ||||
|  | ||||
| // NewDNSProvider returns a DNSProvider instance configured for Designate. | ||||
| // Credentials must be passed in the environment variables: | ||||
| // OS_AUTH_URL, OS_USERNAME, OS_PASSWORD, OS_TENANT_NAME, OS_REGION_NAME. | ||||
| func NewDNSProvider() (*DNSProvider, error) { | ||||
| 	_, err := env.Get("OS_AUTH_URL", "OS_USERNAME", "OS_PASSWORD", "OS_TENANT_NAME", "OS_REGION_NAME") | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("designate: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	opts, err := openstack.AuthOptionsFromEnv() | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("designate: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	config := NewDefaultConfig() | ||||
| 	config.opts = opts | ||||
|  | ||||
| 	return NewDNSProviderConfig(config) | ||||
| } | ||||
|  | ||||
| // NewDNSProviderConfig return a DNSProvider instance configured for Designate. | ||||
| func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { | ||||
| 	if config == nil { | ||||
| 		return nil, errors.New("designate: the configuration of the DNS provider is nil") | ||||
| 	} | ||||
|  | ||||
| 	provider, err := openstack.AuthenticatedClient(config.opts) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("designate: failed to authenticate: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	dnsClient, err := openstack.NewDNSV2(provider, gophercloud.EndpointOpts{ | ||||
| 		Region: os.Getenv("OS_REGION_NAME"), | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("designate: failed to get DNS provider: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return &DNSProvider{client: dnsClient, config: config}, nil | ||||
| } | ||||
|  | ||||
| // Timeout returns the timeout and interval to use when checking for DNS propagation. | ||||
| // Adjusting here to cope with spikes in propagation times. | ||||
| func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { | ||||
| 	return d.config.PropagationTimeout, d.config.PollingInterval | ||||
| } | ||||
|  | ||||
| // Present creates a TXT record to fulfill the dns-01 challenge | ||||
| func (d *DNSProvider) Present(domain, token, keyAuth string) error { | ||||
| 	fqdn, value := dns01.GetRecord(domain, keyAuth) | ||||
|  | ||||
| 	authZone, err := dns01.FindZoneByFqdn(fqdn) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("designate: couldn't get zone ID in Present: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	zoneID, err := d.getZoneID(authZone) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("designate: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// use mutex to prevent race condition between creating the record and verifying it | ||||
| 	d.dnsEntriesMu.Lock() | ||||
| 	defer d.dnsEntriesMu.Unlock() | ||||
|  | ||||
| 	existingRecord, err := d.getRecord(zoneID, fqdn) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("designate: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if existingRecord != nil { | ||||
| 		if contains(existingRecord.Records, value) { | ||||
| 			log.Printf("designate: the record already exists: %s", value) | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		return d.updateRecord(existingRecord, value) | ||||
| 	} | ||||
|  | ||||
| 	err = d.createRecord(zoneID, fqdn, value) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("designate: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // CleanUp removes the TXT record matching the specified parameters | ||||
| func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { | ||||
| 	fqdn, _ := dns01.GetRecord(domain, keyAuth) | ||||
|  | ||||
| 	authZone, err := dns01.FindZoneByFqdn(fqdn) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	zoneID, err := d.getZoneID(authZone) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("designate: couldn't get zone ID in CleanUp: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// use mutex to prevent race condition between getting the record and deleting it | ||||
| 	d.dnsEntriesMu.Lock() | ||||
| 	defer d.dnsEntriesMu.Unlock() | ||||
|  | ||||
| 	record, err := d.getRecord(zoneID, fqdn) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("designate: couldn't get Record ID in CleanUp: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if record == nil { | ||||
| 		// Record is already deleted | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	err = recordsets.Delete(d.client, zoneID, record.ID).ExtractErr() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("designate: error for %s in CleanUp: %v", fqdn, err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func contains(values []string, value string) bool { | ||||
| 	for _, v := range values { | ||||
| 		if v == value { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func (d *DNSProvider) createRecord(zoneID, fqdn, value string) error { | ||||
| 	createOpts := recordsets.CreateOpts{ | ||||
| 		Name:        fqdn, | ||||
| 		Type:        "TXT", | ||||
| 		TTL:         d.config.TTL, | ||||
| 		Description: "ACME verification record", | ||||
| 		Records:     []string{value}, | ||||
| 	} | ||||
|  | ||||
| 	actual, err := recordsets.Create(d.client, zoneID, createOpts).Extract() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("error for %s in Present while creating record: %v", fqdn, err) | ||||
| 	} | ||||
|  | ||||
| 	if actual.Name != fqdn || actual.TTL != d.config.TTL { | ||||
| 		return fmt.Errorf("the created record doesn't match what we wanted to create") | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (d *DNSProvider) updateRecord(record *recordsets.RecordSet, value string) error { | ||||
| 	if contains(record.Records, value) { | ||||
| 		log.Printf("skip: the record already exists: %s", value) | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	values := append([]string{value}, record.Records...) | ||||
|  | ||||
| 	updateOpts := recordsets.UpdateOpts{ | ||||
| 		Description: &record.Description, | ||||
| 		TTL:         record.TTL, | ||||
| 		Records:     values, | ||||
| 	} | ||||
|  | ||||
| 	result := recordsets.Update(d.client, record.ZoneID, record.ID, updateOpts) | ||||
| 	return result.Err | ||||
| } | ||||
|  | ||||
| func (d *DNSProvider) getZoneID(wanted string) (string, error) { | ||||
| 	allPages, err := zones.List(d.client, nil).AllPages() | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	allZones, err := zones.ExtractZones(allPages) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	for _, zone := range allZones { | ||||
| 		if zone.Name == wanted { | ||||
| 			return zone.ID, nil | ||||
| 		} | ||||
| 	} | ||||
| 	return "", fmt.Errorf("zone id not found for %s", wanted) | ||||
| } | ||||
|  | ||||
| func (d *DNSProvider) getRecord(zoneID string, wanted string) (*recordsets.RecordSet, error) { | ||||
| 	allPages, err := recordsets.ListByZone(d.client, zoneID, nil).AllPages() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	allRecords, err := recordsets.ExtractRecordSets(allPages) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	for _, record := range allRecords { | ||||
| 		if record.Name == wanted { | ||||
| 			return &record, nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil, nil | ||||
| } | ||||
							
								
								
									
										241
									
								
								providers/dns/designate/designate_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								providers/dns/designate/designate_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,241 @@ | ||||
| package designate | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"net/http/httptest" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/stretchr/testify/require" | ||||
| 	"github.com/xenolf/lego/platform/tester" | ||||
| ) | ||||
|  | ||||
| var envTest = tester.NewEnvTest( | ||||
| 	"OS_AUTH_URL", | ||||
| 	"OS_USERNAME", | ||||
| 	"OS_PASSWORD", | ||||
| 	"OS_TENANT_NAME", | ||||
| 	"OS_REGION_NAME"). | ||||
| 	WithDomain("DESIGNATE_DOMAIN") | ||||
|  | ||||
| func TestNewDNSProvider(t *testing.T) { | ||||
| 	server := getServer() | ||||
| 	defer server.Close() | ||||
|  | ||||
| 	testCases := []struct { | ||||
| 		desc     string | ||||
| 		envVars  map[string]string | ||||
| 		expected string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			desc: "success", | ||||
| 			envVars: map[string]string{ | ||||
| 				"OS_AUTH_URL":    server.URL + "/v2.0/", | ||||
| 				"OS_USERNAME":    "B", | ||||
| 				"OS_PASSWORD":    "C", | ||||
| 				"OS_REGION_NAME": "D", | ||||
| 				"OS_TENANT_NAME": "E", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc: "missing credentials", | ||||
| 			envVars: map[string]string{ | ||||
| 				"OS_AUTH_URL":    "", | ||||
| 				"OS_USERNAME":    "", | ||||
| 				"OS_PASSWORD":    "", | ||||
| 				"OS_REGION_NAME": "", | ||||
| 				"OS_TENANT_NAME": "", | ||||
| 			}, | ||||
| 			expected: "designate: some credentials information are missing: OS_AUTH_URL,OS_USERNAME,OS_PASSWORD,OS_TENANT_NAME,OS_REGION_NAME", | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc: "missing auth url", | ||||
| 			envVars: map[string]string{ | ||||
| 				"OS_AUTH_URL":    "", | ||||
| 				"OS_USERNAME":    "B", | ||||
| 				"OS_PASSWORD":    "C", | ||||
| 				"OS_REGION_NAME": "D", | ||||
| 				"OS_TENANT_NAME": "E", | ||||
| 			}, | ||||
| 			expected: "designate: some credentials information are missing: OS_AUTH_URL", | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc: "missing username", | ||||
| 			envVars: map[string]string{ | ||||
| 				"OS_AUTH_URL":    server.URL + "/v2.0/", | ||||
| 				"OS_USERNAME":    "", | ||||
| 				"OS_PASSWORD":    "C", | ||||
| 				"OS_REGION_NAME": "D", | ||||
| 				"OS_TENANT_NAME": "E", | ||||
| 			}, | ||||
| 			expected: "designate: some credentials information are missing: OS_USERNAME", | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc: "missing password", | ||||
| 			envVars: map[string]string{ | ||||
| 				"OS_AUTH_URL":    server.URL + "/v2.0/", | ||||
| 				"OS_USERNAME":    "B", | ||||
| 				"OS_PASSWORD":    "", | ||||
| 				"OS_REGION_NAME": "D", | ||||
| 				"OS_TENANT_NAME": "E", | ||||
| 			}, | ||||
| 			expected: "designate: some credentials information are missing: OS_PASSWORD", | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc: "missing region name", | ||||
| 			envVars: map[string]string{ | ||||
| 				"OS_AUTH_URL":    server.URL + "/v2.0/", | ||||
| 				"OS_USERNAME":    "B", | ||||
| 				"OS_PASSWORD":    "C", | ||||
| 				"OS_REGION_NAME": "", | ||||
| 				"OS_TENANT_NAME": "E", | ||||
| 			}, | ||||
| 			expected: "designate: some credentials information are missing: OS_REGION_NAME", | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc: "missing tenant name", | ||||
| 			envVars: map[string]string{ | ||||
| 				"OS_AUTH_URL":    server.URL + "/v2.0/", | ||||
| 				"OS_USERNAME":    "B", | ||||
| 				"OS_PASSWORD":    "C", | ||||
| 				"OS_REGION_NAME": "D", | ||||
| 				"OS_TENANT_NAME": "", | ||||
| 			}, | ||||
| 			expected: "designate: some credentials information are missing: OS_TENANT_NAME", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, test := range testCases { | ||||
| 		t.Run(test.desc, func(t *testing.T) { | ||||
| 			defer envTest.RestoreEnv() | ||||
| 			envTest.ClearEnv() | ||||
|  | ||||
| 			envTest.Apply(test.envVars) | ||||
|  | ||||
| 			p, err := NewDNSProvider() | ||||
|  | ||||
| 			if len(test.expected) == 0 { | ||||
| 				require.NoError(t, err) | ||||
| 				require.NotNil(t, p) | ||||
| 				require.NotNil(t, p.config) | ||||
| 			} else { | ||||
| 				require.EqualError(t, err, test.expected) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestNewDNSProviderConfig(t *testing.T) { | ||||
| 	server := getServer() | ||||
| 	defer server.Close() | ||||
|  | ||||
| 	testCases := []struct { | ||||
| 		desc       string | ||||
| 		tenantName string | ||||
| 		password   string | ||||
| 		userName   string | ||||
| 		authURL    string | ||||
| 		expected   string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			desc:       "success", | ||||
| 			tenantName: "A", | ||||
| 			password:   "B", | ||||
| 			userName:   "C", | ||||
| 			authURL:    server.URL + "/v2.0/", | ||||
| 		}, | ||||
| 		{ | ||||
| 			desc:       "wrong auth url", | ||||
| 			tenantName: "A", | ||||
| 			password:   "B", | ||||
| 			userName:   "C", | ||||
| 			authURL:    server.URL, | ||||
| 			expected:   "designate: failed to authenticate: No supported version available from endpoint " + server.URL + "/", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, test := range testCases { | ||||
| 		t.Run(test.desc, func(t *testing.T) { | ||||
| 			config := NewDefaultConfig() | ||||
| 			config.opts.TenantName = test.tenantName | ||||
| 			config.opts.Password = test.password | ||||
| 			config.opts.Username = test.userName | ||||
| 			config.opts.IdentityEndpoint = test.authURL | ||||
|  | ||||
| 			p, err := NewDNSProviderConfig(config) | ||||
|  | ||||
| 			if len(test.expected) == 0 { | ||||
| 				require.NoError(t, err) | ||||
| 				require.NotNil(t, p) | ||||
| 				require.NotNil(t, p.config) | ||||
| 			} else { | ||||
| 				require.EqualError(t, err, test.expected) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func getServer() *httptest.Server { | ||||
| 	mux := http.NewServeMux() | ||||
| 	mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { | ||||
| 		_, _ = w.Write([]byte(`{ | ||||
| 	"access": { | ||||
| 		"token": { | ||||
| 			"id": "a", | ||||
| 			"expires": "9015-06-05T16:24:57.637Z" | ||||
| 		}, | ||||
| 		"user": { | ||||
| 			"name": "a", | ||||
| 			"roles": [ ], | ||||
| 			"role_links": [ ]  | ||||
| 		}, | ||||
| 		"serviceCatalog": [ | ||||
| 			{ | ||||
| 				"endpoints": [ | ||||
| 					{ | ||||
| 						"adminURL": "http://23.253.72.207:9696/", | ||||
| 						"region": "D", | ||||
| 						"internalURL": "http://23.253.72.207:9696/", | ||||
| 						"id": "97c526db8d7a4c88bbb8d68db1bdcdb8", | ||||
| 						"publicURL": "http://23.253.72.207:9696/" | ||||
| 					} | ||||
| 				], | ||||
| 				"endpoints_links": [ ], | ||||
| 				"type": "dns", | ||||
| 				"name": "designate" | ||||
| 			} | ||||
| 		] | ||||
| 	} | ||||
| }`)) | ||||
| 		w.WriteHeader(200) | ||||
| 	}) | ||||
| 	return httptest.NewServer(mux) | ||||
| } | ||||
|  | ||||
| func TestLivePresent(t *testing.T) { | ||||
| 	if !envTest.IsLiveTest() { | ||||
| 		t.Skip("skipping live test") | ||||
| 	} | ||||
|  | ||||
| 	envTest.RestoreEnv() | ||||
| 	provider, err := NewDNSProvider() | ||||
| 	require.NoError(t, err) | ||||
|  | ||||
| 	err = provider.Present(envTest.GetDomain(), "", "123d==") | ||||
| 	require.NoError(t, err) | ||||
| } | ||||
|  | ||||
| func TestLiveCleanUp(t *testing.T) { | ||||
| 	if !envTest.IsLiveTest() { | ||||
| 		t.Skip("skipping live test") | ||||
| 	} | ||||
|  | ||||
| 	envTest.RestoreEnv() | ||||
| 	provider, err := NewDNSProvider() | ||||
| 	require.NoError(t, err) | ||||
|  | ||||
| 	time.Sleep(1 * time.Second) | ||||
|  | ||||
| 	err = provider.CleanUp(envTest.GetDomain(), "", "123d==") | ||||
| 	require.NoError(t, err) | ||||
| } | ||||
| @@ -13,6 +13,7 @@ import ( | ||||
| 	"github.com/xenolf/lego/providers/dns/cloudflare" | ||||
| 	"github.com/xenolf/lego/providers/dns/cloudxns" | ||||
| 	"github.com/xenolf/lego/providers/dns/conoha" | ||||
| 	"github.com/xenolf/lego/providers/dns/designate" | ||||
| 	"github.com/xenolf/lego/providers/dns/digitalocean" | ||||
| 	"github.com/xenolf/lego/providers/dns/dnsimple" | ||||
| 	"github.com/xenolf/lego/providers/dns/dnsmadeeasy" | ||||
| @@ -76,6 +77,8 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) { | ||||
| 		return cloudxns.NewDNSProvider() | ||||
| 	case "conoha": | ||||
| 		return conoha.NewDNSProvider() | ||||
| 	case "designate": | ||||
| 		return designate.NewDNSProvider() | ||||
| 	case "digitalocean": | ||||
| 		return digitalocean.NewDNSProvider() | ||||
| 	case "dnsimple": | ||||
|   | ||||
							
								
								
									
										191
									
								
								vendor/github.com/gophercloud/gophercloud/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								vendor/github.com/gophercloud/gophercloud/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,191 @@ | ||||
| Copyright 2012-2013 Rackspace, Inc. | ||||
|  | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); you may not use | ||||
| this file except in compliance with the License.  You may obtain a copy of the | ||||
| License at | ||||
|  | ||||
|   http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
| Unless required by applicable law or agreed to in writing, software distributed | ||||
| under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | ||||
| CONDITIONS OF ANY KIND, either express or implied.  See the License for the | ||||
| specific language governing permissions and limitations under the License.                                 | ||||
|  | ||||
| ------ | ||||
|   | ||||
| 				Apache License | ||||
|                            Version 2.0, January 2004 | ||||
|                         http://www.apache.org/licenses/ | ||||
|  | ||||
|    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||
|  | ||||
|    1. Definitions. | ||||
|  | ||||
|       "License" shall mean the terms and conditions for use, reproduction, | ||||
|       and distribution as defined by Sections 1 through 9 of this document. | ||||
|  | ||||
|       "Licensor" shall mean the copyright owner or entity authorized by | ||||
|       the copyright owner that is granting the License. | ||||
|  | ||||
|       "Legal Entity" shall mean the union of the acting entity and all | ||||
|       other entities that control, are controlled by, or are under common | ||||
|       control with that entity. For the purposes of this definition, | ||||
|       "control" means (i) the power, direct or indirect, to cause the | ||||
|       direction or management of such entity, whether by contract or | ||||
|       otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||
|       outstanding shares, or (iii) beneficial ownership of such entity. | ||||
|  | ||||
|       "You" (or "Your") shall mean an individual or Legal Entity | ||||
|       exercising permissions granted by this License. | ||||
|  | ||||
|       "Source" form shall mean the preferred form for making modifications, | ||||
|       including but not limited to software source code, documentation | ||||
|       source, and configuration files. | ||||
|  | ||||
|       "Object" form shall mean any form resulting from mechanical | ||||
|       transformation or translation of a Source form, including but | ||||
|       not limited to compiled object code, generated documentation, | ||||
|       and conversions to other media types. | ||||
|  | ||||
|       "Work" shall mean the work of authorship, whether in Source or | ||||
|       Object form, made available under the License, as indicated by a | ||||
|       copyright notice that is included in or attached to the work | ||||
|       (an example is provided in the Appendix below). | ||||
|  | ||||
|       "Derivative Works" shall mean any work, whether in Source or Object | ||||
|       form, that is based on (or derived from) the Work and for which the | ||||
|       editorial revisions, annotations, elaborations, or other modifications | ||||
|       represent, as a whole, an original work of authorship. For the purposes | ||||
|       of this License, Derivative Works shall not include works that remain | ||||
|       separable from, or merely link (or bind by name) to the interfaces of, | ||||
|       the Work and Derivative Works thereof. | ||||
|  | ||||
|       "Contribution" shall mean any work of authorship, including | ||||
|       the original version of the Work and any modifications or additions | ||||
|       to that Work or Derivative Works thereof, that is intentionally | ||||
|       submitted to Licensor for inclusion in the Work by the copyright owner | ||||
|       or by an individual or Legal Entity authorized to submit on behalf of | ||||
|       the copyright owner. For the purposes of this definition, "submitted" | ||||
|       means any form of electronic, verbal, or written communication sent | ||||
|       to the Licensor or its representatives, including but not limited to | ||||
|       communication on electronic mailing lists, source code control systems, | ||||
|       and issue tracking systems that are managed by, or on behalf of, the | ||||
|       Licensor for the purpose of discussing and improving the Work, but | ||||
|       excluding communication that is conspicuously marked or otherwise | ||||
|       designated in writing by the copyright owner as "Not a Contribution." | ||||
|  | ||||
|       "Contributor" shall mean Licensor and any individual or Legal Entity | ||||
|       on behalf of whom a Contribution has been received by Licensor and | ||||
|       subsequently incorporated within the Work. | ||||
|  | ||||
|    2. Grant of Copyright License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       copyright license to reproduce, prepare Derivative Works of, | ||||
|       publicly display, publicly perform, sublicense, and distribute the | ||||
|       Work and such Derivative Works in Source or Object form. | ||||
|  | ||||
|    3. Grant of Patent License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       (except as stated in this section) patent license to make, have made, | ||||
|       use, offer to sell, sell, import, and otherwise transfer the Work, | ||||
|       where such license applies only to those patent claims licensable | ||||
|       by such Contributor that are necessarily infringed by their | ||||
|       Contribution(s) alone or by combination of their Contribution(s) | ||||
|       with the Work to which such Contribution(s) was submitted. If You | ||||
|       institute patent litigation against any entity (including a | ||||
|       cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||
|       or a Contribution incorporated within the Work constitutes direct | ||||
|       or contributory patent infringement, then any patent licenses | ||||
|       granted to You under this License for that Work shall terminate | ||||
|       as of the date such litigation is filed. | ||||
|  | ||||
|    4. Redistribution. You may reproduce and distribute copies of the | ||||
|       Work or Derivative Works thereof in any medium, with or without | ||||
|       modifications, and in Source or Object form, provided that You | ||||
|       meet the following conditions: | ||||
|  | ||||
|       (a) You must give any other recipients of the Work or | ||||
|           Derivative Works a copy of this License; and | ||||
|  | ||||
|       (b) You must cause any modified files to carry prominent notices | ||||
|           stating that You changed the files; and | ||||
|  | ||||
|       (c) You must retain, in the Source form of any Derivative Works | ||||
|           that You distribute, all copyright, patent, trademark, and | ||||
|           attribution notices from the Source form of the Work, | ||||
|           excluding those notices that do not pertain to any part of | ||||
|           the Derivative Works; and | ||||
|  | ||||
|       (d) If the Work includes a "NOTICE" text file as part of its | ||||
|           distribution, then any Derivative Works that You distribute must | ||||
|           include a readable copy of the attribution notices contained | ||||
|           within such NOTICE file, excluding those notices that do not | ||||
|           pertain to any part of the Derivative Works, in at least one | ||||
|           of the following places: within a NOTICE text file distributed | ||||
|           as part of the Derivative Works; within the Source form or | ||||
|           documentation, if provided along with the Derivative Works; or, | ||||
|           within a display generated by the Derivative Works, if and | ||||
|           wherever such third-party notices normally appear. The contents | ||||
|           of the NOTICE file are for informational purposes only and | ||||
|           do not modify the License. You may add Your own attribution | ||||
|           notices within Derivative Works that You distribute, alongside | ||||
|           or as an addendum to the NOTICE text from the Work, provided | ||||
|           that such additional attribution notices cannot be construed | ||||
|           as modifying the License. | ||||
|  | ||||
|       You may add Your own copyright statement to Your modifications and | ||||
|       may provide additional or different license terms and conditions | ||||
|       for use, reproduction, or distribution of Your modifications, or | ||||
|       for any such Derivative Works as a whole, provided Your use, | ||||
|       reproduction, and distribution of the Work otherwise complies with | ||||
|       the conditions stated in this License. | ||||
|  | ||||
|    5. Submission of Contributions. Unless You explicitly state otherwise, | ||||
|       any Contribution intentionally submitted for inclusion in the Work | ||||
|       by You to the Licensor shall be under the terms and conditions of | ||||
|       this License, without any additional terms or conditions. | ||||
|       Notwithstanding the above, nothing herein shall supersede or modify | ||||
|       the terms of any separate license agreement you may have executed | ||||
|       with Licensor regarding such Contributions. | ||||
|  | ||||
|    6. Trademarks. This License does not grant permission to use the trade | ||||
|       names, trademarks, service marks, or product names of the Licensor, | ||||
|       except as required for reasonable and customary use in describing the | ||||
|       origin of the Work and reproducing the content of the NOTICE file. | ||||
|  | ||||
|    7. Disclaimer of Warranty. Unless required by applicable law or | ||||
|       agreed to in writing, Licensor provides the Work (and each | ||||
|       Contributor provides its Contributions) on an "AS IS" BASIS, | ||||
|       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||
|       implied, including, without limitation, any warranties or conditions | ||||
|       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||
|       PARTICULAR PURPOSE. You are solely responsible for determining the | ||||
|       appropriateness of using or redistributing the Work and assume any | ||||
|       risks associated with Your exercise of permissions under this License. | ||||
|  | ||||
|    8. Limitation of Liability. In no event and under no legal theory, | ||||
|       whether in tort (including negligence), contract, or otherwise, | ||||
|       unless required by applicable law (such as deliberate and grossly | ||||
|       negligent acts) or agreed to in writing, shall any Contributor be | ||||
|       liable to You for damages, including any direct, indirect, special, | ||||
|       incidental, or consequential damages of any character arising as a | ||||
|       result of this License or out of the use or inability to use the | ||||
|       Work (including but not limited to damages for loss of goodwill, | ||||
|       work stoppage, computer failure or malfunction, or any and all | ||||
|       other commercial damages or losses), even if such Contributor | ||||
|       has been advised of the possibility of such damages. | ||||
|  | ||||
|    9. Accepting Warranty or Additional Liability. While redistributing | ||||
|       the Work or Derivative Works thereof, You may choose to offer, | ||||
|       and charge a fee for, acceptance of support, warranty, indemnity, | ||||
|       or other liability obligations and/or rights consistent with this | ||||
|       License. However, in accepting such obligations, You may act only | ||||
|       on Your own behalf and on Your sole responsibility, not on behalf | ||||
|       of any other Contributor, and only if You agree to indemnify, | ||||
|       defend, and hold each Contributor harmless for any liability | ||||
|       incurred by, or claims asserted against, such Contributor by reason | ||||
|       of your accepting any such warranty or additional liability. | ||||
|  | ||||
|    END OF TERMS AND CONDITIONS | ||||
							
								
								
									
										437
									
								
								vendor/github.com/gophercloud/gophercloud/auth_options.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										437
									
								
								vendor/github.com/gophercloud/gophercloud/auth_options.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,437 @@ | ||||
| package gophercloud | ||||
|  | ||||
| /* | ||||
| AuthOptions stores information needed to authenticate to an OpenStack Cloud. | ||||
| You can populate one manually, or use a provider's AuthOptionsFromEnv() function | ||||
| to read relevant information from the standard environment variables. Pass one | ||||
| to a provider's AuthenticatedClient function to authenticate and obtain a | ||||
| ProviderClient representing an active session on that provider. | ||||
|  | ||||
| Its fields are the union of those recognized by each identity implementation and | ||||
| provider. | ||||
|  | ||||
| An example of manually providing authentication information: | ||||
|  | ||||
|   opts := gophercloud.AuthOptions{ | ||||
|     IdentityEndpoint: "https://openstack.example.com:5000/v2.0", | ||||
|     Username: "{username}", | ||||
|     Password: "{password}", | ||||
|     TenantID: "{tenant_id}", | ||||
|   } | ||||
|  | ||||
|   provider, err := openstack.AuthenticatedClient(opts) | ||||
|  | ||||
| An example of using AuthOptionsFromEnv(), where the environment variables can | ||||
| be read from a file, such as a standard openrc file: | ||||
|  | ||||
|   opts, err := openstack.AuthOptionsFromEnv() | ||||
|   provider, err := openstack.AuthenticatedClient(opts) | ||||
| */ | ||||
| type AuthOptions struct { | ||||
| 	// IdentityEndpoint specifies the HTTP endpoint that is required to work with | ||||
| 	// the Identity API of the appropriate version. While it's ultimately needed by | ||||
| 	// all of the identity services, it will often be populated by a provider-level | ||||
| 	// function. | ||||
| 	// | ||||
| 	// The IdentityEndpoint is typically referred to as the "auth_url" or | ||||
| 	// "OS_AUTH_URL" in the information provided by the cloud operator. | ||||
| 	IdentityEndpoint string `json:"-"` | ||||
|  | ||||
| 	// Username is required if using Identity V2 API. Consult with your provider's | ||||
| 	// control panel to discover your account's username. In Identity V3, either | ||||
| 	// UserID or a combination of Username and DomainID or DomainName are needed. | ||||
| 	Username string `json:"username,omitempty"` | ||||
| 	UserID   string `json:"-"` | ||||
|  | ||||
| 	Password string `json:"password,omitempty"` | ||||
|  | ||||
| 	// At most one of DomainID and DomainName must be provided if using Username | ||||
| 	// with Identity V3. Otherwise, either are optional. | ||||
| 	DomainID   string `json:"-"` | ||||
| 	DomainName string `json:"name,omitempty"` | ||||
|  | ||||
| 	// The TenantID and TenantName fields are optional for the Identity V2 API. | ||||
| 	// The same fields are known as project_id and project_name in the Identity | ||||
| 	// V3 API, but are collected as TenantID and TenantName here in both cases. | ||||
| 	// Some providers allow you to specify a TenantName instead of the TenantId. | ||||
| 	// Some require both. Your provider's authentication policies will determine | ||||
| 	// how these fields influence authentication. | ||||
| 	// If DomainID or DomainName are provided, they will also apply to TenantName. | ||||
| 	// It is not currently possible to authenticate with Username and a Domain | ||||
| 	// and scope to a Project in a different Domain by using TenantName. To | ||||
| 	// accomplish that, the ProjectID will need to be provided as the TenantID | ||||
| 	// option. | ||||
| 	TenantID   string `json:"tenantId,omitempty"` | ||||
| 	TenantName string `json:"tenantName,omitempty"` | ||||
|  | ||||
| 	// AllowReauth should be set to true if you grant permission for Gophercloud to | ||||
| 	// cache your credentials in memory, and to allow Gophercloud to attempt to | ||||
| 	// re-authenticate automatically if/when your token expires.  If you set it to | ||||
| 	// false, it will not cache these settings, but re-authentication will not be | ||||
| 	// possible.  This setting defaults to false. | ||||
| 	// | ||||
| 	// NOTE: The reauth function will try to re-authenticate endlessly if left | ||||
| 	// unchecked. The way to limit the number of attempts is to provide a custom | ||||
| 	// HTTP client to the provider client and provide a transport that implements | ||||
| 	// the RoundTripper interface and stores the number of failed retries. For an | ||||
| 	// example of this, see here: | ||||
| 	// https://github.com/rackspace/rack/blob/1.0.0/auth/clients.go#L311 | ||||
| 	AllowReauth bool `json:"-"` | ||||
|  | ||||
| 	// TokenID allows users to authenticate (possibly as another user) with an | ||||
| 	// authentication token ID. | ||||
| 	TokenID string `json:"-"` | ||||
|  | ||||
| 	// Scope determines the scoping of the authentication request. | ||||
| 	Scope *AuthScope `json:"-"` | ||||
|  | ||||
| 	// Authentication through Application Credentials requires supplying name, project and secret | ||||
| 	// For project we can use TenantID | ||||
| 	ApplicationCredentialID     string `json:"-"` | ||||
| 	ApplicationCredentialName   string `json:"-"` | ||||
| 	ApplicationCredentialSecret string `json:"-"` | ||||
| } | ||||
|  | ||||
| // AuthScope allows a created token to be limited to a specific domain or project. | ||||
| type AuthScope struct { | ||||
| 	ProjectID   string | ||||
| 	ProjectName string | ||||
| 	DomainID    string | ||||
| 	DomainName  string | ||||
| } | ||||
|  | ||||
| // ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder | ||||
| // interface in the v2 tokens package | ||||
| func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { | ||||
| 	// Populate the request map. | ||||
| 	authMap := make(map[string]interface{}) | ||||
|  | ||||
| 	if opts.Username != "" { | ||||
| 		if opts.Password != "" { | ||||
| 			authMap["passwordCredentials"] = map[string]interface{}{ | ||||
| 				"username": opts.Username, | ||||
| 				"password": opts.Password, | ||||
| 			} | ||||
| 		} else { | ||||
| 			return nil, ErrMissingInput{Argument: "Password"} | ||||
| 		} | ||||
| 	} else if opts.TokenID != "" { | ||||
| 		authMap["token"] = map[string]interface{}{ | ||||
| 			"id": opts.TokenID, | ||||
| 		} | ||||
| 	} else { | ||||
| 		return nil, ErrMissingInput{Argument: "Username"} | ||||
| 	} | ||||
|  | ||||
| 	if opts.TenantID != "" { | ||||
| 		authMap["tenantId"] = opts.TenantID | ||||
| 	} | ||||
| 	if opts.TenantName != "" { | ||||
| 		authMap["tenantName"] = opts.TenantName | ||||
| 	} | ||||
|  | ||||
| 	return map[string]interface{}{"auth": authMap}, nil | ||||
| } | ||||
|  | ||||
| func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { | ||||
| 	type domainReq struct { | ||||
| 		ID   *string `json:"id,omitempty"` | ||||
| 		Name *string `json:"name,omitempty"` | ||||
| 	} | ||||
|  | ||||
| 	type projectReq struct { | ||||
| 		Domain *domainReq `json:"domain,omitempty"` | ||||
| 		Name   *string    `json:"name,omitempty"` | ||||
| 		ID     *string    `json:"id,omitempty"` | ||||
| 	} | ||||
|  | ||||
| 	type userReq struct { | ||||
| 		ID       *string    `json:"id,omitempty"` | ||||
| 		Name     *string    `json:"name,omitempty"` | ||||
| 		Password string     `json:"password,omitempty"` | ||||
| 		Domain   *domainReq `json:"domain,omitempty"` | ||||
| 	} | ||||
|  | ||||
| 	type passwordReq struct { | ||||
| 		User userReq `json:"user"` | ||||
| 	} | ||||
|  | ||||
| 	type tokenReq struct { | ||||
| 		ID string `json:"id"` | ||||
| 	} | ||||
|  | ||||
| 	type applicationCredentialReq struct { | ||||
| 		ID     *string  `json:"id,omitempty"` | ||||
| 		Name   *string  `json:"name,omitempty"` | ||||
| 		User   *userReq `json:"user,omitempty"` | ||||
| 		Secret *string  `json:"secret,omitempty"` | ||||
| 	} | ||||
|  | ||||
| 	type identityReq struct { | ||||
| 		Methods               []string                  `json:"methods"` | ||||
| 		Password              *passwordReq              `json:"password,omitempty"` | ||||
| 		Token                 *tokenReq                 `json:"token,omitempty"` | ||||
| 		ApplicationCredential *applicationCredentialReq `json:"application_credential,omitempty"` | ||||
| 	} | ||||
|  | ||||
| 	type authReq struct { | ||||
| 		Identity identityReq `json:"identity"` | ||||
| 	} | ||||
|  | ||||
| 	type request struct { | ||||
| 		Auth authReq `json:"auth"` | ||||
| 	} | ||||
|  | ||||
| 	// Populate the request structure based on the provided arguments. Create and return an error | ||||
| 	// if insufficient or incompatible information is present. | ||||
| 	var req request | ||||
|  | ||||
| 	if opts.Password == "" { | ||||
| 		if opts.TokenID != "" { | ||||
| 			// Because we aren't using password authentication, it's an error to also provide any of the user-based authentication | ||||
| 			// parameters. | ||||
| 			if opts.Username != "" { | ||||
| 				return nil, ErrUsernameWithToken{} | ||||
| 			} | ||||
| 			if opts.UserID != "" { | ||||
| 				return nil, ErrUserIDWithToken{} | ||||
| 			} | ||||
| 			if opts.DomainID != "" { | ||||
| 				return nil, ErrDomainIDWithToken{} | ||||
| 			} | ||||
| 			if opts.DomainName != "" { | ||||
| 				return nil, ErrDomainNameWithToken{} | ||||
| 			} | ||||
|  | ||||
| 			// Configure the request for Token authentication. | ||||
| 			req.Auth.Identity.Methods = []string{"token"} | ||||
| 			req.Auth.Identity.Token = &tokenReq{ | ||||
| 				ID: opts.TokenID, | ||||
| 			} | ||||
|  | ||||
| 		} else if opts.ApplicationCredentialID != "" { | ||||
| 			// Configure the request for ApplicationCredentialID authentication. | ||||
| 			// https://github.com/openstack/keystoneauth/blob/stable/rocky/keystoneauth1/identity/v3/application_credential.py#L48-L67 | ||||
| 			// There are three kinds of possible application_credential requests | ||||
| 			// 1. application_credential id + secret | ||||
| 			// 2. application_credential name + secret + user_id | ||||
| 			// 3. application_credential name + secret + username + domain_id / domain_name | ||||
| 			if opts.ApplicationCredentialSecret == "" { | ||||
| 				return nil, ErrAppCredMissingSecret{} | ||||
| 			} | ||||
| 			req.Auth.Identity.Methods = []string{"application_credential"} | ||||
| 			req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{ | ||||
| 				ID:     &opts.ApplicationCredentialID, | ||||
| 				Secret: &opts.ApplicationCredentialSecret, | ||||
| 			} | ||||
| 		} else if opts.ApplicationCredentialName != "" { | ||||
| 			if opts.ApplicationCredentialSecret == "" { | ||||
| 				return nil, ErrAppCredMissingSecret{} | ||||
| 			} | ||||
|  | ||||
| 			var userRequest *userReq | ||||
|  | ||||
| 			if opts.UserID != "" { | ||||
| 				// UserID could be used without the domain information | ||||
| 				userRequest = &userReq{ | ||||
| 					ID: &opts.UserID, | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if userRequest == nil && opts.Username == "" { | ||||
| 				// Make sure that Username or UserID are provided | ||||
| 				return nil, ErrUsernameOrUserID{} | ||||
| 			} | ||||
|  | ||||
| 			if userRequest == nil && opts.DomainID != "" { | ||||
| 				userRequest = &userReq{ | ||||
| 					Name:   &opts.Username, | ||||
| 					Domain: &domainReq{ID: &opts.DomainID}, | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if userRequest == nil && opts.DomainName != "" { | ||||
| 				userRequest = &userReq{ | ||||
| 					Name:   &opts.Username, | ||||
| 					Domain: &domainReq{Name: &opts.DomainName}, | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Make sure that DomainID or DomainName are provided among Username | ||||
| 			if userRequest == nil { | ||||
| 				return nil, ErrDomainIDOrDomainName{} | ||||
| 			} | ||||
|  | ||||
| 			req.Auth.Identity.Methods = []string{"application_credential"} | ||||
| 			req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{ | ||||
| 				Name:   &opts.ApplicationCredentialName, | ||||
| 				User:   userRequest, | ||||
| 				Secret: &opts.ApplicationCredentialSecret, | ||||
| 			} | ||||
| 		} else { | ||||
| 			// If no password or token ID or ApplicationCredential are available, authentication can't continue. | ||||
| 			return nil, ErrMissingPassword{} | ||||
| 		} | ||||
| 	} else { | ||||
| 		// Password authentication. | ||||
| 		req.Auth.Identity.Methods = []string{"password"} | ||||
|  | ||||
| 		// At least one of Username and UserID must be specified. | ||||
| 		if opts.Username == "" && opts.UserID == "" { | ||||
| 			return nil, ErrUsernameOrUserID{} | ||||
| 		} | ||||
|  | ||||
| 		if opts.Username != "" { | ||||
| 			// If Username is provided, UserID may not be provided. | ||||
| 			if opts.UserID != "" { | ||||
| 				return nil, ErrUsernameOrUserID{} | ||||
| 			} | ||||
|  | ||||
| 			// Either DomainID or DomainName must also be specified. | ||||
| 			if opts.DomainID == "" && opts.DomainName == "" { | ||||
| 				return nil, ErrDomainIDOrDomainName{} | ||||
| 			} | ||||
|  | ||||
| 			if opts.DomainID != "" { | ||||
| 				if opts.DomainName != "" { | ||||
| 					return nil, ErrDomainIDOrDomainName{} | ||||
| 				} | ||||
|  | ||||
| 				// Configure the request for Username and Password authentication with a DomainID. | ||||
| 				req.Auth.Identity.Password = &passwordReq{ | ||||
| 					User: userReq{ | ||||
| 						Name:     &opts.Username, | ||||
| 						Password: opts.Password, | ||||
| 						Domain:   &domainReq{ID: &opts.DomainID}, | ||||
| 					}, | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if opts.DomainName != "" { | ||||
| 				// Configure the request for Username and Password authentication with a DomainName. | ||||
| 				req.Auth.Identity.Password = &passwordReq{ | ||||
| 					User: userReq{ | ||||
| 						Name:     &opts.Username, | ||||
| 						Password: opts.Password, | ||||
| 						Domain:   &domainReq{Name: &opts.DomainName}, | ||||
| 					}, | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if opts.UserID != "" { | ||||
| 			// If UserID is specified, neither DomainID nor DomainName may be. | ||||
| 			if opts.DomainID != "" { | ||||
| 				return nil, ErrDomainIDWithUserID{} | ||||
| 			} | ||||
| 			if opts.DomainName != "" { | ||||
| 				return nil, ErrDomainNameWithUserID{} | ||||
| 			} | ||||
|  | ||||
| 			// Configure the request for UserID and Password authentication. | ||||
| 			req.Auth.Identity.Password = &passwordReq{ | ||||
| 				User: userReq{ID: &opts.UserID, Password: opts.Password}, | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	b, err := BuildRequestBody(req, "") | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if len(scope) != 0 { | ||||
| 		b["auth"].(map[string]interface{})["scope"] = scope | ||||
| 	} | ||||
|  | ||||
| 	return b, nil | ||||
| } | ||||
|  | ||||
| func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { | ||||
| 	// For backwards compatibility. | ||||
| 	// If AuthOptions.Scope was not set, try to determine it. | ||||
| 	// This works well for common scenarios. | ||||
| 	if opts.Scope == nil { | ||||
| 		opts.Scope = new(AuthScope) | ||||
| 		if opts.TenantID != "" { | ||||
| 			opts.Scope.ProjectID = opts.TenantID | ||||
| 		} else { | ||||
| 			if opts.TenantName != "" { | ||||
| 				opts.Scope.ProjectName = opts.TenantName | ||||
| 				opts.Scope.DomainID = opts.DomainID | ||||
| 				opts.Scope.DomainName = opts.DomainName | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if opts.Scope.ProjectName != "" { | ||||
| 		// ProjectName provided: either DomainID or DomainName must also be supplied. | ||||
| 		// ProjectID may not be supplied. | ||||
| 		if opts.Scope.DomainID == "" && opts.Scope.DomainName == "" { | ||||
| 			return nil, ErrScopeDomainIDOrDomainName{} | ||||
| 		} | ||||
| 		if opts.Scope.ProjectID != "" { | ||||
| 			return nil, ErrScopeProjectIDOrProjectName{} | ||||
| 		} | ||||
|  | ||||
| 		if opts.Scope.DomainID != "" { | ||||
| 			// ProjectName + DomainID | ||||
| 			return map[string]interface{}{ | ||||
| 				"project": map[string]interface{}{ | ||||
| 					"name":   &opts.Scope.ProjectName, | ||||
| 					"domain": map[string]interface{}{"id": &opts.Scope.DomainID}, | ||||
| 				}, | ||||
| 			}, nil | ||||
| 		} | ||||
|  | ||||
| 		if opts.Scope.DomainName != "" { | ||||
| 			// ProjectName + DomainName | ||||
| 			return map[string]interface{}{ | ||||
| 				"project": map[string]interface{}{ | ||||
| 					"name":   &opts.Scope.ProjectName, | ||||
| 					"domain": map[string]interface{}{"name": &opts.Scope.DomainName}, | ||||
| 				}, | ||||
| 			}, nil | ||||
| 		} | ||||
| 	} else if opts.Scope.ProjectID != "" { | ||||
| 		// ProjectID provided. ProjectName, DomainID, and DomainName may not be provided. | ||||
| 		if opts.Scope.DomainID != "" { | ||||
| 			return nil, ErrScopeProjectIDAlone{} | ||||
| 		} | ||||
| 		if opts.Scope.DomainName != "" { | ||||
| 			return nil, ErrScopeProjectIDAlone{} | ||||
| 		} | ||||
|  | ||||
| 		// ProjectID | ||||
| 		return map[string]interface{}{ | ||||
| 			"project": map[string]interface{}{ | ||||
| 				"id": &opts.Scope.ProjectID, | ||||
| 			}, | ||||
| 		}, nil | ||||
| 	} else if opts.Scope.DomainID != "" { | ||||
| 		// DomainID provided. ProjectID, ProjectName, and DomainName may not be provided. | ||||
| 		if opts.Scope.DomainName != "" { | ||||
| 			return nil, ErrScopeDomainIDOrDomainName{} | ||||
| 		} | ||||
|  | ||||
| 		// DomainID | ||||
| 		return map[string]interface{}{ | ||||
| 			"domain": map[string]interface{}{ | ||||
| 				"id": &opts.Scope.DomainID, | ||||
| 			}, | ||||
| 		}, nil | ||||
| 	} else if opts.Scope.DomainName != "" { | ||||
| 		// DomainName | ||||
| 		return map[string]interface{}{ | ||||
| 			"domain": map[string]interface{}{ | ||||
| 				"name": &opts.Scope.DomainName, | ||||
| 			}, | ||||
| 		}, nil | ||||
| 	} | ||||
|  | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| func (opts AuthOptions) CanReauth() bool { | ||||
| 	return opts.AllowReauth | ||||
| } | ||||
							
								
								
									
										93
									
								
								vendor/github.com/gophercloud/gophercloud/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								vendor/github.com/gophercloud/gophercloud/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| /* | ||||
| Package gophercloud provides a multi-vendor interface to OpenStack-compatible | ||||
| clouds. The library has a three-level hierarchy: providers, services, and | ||||
| resources. | ||||
|  | ||||
| Authenticating with Providers | ||||
|  | ||||
| Provider structs represent the cloud providers that offer and manage a | ||||
| collection of services. You will generally want to create one Provider | ||||
| client per OpenStack cloud. | ||||
|  | ||||
| Use your OpenStack credentials to create a Provider client.  The | ||||
| IdentityEndpoint is typically refered to as "auth_url" or "OS_AUTH_URL" in | ||||
| information provided by the cloud operator. Additionally, the cloud may refer to | ||||
| TenantID or TenantName as project_id and project_name. Credentials are | ||||
| specified like so: | ||||
|  | ||||
|   opts := gophercloud.AuthOptions{ | ||||
|     IdentityEndpoint: "https://openstack.example.com:5000/v2.0", | ||||
|     Username: "{username}", | ||||
|     Password: "{password}", | ||||
|     TenantID: "{tenant_id}", | ||||
|   } | ||||
|  | ||||
|   provider, err := openstack.AuthenticatedClient(opts) | ||||
|  | ||||
| You may also use the openstack.AuthOptionsFromEnv() helper function. This | ||||
| function reads in standard environment variables frequently found in an | ||||
| OpenStack `openrc` file. Again note that Gophercloud currently uses "tenant" | ||||
| instead of "project". | ||||
|  | ||||
| 	opts, err := openstack.AuthOptionsFromEnv() | ||||
| 	provider, err := openstack.AuthenticatedClient(opts) | ||||
|  | ||||
| Service Clients | ||||
|  | ||||
| Service structs are specific to a provider and handle all of the logic and | ||||
| operations for a particular OpenStack service. Examples of services include: | ||||
| Compute, Object Storage, Block Storage. In order to define one, you need to | ||||
| pass in the parent provider, like so: | ||||
|  | ||||
|   opts := gophercloud.EndpointOpts{Region: "RegionOne"} | ||||
|  | ||||
|   client, err := openstack.NewComputeV2(provider, opts) | ||||
|  | ||||
| Resources | ||||
|  | ||||
| Resource structs are the domain models that services make use of in order | ||||
| to work with and represent the state of API resources: | ||||
|  | ||||
|   server, err := servers.Get(client, "{serverId}").Extract() | ||||
|  | ||||
| Intermediate Result structs are returned for API operations, which allow | ||||
| generic access to the HTTP headers, response body, and any errors associated | ||||
| with the network transaction. To turn a result into a usable resource struct, | ||||
| you must call the Extract method which is chained to the response, or an | ||||
| Extract function from an applicable extension: | ||||
|  | ||||
|   result := servers.Get(client, "{serverId}") | ||||
|  | ||||
|   // Attempt to extract the disk configuration from the OS-DCF disk config | ||||
|   // extension: | ||||
|   config, err := diskconfig.ExtractGet(result) | ||||
|  | ||||
| All requests that enumerate a collection return a Pager struct that is used to | ||||
| iterate through the results one page at a time. Use the EachPage method on that | ||||
| Pager to handle each successive Page in a closure, then use the appropriate | ||||
| extraction method from that request's package to interpret that Page as a slice | ||||
| of results: | ||||
|  | ||||
|   err := servers.List(client, nil).EachPage(func (page pagination.Page) (bool, error) { | ||||
|     s, err := servers.ExtractServers(page) | ||||
|     if err != nil { | ||||
|       return false, err | ||||
|     } | ||||
|  | ||||
|     // Handle the []servers.Server slice. | ||||
|  | ||||
|     // Return "false" or an error to prematurely stop fetching new pages. | ||||
|     return true, nil | ||||
|   }) | ||||
|  | ||||
| If you want to obtain the entire collection of pages without doing any | ||||
| intermediary processing on each page, you can use the AllPages method: | ||||
|  | ||||
| 	allPages, err := servers.List(client, nil).AllPages() | ||||
| 	allServers, err := servers.ExtractServers(allPages) | ||||
|  | ||||
| This top-level package contains utility functions and data types that are used | ||||
| throughout the provider and service packages. Of particular note for end users | ||||
| are the AuthOptions and EndpointOpts structs. | ||||
| */ | ||||
| package gophercloud | ||||
							
								
								
									
										76
									
								
								vendor/github.com/gophercloud/gophercloud/endpoint_search.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								vendor/github.com/gophercloud/gophercloud/endpoint_search.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| package gophercloud | ||||
|  | ||||
| // Availability indicates to whom a specific service endpoint is accessible: | ||||
| // the internet at large, internal networks only, or only to administrators. | ||||
| // Different identity services use different terminology for these. Identity v2 | ||||
| // lists them as different kinds of URLs within the service catalog ("adminURL", | ||||
| // "internalURL", and "publicURL"), while v3 lists them as "Interfaces" in an | ||||
| // endpoint's response. | ||||
| type Availability string | ||||
|  | ||||
| const ( | ||||
| 	// AvailabilityAdmin indicates that an endpoint is only available to | ||||
| 	// administrators. | ||||
| 	AvailabilityAdmin Availability = "admin" | ||||
|  | ||||
| 	// AvailabilityPublic indicates that an endpoint is available to everyone on | ||||
| 	// the internet. | ||||
| 	AvailabilityPublic Availability = "public" | ||||
|  | ||||
| 	// AvailabilityInternal indicates that an endpoint is only available within | ||||
| 	// the cluster's internal network. | ||||
| 	AvailabilityInternal Availability = "internal" | ||||
| ) | ||||
|  | ||||
| // EndpointOpts specifies search criteria used by queries against an | ||||
| // OpenStack service catalog. The options must contain enough information to | ||||
| // unambiguously identify one, and only one, endpoint within the catalog. | ||||
| // | ||||
| // Usually, these are passed to service client factory functions in a provider | ||||
| // package, like "openstack.NewComputeV2()". | ||||
| type EndpointOpts struct { | ||||
| 	// Type [required] is the service type for the client (e.g., "compute", | ||||
| 	// "object-store"). Generally, this will be supplied by the service client | ||||
| 	// function, but a user-given value will be honored if provided. | ||||
| 	Type string | ||||
|  | ||||
| 	// Name [optional] is the service name for the client (e.g., "nova") as it | ||||
| 	// appears in the service catalog. Services can have the same Type but a | ||||
| 	// different Name, which is why both Type and Name are sometimes needed. | ||||
| 	Name string | ||||
|  | ||||
| 	// Region [required] is the geographic region in which the endpoint resides, | ||||
| 	// generally specifying which datacenter should house your resources. | ||||
| 	// Required only for services that span multiple regions. | ||||
| 	Region string | ||||
|  | ||||
| 	// Availability [optional] is the visibility of the endpoint to be returned. | ||||
| 	// Valid types include the constants AvailabilityPublic, AvailabilityInternal, | ||||
| 	// or AvailabilityAdmin from this package. | ||||
| 	// | ||||
| 	// Availability is not required, and defaults to AvailabilityPublic. Not all | ||||
| 	// providers or services offer all Availability options. | ||||
| 	Availability Availability | ||||
| } | ||||
|  | ||||
| /* | ||||
| EndpointLocator is an internal function to be used by provider implementations. | ||||
|  | ||||
| It provides an implementation that locates a single endpoint from a service | ||||
| catalog for a specific ProviderClient based on user-provided EndpointOpts. The | ||||
| provider then uses it to discover related ServiceClients. | ||||
| */ | ||||
| type EndpointLocator func(EndpointOpts) (string, error) | ||||
|  | ||||
| // ApplyDefaults is an internal method to be used by provider implementations. | ||||
| // | ||||
| // It sets EndpointOpts fields if not already set, including a default type. | ||||
| // Currently, EndpointOpts.Availability defaults to the public endpoint. | ||||
| func (eo *EndpointOpts) ApplyDefaults(t string) { | ||||
| 	if eo.Type == "" { | ||||
| 		eo.Type = t | ||||
| 	} | ||||
| 	if eo.Availability == "" { | ||||
| 		eo.Availability = AvailabilityPublic | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										460
									
								
								vendor/github.com/gophercloud/gophercloud/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										460
									
								
								vendor/github.com/gophercloud/gophercloud/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,460 @@ | ||||
| package gophercloud | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| // BaseError is an error type that all other error types embed. | ||||
| type BaseError struct { | ||||
| 	DefaultErrString string | ||||
| 	Info             string | ||||
| } | ||||
|  | ||||
| func (e BaseError) Error() string { | ||||
| 	e.DefaultErrString = "An error occurred while executing a Gophercloud request." | ||||
| 	return e.choseErrString() | ||||
| } | ||||
|  | ||||
| func (e BaseError) choseErrString() string { | ||||
| 	if e.Info != "" { | ||||
| 		return e.Info | ||||
| 	} | ||||
| 	return e.DefaultErrString | ||||
| } | ||||
|  | ||||
| // ErrMissingInput is the error when input is required in a particular | ||||
| // situation but not provided by the user | ||||
| type ErrMissingInput struct { | ||||
| 	BaseError | ||||
| 	Argument string | ||||
| } | ||||
|  | ||||
| func (e ErrMissingInput) Error() string { | ||||
| 	e.DefaultErrString = fmt.Sprintf("Missing input for argument [%s]", e.Argument) | ||||
| 	return e.choseErrString() | ||||
| } | ||||
|  | ||||
| // ErrInvalidInput is an error type used for most non-HTTP Gophercloud errors. | ||||
| type ErrInvalidInput struct { | ||||
| 	ErrMissingInput | ||||
| 	Value interface{} | ||||
| } | ||||
|  | ||||
| func (e ErrInvalidInput) Error() string { | ||||
| 	e.DefaultErrString = fmt.Sprintf("Invalid input provided for argument [%s]: [%+v]", e.Argument, e.Value) | ||||
| 	return e.choseErrString() | ||||
| } | ||||
|  | ||||
| // ErrMissingEnvironmentVariable is the error when environment variable is required | ||||
| // in a particular situation but not provided by the user | ||||
| type ErrMissingEnvironmentVariable struct { | ||||
| 	BaseError | ||||
| 	EnvironmentVariable string | ||||
| } | ||||
|  | ||||
| func (e ErrMissingEnvironmentVariable) Error() string { | ||||
| 	e.DefaultErrString = fmt.Sprintf("Missing environment variable [%s]", e.EnvironmentVariable) | ||||
| 	return e.choseErrString() | ||||
| } | ||||
|  | ||||
| // ErrMissingAnyoneOfEnvironmentVariables is the error when anyone of the environment variables | ||||
| // is required in a particular situation but not provided by the user | ||||
| type ErrMissingAnyoneOfEnvironmentVariables struct { | ||||
| 	BaseError | ||||
| 	EnvironmentVariables []string | ||||
| } | ||||
|  | ||||
| func (e ErrMissingAnyoneOfEnvironmentVariables) Error() string { | ||||
| 	e.DefaultErrString = fmt.Sprintf( | ||||
| 		"Missing one of the following environment variables [%s]", | ||||
| 		strings.Join(e.EnvironmentVariables, ", "), | ||||
| 	) | ||||
| 	return e.choseErrString() | ||||
| } | ||||
|  | ||||
| // ErrUnexpectedResponseCode is returned by the Request method when a response code other than | ||||
| // those listed in OkCodes is encountered. | ||||
| type ErrUnexpectedResponseCode struct { | ||||
| 	BaseError | ||||
| 	URL      string | ||||
| 	Method   string | ||||
| 	Expected []int | ||||
| 	Actual   int | ||||
| 	Body     []byte | ||||
| } | ||||
|  | ||||
| func (e ErrUnexpectedResponseCode) Error() string { | ||||
| 	e.DefaultErrString = fmt.Sprintf( | ||||
| 		"Expected HTTP response code %v when accessing [%s %s], but got %d instead\n%s", | ||||
| 		e.Expected, e.Method, e.URL, e.Actual, e.Body, | ||||
| 	) | ||||
| 	return e.choseErrString() | ||||
| } | ||||
|  | ||||
| // ErrDefault400 is the default error type returned on a 400 HTTP response code. | ||||
| type ErrDefault400 struct { | ||||
| 	ErrUnexpectedResponseCode | ||||
| } | ||||
|  | ||||
| // ErrDefault401 is the default error type returned on a 401 HTTP response code. | ||||
| type ErrDefault401 struct { | ||||
| 	ErrUnexpectedResponseCode | ||||
| } | ||||
|  | ||||
| // ErrDefault403 is the default error type returned on a 403 HTTP response code. | ||||
| type ErrDefault403 struct { | ||||
| 	ErrUnexpectedResponseCode | ||||
| } | ||||
|  | ||||
| // ErrDefault404 is the default error type returned on a 404 HTTP response code. | ||||
| type ErrDefault404 struct { | ||||
| 	ErrUnexpectedResponseCode | ||||
| } | ||||
|  | ||||
| // ErrDefault405 is the default error type returned on a 405 HTTP response code. | ||||
| type ErrDefault405 struct { | ||||
| 	ErrUnexpectedResponseCode | ||||
| } | ||||
|  | ||||
| // ErrDefault408 is the default error type returned on a 408 HTTP response code. | ||||
| type ErrDefault408 struct { | ||||
| 	ErrUnexpectedResponseCode | ||||
| } | ||||
|  | ||||
| // ErrDefault429 is the default error type returned on a 429 HTTP response code. | ||||
| type ErrDefault429 struct { | ||||
| 	ErrUnexpectedResponseCode | ||||
| } | ||||
|  | ||||
| // ErrDefault500 is the default error type returned on a 500 HTTP response code. | ||||
| type ErrDefault500 struct { | ||||
| 	ErrUnexpectedResponseCode | ||||
| } | ||||
|  | ||||
| // ErrDefault503 is the default error type returned on a 503 HTTP response code. | ||||
| type ErrDefault503 struct { | ||||
| 	ErrUnexpectedResponseCode | ||||
| } | ||||
|  | ||||
| func (e ErrDefault400) Error() string { | ||||
| 	e.DefaultErrString = fmt.Sprintf( | ||||
| 		"Bad request with: [%s %s], error message: %s", | ||||
| 		e.Method, e.URL, e.Body, | ||||
| 	) | ||||
| 	return e.choseErrString() | ||||
| } | ||||
| func (e ErrDefault401) Error() string { | ||||
| 	return "Authentication failed" | ||||
| } | ||||
| func (e ErrDefault403) Error() string { | ||||
| 	e.DefaultErrString = fmt.Sprintf( | ||||
| 		"Request forbidden: [%s %s], error message: %s", | ||||
| 		e.Method, e.URL, e.Body, | ||||
| 	) | ||||
| 	return e.choseErrString() | ||||
| } | ||||
| func (e ErrDefault404) Error() string { | ||||
| 	return "Resource not found" | ||||
| } | ||||
| func (e ErrDefault405) Error() string { | ||||
| 	return "Method not allowed" | ||||
| } | ||||
| func (e ErrDefault408) Error() string { | ||||
| 	return "The server timed out waiting for the request" | ||||
| } | ||||
| func (e ErrDefault429) Error() string { | ||||
| 	return "Too many requests have been sent in a given amount of time. Pause" + | ||||
| 		" requests, wait up to one minute, and try again." | ||||
| } | ||||
| func (e ErrDefault500) Error() string { | ||||
| 	return "Internal Server Error" | ||||
| } | ||||
| func (e ErrDefault503) Error() string { | ||||
| 	return "The service is currently unable to handle the request due to a temporary" + | ||||
| 		" overloading or maintenance. This is a temporary condition. Try again later." | ||||
| } | ||||
|  | ||||
| // Err400er is the interface resource error types implement to override the error message | ||||
| // from a 400 error. | ||||
| type Err400er interface { | ||||
| 	Error400(ErrUnexpectedResponseCode) error | ||||
| } | ||||
|  | ||||
| // Err401er is the interface resource error types implement to override the error message | ||||
| // from a 401 error. | ||||
| type Err401er interface { | ||||
| 	Error401(ErrUnexpectedResponseCode) error | ||||
| } | ||||
|  | ||||
| // Err403er is the interface resource error types implement to override the error message | ||||
| // from a 403 error. | ||||
| type Err403er interface { | ||||
| 	Error403(ErrUnexpectedResponseCode) error | ||||
| } | ||||
|  | ||||
| // Err404er is the interface resource error types implement to override the error message | ||||
| // from a 404 error. | ||||
| type Err404er interface { | ||||
| 	Error404(ErrUnexpectedResponseCode) error | ||||
| } | ||||
|  | ||||
| // Err405er is the interface resource error types implement to override the error message | ||||
| // from a 405 error. | ||||
| type Err405er interface { | ||||
| 	Error405(ErrUnexpectedResponseCode) error | ||||
| } | ||||
|  | ||||
| // Err408er is the interface resource error types implement to override the error message | ||||
| // from a 408 error. | ||||
| type Err408er interface { | ||||
| 	Error408(ErrUnexpectedResponseCode) error | ||||
| } | ||||
|  | ||||
| // Err429er is the interface resource error types implement to override the error message | ||||
| // from a 429 error. | ||||
| type Err429er interface { | ||||
| 	Error429(ErrUnexpectedResponseCode) error | ||||
| } | ||||
|  | ||||
| // Err500er is the interface resource error types implement to override the error message | ||||
| // from a 500 error. | ||||
| type Err500er interface { | ||||
| 	Error500(ErrUnexpectedResponseCode) error | ||||
| } | ||||
|  | ||||
| // Err503er is the interface resource error types implement to override the error message | ||||
| // from a 503 error. | ||||
| type Err503er interface { | ||||
| 	Error503(ErrUnexpectedResponseCode) error | ||||
| } | ||||
|  | ||||
| // ErrTimeOut is the error type returned when an operations times out. | ||||
| type ErrTimeOut struct { | ||||
| 	BaseError | ||||
| } | ||||
|  | ||||
| func (e ErrTimeOut) Error() string { | ||||
| 	e.DefaultErrString = "A time out occurred" | ||||
| 	return e.choseErrString() | ||||
| } | ||||
|  | ||||
| // ErrUnableToReauthenticate is the error type returned when reauthentication fails. | ||||
| type ErrUnableToReauthenticate struct { | ||||
| 	BaseError | ||||
| 	ErrOriginal error | ||||
| } | ||||
|  | ||||
| func (e ErrUnableToReauthenticate) Error() string { | ||||
| 	e.DefaultErrString = fmt.Sprintf("Unable to re-authenticate: %s", e.ErrOriginal) | ||||
| 	return e.choseErrString() | ||||
| } | ||||
|  | ||||
| // ErrErrorAfterReauthentication is the error type returned when reauthentication | ||||
| // succeeds, but an error occurs afterword (usually an HTTP error). | ||||
| type ErrErrorAfterReauthentication struct { | ||||
| 	BaseError | ||||
| 	ErrOriginal error | ||||
| } | ||||
|  | ||||
| func (e ErrErrorAfterReauthentication) Error() string { | ||||
| 	e.DefaultErrString = fmt.Sprintf("Successfully re-authenticated, but got error executing request: %s", e.ErrOriginal) | ||||
| 	return e.choseErrString() | ||||
| } | ||||
|  | ||||
| // ErrServiceNotFound is returned when no service in a service catalog matches | ||||
| // the provided EndpointOpts. This is generally returned by provider service | ||||
| // factory methods like "NewComputeV2()" and can mean that a service is not | ||||
| // enabled for your account. | ||||
| type ErrServiceNotFound struct { | ||||
| 	BaseError | ||||
| } | ||||
|  | ||||
| func (e ErrServiceNotFound) Error() string { | ||||
| 	e.DefaultErrString = "No suitable service could be found in the service catalog." | ||||
| 	return e.choseErrString() | ||||
| } | ||||
|  | ||||
| // ErrEndpointNotFound is returned when no available endpoints match the | ||||
| // provided EndpointOpts. This is also generally returned by provider service | ||||
| // factory methods, and usually indicates that a region was specified | ||||
| // incorrectly. | ||||
| type ErrEndpointNotFound struct { | ||||
| 	BaseError | ||||
| } | ||||
|  | ||||
| func (e ErrEndpointNotFound) Error() string { | ||||
| 	e.DefaultErrString = "No suitable endpoint could be found in the service catalog." | ||||
| 	return e.choseErrString() | ||||
| } | ||||
|  | ||||
| // ErrResourceNotFound is the error when trying to retrieve a resource's | ||||
| // ID by name and the resource doesn't exist. | ||||
| type ErrResourceNotFound struct { | ||||
| 	BaseError | ||||
| 	Name         string | ||||
| 	ResourceType string | ||||
| } | ||||
|  | ||||
| func (e ErrResourceNotFound) Error() string { | ||||
| 	e.DefaultErrString = fmt.Sprintf("Unable to find %s with name %s", e.ResourceType, e.Name) | ||||
| 	return e.choseErrString() | ||||
| } | ||||
|  | ||||
| // ErrMultipleResourcesFound is the error when trying to retrieve a resource's | ||||
| // ID by name and multiple resources have the user-provided name. | ||||
| type ErrMultipleResourcesFound struct { | ||||
| 	BaseError | ||||
| 	Name         string | ||||
| 	Count        int | ||||
| 	ResourceType string | ||||
| } | ||||
|  | ||||
| func (e ErrMultipleResourcesFound) Error() string { | ||||
| 	e.DefaultErrString = fmt.Sprintf("Found %d %ss matching %s", e.Count, e.ResourceType, e.Name) | ||||
| 	return e.choseErrString() | ||||
| } | ||||
|  | ||||
| // ErrUnexpectedType is the error when an unexpected type is encountered | ||||
| type ErrUnexpectedType struct { | ||||
| 	BaseError | ||||
| 	Expected string | ||||
| 	Actual   string | ||||
| } | ||||
|  | ||||
| func (e ErrUnexpectedType) Error() string { | ||||
| 	e.DefaultErrString = fmt.Sprintf("Expected %s but got %s", e.Expected, e.Actual) | ||||
| 	return e.choseErrString() | ||||
| } | ||||
|  | ||||
| func unacceptedAttributeErr(attribute string) string { | ||||
| 	return fmt.Sprintf("The base Identity V3 API does not accept authentication by %s", attribute) | ||||
| } | ||||
|  | ||||
| func redundantWithTokenErr(attribute string) string { | ||||
| 	return fmt.Sprintf("%s may not be provided when authenticating with a TokenID", attribute) | ||||
| } | ||||
|  | ||||
| func redundantWithUserID(attribute string) string { | ||||
| 	return fmt.Sprintf("%s may not be provided when authenticating with a UserID", attribute) | ||||
| } | ||||
|  | ||||
| // ErrAPIKeyProvided indicates that an APIKey was provided but can't be used. | ||||
| type ErrAPIKeyProvided struct{ BaseError } | ||||
|  | ||||
| func (e ErrAPIKeyProvided) Error() string { | ||||
| 	return unacceptedAttributeErr("APIKey") | ||||
| } | ||||
|  | ||||
| // ErrTenantIDProvided indicates that a TenantID was provided but can't be used. | ||||
| type ErrTenantIDProvided struct{ BaseError } | ||||
|  | ||||
| func (e ErrTenantIDProvided) Error() string { | ||||
| 	return unacceptedAttributeErr("TenantID") | ||||
| } | ||||
|  | ||||
| // ErrTenantNameProvided indicates that a TenantName was provided but can't be used. | ||||
| type ErrTenantNameProvided struct{ BaseError } | ||||
|  | ||||
| func (e ErrTenantNameProvided) Error() string { | ||||
| 	return unacceptedAttributeErr("TenantName") | ||||
| } | ||||
|  | ||||
| // ErrUsernameWithToken indicates that a Username was provided, but token authentication is being used instead. | ||||
| type ErrUsernameWithToken struct{ BaseError } | ||||
|  | ||||
| func (e ErrUsernameWithToken) Error() string { | ||||
| 	return redundantWithTokenErr("Username") | ||||
| } | ||||
|  | ||||
| // ErrUserIDWithToken indicates that a UserID was provided, but token authentication is being used instead. | ||||
| type ErrUserIDWithToken struct{ BaseError } | ||||
|  | ||||
| func (e ErrUserIDWithToken) Error() string { | ||||
| 	return redundantWithTokenErr("UserID") | ||||
| } | ||||
|  | ||||
| // ErrDomainIDWithToken indicates that a DomainID was provided, but token authentication is being used instead. | ||||
| type ErrDomainIDWithToken struct{ BaseError } | ||||
|  | ||||
| func (e ErrDomainIDWithToken) Error() string { | ||||
| 	return redundantWithTokenErr("DomainID") | ||||
| } | ||||
|  | ||||
| // ErrDomainNameWithToken indicates that a DomainName was provided, but token authentication is being used instead.s | ||||
| type ErrDomainNameWithToken struct{ BaseError } | ||||
|  | ||||
| func (e ErrDomainNameWithToken) Error() string { | ||||
| 	return redundantWithTokenErr("DomainName") | ||||
| } | ||||
|  | ||||
| // ErrUsernameOrUserID indicates that neither username nor userID are specified, or both are at once. | ||||
| type ErrUsernameOrUserID struct{ BaseError } | ||||
|  | ||||
| func (e ErrUsernameOrUserID) Error() string { | ||||
| 	return "Exactly one of Username and UserID must be provided for password authentication" | ||||
| } | ||||
|  | ||||
| // ErrDomainIDWithUserID indicates that a DomainID was provided, but unnecessary because a UserID is being used. | ||||
| type ErrDomainIDWithUserID struct{ BaseError } | ||||
|  | ||||
| func (e ErrDomainIDWithUserID) Error() string { | ||||
| 	return redundantWithUserID("DomainID") | ||||
| } | ||||
|  | ||||
| // ErrDomainNameWithUserID indicates that a DomainName was provided, but unnecessary because a UserID is being used. | ||||
| type ErrDomainNameWithUserID struct{ BaseError } | ||||
|  | ||||
| func (e ErrDomainNameWithUserID) Error() string { | ||||
| 	return redundantWithUserID("DomainName") | ||||
| } | ||||
|  | ||||
| // ErrDomainIDOrDomainName indicates that a username was provided, but no domain to scope it. | ||||
| // It may also indicate that both a DomainID and a DomainName were provided at once. | ||||
| type ErrDomainIDOrDomainName struct{ BaseError } | ||||
|  | ||||
| func (e ErrDomainIDOrDomainName) Error() string { | ||||
| 	return "You must provide exactly one of DomainID or DomainName to authenticate by Username" | ||||
| } | ||||
|  | ||||
| // ErrMissingPassword indicates that no password was provided and no token is available. | ||||
| type ErrMissingPassword struct{ BaseError } | ||||
|  | ||||
| func (e ErrMissingPassword) Error() string { | ||||
| 	return "You must provide a password to authenticate" | ||||
| } | ||||
|  | ||||
| // ErrScopeDomainIDOrDomainName indicates that a domain ID or Name was required in a Scope, but not present. | ||||
| type ErrScopeDomainIDOrDomainName struct{ BaseError } | ||||
|  | ||||
| func (e ErrScopeDomainIDOrDomainName) Error() string { | ||||
| 	return "You must provide exactly one of DomainID or DomainName in a Scope with ProjectName" | ||||
| } | ||||
|  | ||||
| // ErrScopeProjectIDOrProjectName indicates that both a ProjectID and a ProjectName were provided in a Scope. | ||||
| type ErrScopeProjectIDOrProjectName struct{ BaseError } | ||||
|  | ||||
| func (e ErrScopeProjectIDOrProjectName) Error() string { | ||||
| 	return "You must provide at most one of ProjectID or ProjectName in a Scope" | ||||
| } | ||||
|  | ||||
| // ErrScopeProjectIDAlone indicates that a ProjectID was provided with other constraints in a Scope. | ||||
| type ErrScopeProjectIDAlone struct{ BaseError } | ||||
|  | ||||
| func (e ErrScopeProjectIDAlone) Error() string { | ||||
| 	return "ProjectID must be supplied alone in a Scope" | ||||
| } | ||||
|  | ||||
| // ErrScopeEmpty indicates that no credentials were provided in a Scope. | ||||
| type ErrScopeEmpty struct{ BaseError } | ||||
|  | ||||
| func (e ErrScopeEmpty) Error() string { | ||||
| 	return "You must provide either a Project or Domain in a Scope" | ||||
| } | ||||
|  | ||||
| // ErrAppCredMissingSecret indicates that no Application Credential Secret was provided with Application Credential ID or Name | ||||
| type ErrAppCredMissingSecret struct{ BaseError } | ||||
|  | ||||
| func (e ErrAppCredMissingSecret) Error() string { | ||||
| 	return "You must provide an Application Credential Secret" | ||||
| } | ||||
							
								
								
									
										114
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,114 @@ | ||||
| package openstack | ||||
|  | ||||
| import ( | ||||
| 	"os" | ||||
|  | ||||
| 	"github.com/gophercloud/gophercloud" | ||||
| ) | ||||
|  | ||||
| var nilOptions = gophercloud.AuthOptions{} | ||||
|  | ||||
| /* | ||||
| AuthOptionsFromEnv fills out an identity.AuthOptions structure with the | ||||
| settings found on the various OpenStack OS_* environment variables. | ||||
|  | ||||
| The following variables provide sources of truth: OS_AUTH_URL, OS_USERNAME, | ||||
| OS_PASSWORD, OS_TENANT_ID, and OS_TENANT_NAME. | ||||
|  | ||||
| Of these, OS_USERNAME, OS_PASSWORD, and OS_AUTH_URL must have settings, | ||||
| or an error will result.  OS_TENANT_ID, OS_TENANT_NAME, OS_PROJECT_ID, and | ||||
| OS_PROJECT_NAME are optional. | ||||
|  | ||||
| OS_TENANT_ID and OS_TENANT_NAME are mutually exclusive to OS_PROJECT_ID and | ||||
| OS_PROJECT_NAME. If OS_PROJECT_ID and OS_PROJECT_NAME are set, they will | ||||
| still be referred as "tenant" in Gophercloud. | ||||
|  | ||||
| To use this function, first set the OS_* environment variables (for example, | ||||
| by sourcing an `openrc` file), then: | ||||
|  | ||||
| 	opts, err := openstack.AuthOptionsFromEnv() | ||||
| 	provider, err := openstack.AuthenticatedClient(opts) | ||||
| */ | ||||
| func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { | ||||
| 	authURL := os.Getenv("OS_AUTH_URL") | ||||
| 	username := os.Getenv("OS_USERNAME") | ||||
| 	userID := os.Getenv("OS_USERID") | ||||
| 	password := os.Getenv("OS_PASSWORD") | ||||
| 	tenantID := os.Getenv("OS_TENANT_ID") | ||||
| 	tenantName := os.Getenv("OS_TENANT_NAME") | ||||
| 	domainID := os.Getenv("OS_DOMAIN_ID") | ||||
| 	domainName := os.Getenv("OS_DOMAIN_NAME") | ||||
| 	applicationCredentialID := os.Getenv("OS_APPLICATION_CREDENTIAL_ID") | ||||
| 	applicationCredentialName := os.Getenv("OS_APPLICATION_CREDENTIAL_NAME") | ||||
| 	applicationCredentialSecret := os.Getenv("OS_APPLICATION_CREDENTIAL_SECRET") | ||||
|  | ||||
| 	// If OS_PROJECT_ID is set, overwrite tenantID with the value. | ||||
| 	if v := os.Getenv("OS_PROJECT_ID"); v != "" { | ||||
| 		tenantID = v | ||||
| 	} | ||||
|  | ||||
| 	// If OS_PROJECT_NAME is set, overwrite tenantName with the value. | ||||
| 	if v := os.Getenv("OS_PROJECT_NAME"); v != "" { | ||||
| 		tenantName = v | ||||
| 	} | ||||
|  | ||||
| 	if authURL == "" { | ||||
| 		err := gophercloud.ErrMissingEnvironmentVariable{ | ||||
| 			EnvironmentVariable: "OS_AUTH_URL", | ||||
| 		} | ||||
| 		return nilOptions, err | ||||
| 	} | ||||
|  | ||||
| 	if userID == "" && username == "" { | ||||
| 		// Empty username and userID could be ignored, when applicationCredentialID and applicationCredentialSecret are set | ||||
| 		if applicationCredentialID == "" && applicationCredentialSecret == "" { | ||||
| 			err := gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ | ||||
| 				EnvironmentVariables: []string{"OS_USERID", "OS_USERNAME"}, | ||||
| 			} | ||||
| 			return nilOptions, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if password == "" && applicationCredentialID == "" && applicationCredentialName == "" { | ||||
| 		err := gophercloud.ErrMissingEnvironmentVariable{ | ||||
| 			EnvironmentVariable: "OS_PASSWORD", | ||||
| 		} | ||||
| 		return nilOptions, err | ||||
| 	} | ||||
|  | ||||
| 	if (applicationCredentialID != "" || applicationCredentialName != "") && applicationCredentialSecret == "" { | ||||
| 		err := gophercloud.ErrMissingEnvironmentVariable{ | ||||
| 			EnvironmentVariable: "OS_APPLICATION_CREDENTIAL_SECRET", | ||||
| 		} | ||||
| 		return nilOptions, err | ||||
| 	} | ||||
|  | ||||
| 	if applicationCredentialID == "" && applicationCredentialName != "" && applicationCredentialSecret != "" { | ||||
| 		if userID == "" && username == "" { | ||||
| 			return nilOptions, gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ | ||||
| 				EnvironmentVariables: []string{"OS_USERID", "OS_USERNAME"}, | ||||
| 			} | ||||
| 		} | ||||
| 		if username != "" && domainID == "" && domainName == "" { | ||||
| 			return nilOptions, gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ | ||||
| 				EnvironmentVariables: []string{"OS_DOMAIN_ID", "OS_DOMAIN_NAME"}, | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	ao := gophercloud.AuthOptions{ | ||||
| 		IdentityEndpoint:            authURL, | ||||
| 		UserID:                      userID, | ||||
| 		Username:                    username, | ||||
| 		Password:                    password, | ||||
| 		TenantID:                    tenantID, | ||||
| 		TenantName:                  tenantName, | ||||
| 		DomainID:                    domainID, | ||||
| 		DomainName:                  domainName, | ||||
| 		ApplicationCredentialID:     applicationCredentialID, | ||||
| 		ApplicationCredentialName:   applicationCredentialName, | ||||
| 		ApplicationCredentialSecret: applicationCredentialSecret, | ||||
| 	} | ||||
|  | ||||
| 	return ao, nil | ||||
| } | ||||
							
								
								
									
										429
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										429
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,429 @@ | ||||
| package openstack | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
|  | ||||
| 	"github.com/gophercloud/gophercloud" | ||||
| 	tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" | ||||
| 	tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" | ||||
| 	"github.com/gophercloud/gophercloud/openstack/utils" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// v2 represents Keystone v2. | ||||
| 	// It should never increase beyond 2.0. | ||||
| 	v2 = "v2.0" | ||||
|  | ||||
| 	// v3 represents Keystone v3. | ||||
| 	// The version can be anything from v3 to v3.x. | ||||
| 	v3 = "v3" | ||||
| ) | ||||
|  | ||||
| /* | ||||
| NewClient prepares an unauthenticated ProviderClient instance. | ||||
| Most users will probably prefer using the AuthenticatedClient function | ||||
| instead. | ||||
|  | ||||
| This is useful if you wish to explicitly control the version of the identity | ||||
| service that's used for authentication explicitly, for example. | ||||
|  | ||||
| A basic example of using this would be: | ||||
|  | ||||
| 	ao, err := openstack.AuthOptionsFromEnv() | ||||
| 	provider, err := openstack.NewClient(ao.IdentityEndpoint) | ||||
| 	client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{}) | ||||
| */ | ||||
| func NewClient(endpoint string) (*gophercloud.ProviderClient, error) { | ||||
| 	base, err := utils.BaseEndpoint(endpoint) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	endpoint = gophercloud.NormalizeURL(endpoint) | ||||
| 	base = gophercloud.NormalizeURL(base) | ||||
|  | ||||
| 	p := new(gophercloud.ProviderClient) | ||||
| 	p.IdentityBase = base | ||||
| 	p.IdentityEndpoint = endpoint | ||||
| 	p.UseTokenLock() | ||||
|  | ||||
| 	return p, nil | ||||
| } | ||||
|  | ||||
| /* | ||||
| AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint | ||||
| specified by the options, acquires a token, and returns a Provider Client | ||||
| instance that's ready to operate. | ||||
|  | ||||
| If the full path to a versioned identity endpoint was specified  (example: | ||||
| http://example.com:5000/v3), that path will be used as the endpoint to query. | ||||
|  | ||||
| If a versionless endpoint was specified (example: http://example.com:5000/), | ||||
| the endpoint will be queried to determine which versions of the identity service | ||||
| are available, then chooses the most recent or most supported version. | ||||
|  | ||||
| Example: | ||||
|  | ||||
| 	ao, err := openstack.AuthOptionsFromEnv() | ||||
| 	provider, err := openstack.AuthenticatedClient(ao) | ||||
| 	client, err := openstack.NewNetworkV2(client, gophercloud.EndpointOpts{ | ||||
| 		Region: os.Getenv("OS_REGION_NAME"), | ||||
| 	}) | ||||
| */ | ||||
| func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) { | ||||
| 	client, err := NewClient(options.IdentityEndpoint) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	err = Authenticate(client, options) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return client, nil | ||||
| } | ||||
|  | ||||
| // Authenticate or re-authenticate against the most recent identity service | ||||
| // supported at the provided endpoint. | ||||
| func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error { | ||||
| 	versions := []*utils.Version{ | ||||
| 		{ID: v2, Priority: 20, Suffix: "/v2.0/"}, | ||||
| 		{ID: v3, Priority: 30, Suffix: "/v3/"}, | ||||
| 	} | ||||
|  | ||||
| 	chosen, endpoint, err := utils.ChooseVersion(client, versions) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	switch chosen.ID { | ||||
| 	case v2: | ||||
| 		return v2auth(client, endpoint, options, gophercloud.EndpointOpts{}) | ||||
| 	case v3: | ||||
| 		return v3auth(client, endpoint, &options, gophercloud.EndpointOpts{}) | ||||
| 	default: | ||||
| 		// The switch statement must be out of date from the versions list. | ||||
| 		return fmt.Errorf("Unrecognized identity version: %s", chosen.ID) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // AuthenticateV2 explicitly authenticates against the identity v2 endpoint. | ||||
| func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { | ||||
| 	return v2auth(client, "", options, eo) | ||||
| } | ||||
|  | ||||
| func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { | ||||
| 	v2Client, err := NewIdentityV2(client, eo) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if endpoint != "" { | ||||
| 		v2Client.Endpoint = endpoint | ||||
| 	} | ||||
|  | ||||
| 	v2Opts := tokens2.AuthOptions{ | ||||
| 		IdentityEndpoint: options.IdentityEndpoint, | ||||
| 		Username:         options.Username, | ||||
| 		Password:         options.Password, | ||||
| 		TenantID:         options.TenantID, | ||||
| 		TenantName:       options.TenantName, | ||||
| 		AllowReauth:      options.AllowReauth, | ||||
| 		TokenID:          options.TokenID, | ||||
| 	} | ||||
|  | ||||
| 	result := tokens2.Create(v2Client, v2Opts) | ||||
|  | ||||
| 	token, err := result.ExtractToken() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	catalog, err := result.ExtractServiceCatalog() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if options.AllowReauth { | ||||
| 		// here we're creating a throw-away client (tac). it's a copy of the user's provider client, but | ||||
| 		// with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`, | ||||
| 		// this should retry authentication only once | ||||
| 		tac := *client | ||||
| 		tac.SetThrowaway(true) | ||||
| 		tac.ReauthFunc = nil | ||||
| 		tac.TokenID = "" | ||||
| 		tao := options | ||||
| 		tao.AllowReauth = false | ||||
| 		client.ReauthFunc = func() error { | ||||
| 			err := v2auth(&tac, endpoint, tao, eo) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			client.TokenID = tac.TokenID | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
| 	client.TokenID = token.ID | ||||
| 	client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) { | ||||
| 		return V2EndpointURL(catalog, opts) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // AuthenticateV3 explicitly authenticates against the identity v3 service. | ||||
| func AuthenticateV3(client *gophercloud.ProviderClient, options tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { | ||||
| 	return v3auth(client, "", options, eo) | ||||
| } | ||||
|  | ||||
| func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { | ||||
| 	// Override the generated service endpoint with the one returned by the version endpoint. | ||||
| 	v3Client, err := NewIdentityV3(client, eo) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if endpoint != "" { | ||||
| 		v3Client.Endpoint = endpoint | ||||
| 	} | ||||
|  | ||||
| 	result := tokens3.Create(v3Client, opts) | ||||
|  | ||||
| 	token, err := result.ExtractToken() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	catalog, err := result.ExtractServiceCatalog() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	client.TokenID = token.ID | ||||
|  | ||||
| 	if opts.CanReauth() { | ||||
| 		// here we're creating a throw-away client (tac). it's a copy of the user's provider client, but | ||||
| 		// with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`, | ||||
| 		// this should retry authentication only once | ||||
| 		tac := *client | ||||
| 		tac.SetThrowaway(true) | ||||
| 		tac.ReauthFunc = nil | ||||
| 		tac.TokenID = "" | ||||
| 		var tao tokens3.AuthOptionsBuilder | ||||
| 		switch ot := opts.(type) { | ||||
| 		case *gophercloud.AuthOptions: | ||||
| 			o := *ot | ||||
| 			o.AllowReauth = false | ||||
| 			tao = &o | ||||
| 		case *tokens3.AuthOptions: | ||||
| 			o := *ot | ||||
| 			o.AllowReauth = false | ||||
| 			tao = &o | ||||
| 		default: | ||||
| 			tao = opts | ||||
| 		} | ||||
| 		client.ReauthFunc = func() error { | ||||
| 			err := v3auth(&tac, endpoint, tao, eo) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			client.TokenID = tac.TokenID | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
| 	client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) { | ||||
| 		return V3EndpointURL(catalog, opts) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // NewIdentityV2 creates a ServiceClient that may be used to interact with the | ||||
| // v2 identity service. | ||||
| func NewIdentityV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	endpoint := client.IdentityBase + "v2.0/" | ||||
| 	clientType := "identity" | ||||
| 	var err error | ||||
| 	if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) { | ||||
| 		eo.ApplyDefaults(clientType) | ||||
| 		endpoint, err = client.EndpointLocator(eo) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return &gophercloud.ServiceClient{ | ||||
| 		ProviderClient: client, | ||||
| 		Endpoint:       endpoint, | ||||
| 		Type:           clientType, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // NewIdentityV3 creates a ServiceClient that may be used to access the v3 | ||||
| // identity service. | ||||
| func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	endpoint := client.IdentityBase + "v3/" | ||||
| 	clientType := "identity" | ||||
| 	var err error | ||||
| 	if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) { | ||||
| 		eo.ApplyDefaults(clientType) | ||||
| 		endpoint, err = client.EndpointLocator(eo) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Ensure endpoint still has a suffix of v3. | ||||
| 	// This is because EndpointLocator might have found a versionless | ||||
| 	// endpoint or the published endpoint is still /v2.0. In both | ||||
| 	// cases, we need to fix the endpoint to point to /v3. | ||||
| 	base, err := utils.BaseEndpoint(endpoint) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	base = gophercloud.NormalizeURL(base) | ||||
|  | ||||
| 	endpoint = base + "v3/" | ||||
|  | ||||
| 	return &gophercloud.ServiceClient{ | ||||
| 		ProviderClient: client, | ||||
| 		Endpoint:       endpoint, | ||||
| 		Type:           clientType, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func initClientOpts(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts, clientType string) (*gophercloud.ServiceClient, error) { | ||||
| 	sc := new(gophercloud.ServiceClient) | ||||
| 	eo.ApplyDefaults(clientType) | ||||
| 	url, err := client.EndpointLocator(eo) | ||||
| 	if err != nil { | ||||
| 		return sc, err | ||||
| 	} | ||||
| 	sc.ProviderClient = client | ||||
| 	sc.Endpoint = url | ||||
| 	sc.Type = clientType | ||||
| 	return sc, nil | ||||
| } | ||||
|  | ||||
| // NewObjectStorageV1 creates a ServiceClient that may be used with the v1 | ||||
| // object storage package. | ||||
| func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	return initClientOpts(client, eo, "object-store") | ||||
| } | ||||
|  | ||||
| // NewComputeV2 creates a ServiceClient that may be used with the v2 compute | ||||
| // package. | ||||
| func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	return initClientOpts(client, eo, "compute") | ||||
| } | ||||
|  | ||||
| // NewNetworkV2 creates a ServiceClient that may be used with the v2 network | ||||
| // package. | ||||
| func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	sc, err := initClientOpts(client, eo, "network") | ||||
| 	sc.ResourceBase = sc.Endpoint + "v2.0/" | ||||
| 	return sc, err | ||||
| } | ||||
|  | ||||
| // NewBlockStorageV1 creates a ServiceClient that may be used to access the v1 | ||||
| // block storage service. | ||||
| func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	return initClientOpts(client, eo, "volume") | ||||
| } | ||||
|  | ||||
| // NewBlockStorageV2 creates a ServiceClient that may be used to access the v2 | ||||
| // block storage service. | ||||
| func NewBlockStorageV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	return initClientOpts(client, eo, "volumev2") | ||||
| } | ||||
|  | ||||
| // NewBlockStorageV3 creates a ServiceClient that may be used to access the v3 block storage service. | ||||
| func NewBlockStorageV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	return initClientOpts(client, eo, "volumev3") | ||||
| } | ||||
|  | ||||
| // NewSharedFileSystemV2 creates a ServiceClient that may be used to access the v2 shared file system service. | ||||
| func NewSharedFileSystemV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	return initClientOpts(client, eo, "sharev2") | ||||
| } | ||||
|  | ||||
| // NewCDNV1 creates a ServiceClient that may be used to access the OpenStack v1 | ||||
| // CDN service. | ||||
| func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	return initClientOpts(client, eo, "cdn") | ||||
| } | ||||
|  | ||||
| // NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 | ||||
| // orchestration service. | ||||
| func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	return initClientOpts(client, eo, "orchestration") | ||||
| } | ||||
|  | ||||
| // NewDBV1 creates a ServiceClient that may be used to access the v1 DB service. | ||||
| func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	return initClientOpts(client, eo, "database") | ||||
| } | ||||
|  | ||||
| // NewDNSV2 creates a ServiceClient that may be used to access the v2 DNS | ||||
| // service. | ||||
| func NewDNSV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	sc, err := initClientOpts(client, eo, "dns") | ||||
| 	sc.ResourceBase = sc.Endpoint + "v2/" | ||||
| 	return sc, err | ||||
| } | ||||
|  | ||||
| // NewImageServiceV2 creates a ServiceClient that may be used to access the v2 | ||||
| // image service. | ||||
| func NewImageServiceV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	sc, err := initClientOpts(client, eo, "image") | ||||
| 	sc.ResourceBase = sc.Endpoint + "v2/" | ||||
| 	return sc, err | ||||
| } | ||||
|  | ||||
| // NewLoadBalancerV2 creates a ServiceClient that may be used to access the v2 | ||||
| // load balancer service. | ||||
| func NewLoadBalancerV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	sc, err := initClientOpts(client, eo, "load-balancer") | ||||
| 	sc.ResourceBase = sc.Endpoint + "v2.0/" | ||||
| 	return sc, err | ||||
| } | ||||
|  | ||||
| // NewClusteringV1 creates a ServiceClient that may be used with the v1 clustering | ||||
| // package. | ||||
| func NewClusteringV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	return initClientOpts(client, eo, "clustering") | ||||
| } | ||||
|  | ||||
| // NewMessagingV2 creates a ServiceClient that may be used with the v2 messaging | ||||
| // service. | ||||
| func NewMessagingV2(client *gophercloud.ProviderClient, clientID string, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	sc, err := initClientOpts(client, eo, "messaging") | ||||
| 	sc.MoreHeaders = map[string]string{"Client-ID": clientID} | ||||
| 	return sc, err | ||||
| } | ||||
|  | ||||
| // NewContainerV1 creates a ServiceClient that may be used with v1 container package | ||||
| func NewContainerV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	return initClientOpts(client, eo, "container") | ||||
| } | ||||
|  | ||||
| // NewKeyManagerV1 creates a ServiceClient that may be used with the v1 key | ||||
| // manager service. | ||||
| func NewKeyManagerV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	sc, err := initClientOpts(client, eo, "key-manager") | ||||
| 	sc.ResourceBase = sc.Endpoint + "v1/" | ||||
| 	return sc, err | ||||
| } | ||||
|  | ||||
| // NewContainerInfraV1 creates a ServiceClient that may be used with the v1 container infra management | ||||
| // package. | ||||
| func NewContainerInfraV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	return initClientOpts(client, eo, "container-infra") | ||||
| } | ||||
|  | ||||
| // NewWorkflowV2 creates a ServiceClient that may be used with the v2 workflow management package. | ||||
| func NewWorkflowV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { | ||||
| 	return initClientOpts(client, eo, "workflowv2") | ||||
| } | ||||
							
								
								
									
										54
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| /* | ||||
| Package recordsets provides information and interaction with the zone API | ||||
| resource for the OpenStack DNS service. | ||||
|  | ||||
| Example to List RecordSets by Zone | ||||
|  | ||||
| 	listOpts := recordsets.ListOpts{ | ||||
| 		Type: "A", | ||||
| 	} | ||||
|  | ||||
| 	zoneID := "fff121f5-c506-410a-a69e-2d73ef9cbdbd" | ||||
|  | ||||
| 	allPages, err := recordsets.ListByZone(dnsClient, zoneID, listOpts).AllPages() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| 	allRRs, err := recordsets.ExtractRecordSets(allPages() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| 	for _, rr := range allRRs { | ||||
| 		fmt.Printf("%+v\n", rr) | ||||
| 	} | ||||
|  | ||||
| Example to Create a RecordSet | ||||
|  | ||||
| 	createOpts := recordsets.CreateOpts{ | ||||
| 		Name:        "example.com.", | ||||
| 		Type:        "A", | ||||
| 		TTL:         3600, | ||||
| 		Description: "This is a recordset.", | ||||
| 		Records:     []string{"10.1.0.2"}, | ||||
| 	} | ||||
|  | ||||
| 	zoneID := "fff121f5-c506-410a-a69e-2d73ef9cbdbd" | ||||
|  | ||||
| 	rr, err := recordsets.Create(dnsClient, zoneID, createOpts).Extract() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| Example to Delete a RecordSet | ||||
|  | ||||
| 	zoneID := "fff121f5-c506-410a-a69e-2d73ef9cbdbd" | ||||
| 	recordsetID := "d96ed01a-b439-4eb8-9b90-7a9f71017f7b" | ||||
|  | ||||
| 	err := recordsets.Delete(dnsClient, zoneID, recordsetID).ExtractErr() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| */ | ||||
| package recordsets | ||||
							
								
								
									
										166
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/requests.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/requests.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,166 @@ | ||||
| package recordsets | ||||
|  | ||||
| import ( | ||||
| 	"github.com/gophercloud/gophercloud" | ||||
| 	"github.com/gophercloud/gophercloud/pagination" | ||||
| ) | ||||
|  | ||||
| // ListOptsBuilder allows extensions to add additional parameters to the | ||||
| // List request. | ||||
| type ListOptsBuilder interface { | ||||
| 	ToRecordSetListQuery() (string, error) | ||||
| } | ||||
|  | ||||
| // ListOpts allows the filtering and sorting of paginated collections through | ||||
| // the API. Filtering is achieved by passing in struct field values that map to | ||||
| // the server attributes you want to see returned. Marker and Limit are used | ||||
| // for pagination. | ||||
| // https://developer.openstack.org/api-ref/dns/ | ||||
| type ListOpts struct { | ||||
| 	// Integer value for the limit of values to return. | ||||
| 	Limit int `q:"limit"` | ||||
|  | ||||
| 	// UUID of the recordset at which you want to set a marker. | ||||
| 	Marker string `q:"marker"` | ||||
|  | ||||
| 	Data        string `q:"data"` | ||||
| 	Description string `q:"description"` | ||||
| 	Name        string `q:"name"` | ||||
| 	SortDir     string `q:"sort_dir"` | ||||
| 	SortKey     string `q:"sort_key"` | ||||
| 	Status      string `q:"status"` | ||||
| 	TTL         int    `q:"ttl"` | ||||
| 	Type        string `q:"type"` | ||||
| 	ZoneID      string `q:"zone_id"` | ||||
| } | ||||
|  | ||||
| // ToRecordSetListQuery formats a ListOpts into a query string. | ||||
| func (opts ListOpts) ToRecordSetListQuery() (string, error) { | ||||
| 	q, err := gophercloud.BuildQueryString(opts) | ||||
| 	return q.String(), err | ||||
| } | ||||
|  | ||||
| // ListByZone implements the recordset list request. | ||||
| func ListByZone(client *gophercloud.ServiceClient, zoneID string, opts ListOptsBuilder) pagination.Pager { | ||||
| 	url := baseURL(client, zoneID) | ||||
| 	if opts != nil { | ||||
| 		query, err := opts.ToRecordSetListQuery() | ||||
| 		if err != nil { | ||||
| 			return pagination.Pager{Err: err} | ||||
| 		} | ||||
| 		url += query | ||||
| 	} | ||||
| 	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { | ||||
| 		return RecordSetPage{pagination.LinkedPageBase{PageResult: r}} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // Get implements the recordset Get request. | ||||
| func Get(client *gophercloud.ServiceClient, zoneID string, rrsetID string) (r GetResult) { | ||||
| 	_, r.Err = client.Get(rrsetURL(client, zoneID, rrsetID), &r.Body, nil) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // CreateOptsBuilder allows extensions to add additional attributes to the | ||||
| // Create request. | ||||
| type CreateOptsBuilder interface { | ||||
| 	ToRecordSetCreateMap() (map[string]interface{}, error) | ||||
| } | ||||
|  | ||||
| // CreateOpts specifies the base attributes that may be used to create a | ||||
| // RecordSet. | ||||
| type CreateOpts struct { | ||||
| 	// Name is the name of the RecordSet. | ||||
| 	Name string `json:"name" required:"true"` | ||||
|  | ||||
| 	// Description is a description of the RecordSet. | ||||
| 	Description string `json:"description,omitempty"` | ||||
|  | ||||
| 	// Records are the DNS records of the RecordSet. | ||||
| 	Records []string `json:"records,omitempty"` | ||||
|  | ||||
| 	// TTL is the time to live of the RecordSet. | ||||
| 	TTL int `json:"ttl,omitempty"` | ||||
|  | ||||
| 	// Type is the RRTYPE of the RecordSet. | ||||
| 	Type string `json:"type,omitempty"` | ||||
| } | ||||
|  | ||||
| // ToRecordSetCreateMap formats an CreateOpts structure into a request body. | ||||
| func (opts CreateOpts) ToRecordSetCreateMap() (map[string]interface{}, error) { | ||||
| 	b, err := gophercloud.BuildRequestBody(opts, "") | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return b, nil | ||||
| } | ||||
|  | ||||
| // Create creates a recordset in a given zone. | ||||
| func Create(client *gophercloud.ServiceClient, zoneID string, opts CreateOptsBuilder) (r CreateResult) { | ||||
| 	b, err := opts.ToRecordSetCreateMap() | ||||
| 	if err != nil { | ||||
| 		r.Err = err | ||||
| 		return | ||||
| 	} | ||||
| 	_, r.Err = client.Post(baseURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ | ||||
| 		OkCodes: []int{201, 202}, | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // UpdateOptsBuilder allows extensions to add additional attributes to the | ||||
| // Update request. | ||||
| type UpdateOptsBuilder interface { | ||||
| 	ToRecordSetUpdateMap() (map[string]interface{}, error) | ||||
| } | ||||
|  | ||||
| // UpdateOpts specifies the base attributes that may be updated on an existing | ||||
| // RecordSet. | ||||
| type UpdateOpts struct { | ||||
| 	// Description is a description of the RecordSet. | ||||
| 	Description *string `json:"description,omitempty"` | ||||
|  | ||||
| 	// TTL is the time to live of the RecordSet. | ||||
| 	TTL int `json:"ttl,omitempty"` | ||||
|  | ||||
| 	// Records are the DNS records of the RecordSet. | ||||
| 	Records []string `json:"records,omitempty"` | ||||
| } | ||||
|  | ||||
| // ToRecordSetUpdateMap formats an UpdateOpts structure into a request body. | ||||
| func (opts UpdateOpts) ToRecordSetUpdateMap() (map[string]interface{}, error) { | ||||
| 	b, err := gophercloud.BuildRequestBody(opts, "") | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if opts.TTL > 0 { | ||||
| 		b["ttl"] = opts.TTL | ||||
| 	} else { | ||||
| 		b["ttl"] = nil | ||||
| 	} | ||||
|  | ||||
| 	return b, nil | ||||
| } | ||||
|  | ||||
| // Update updates a recordset in a given zone | ||||
| func Update(client *gophercloud.ServiceClient, zoneID string, rrsetID string, opts UpdateOptsBuilder) (r UpdateResult) { | ||||
| 	b, err := opts.ToRecordSetUpdateMap() | ||||
| 	if err != nil { | ||||
| 		r.Err = err | ||||
| 		return | ||||
| 	} | ||||
| 	_, r.Err = client.Put(rrsetURL(client, zoneID, rrsetID), &b, &r.Body, &gophercloud.RequestOpts{ | ||||
| 		OkCodes: []int{200, 202}, | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // Delete removes an existing RecordSet. | ||||
| func Delete(client *gophercloud.ServiceClient, zoneID string, rrsetID string) (r DeleteResult) { | ||||
| 	_, r.Err = client.Delete(rrsetURL(client, zoneID, rrsetID), &gophercloud.RequestOpts{ | ||||
| 		OkCodes: []int{202}, | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										147
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/results.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/results.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,147 @@ | ||||
| package recordsets | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/gophercloud/gophercloud" | ||||
| 	"github.com/gophercloud/gophercloud/pagination" | ||||
| ) | ||||
|  | ||||
| type commonResult struct { | ||||
| 	gophercloud.Result | ||||
| } | ||||
|  | ||||
| // Extract interprets a GetResult, CreateResult or UpdateResult as a RecordSet. | ||||
| // An error is returned if the original call or the extraction failed. | ||||
| func (r commonResult) Extract() (*RecordSet, error) { | ||||
| 	var s *RecordSet | ||||
| 	err := r.ExtractInto(&s) | ||||
| 	return s, err | ||||
| } | ||||
|  | ||||
| // CreateResult is the result of a Create operation. Call its Extract method to | ||||
| // interpret the result as a RecordSet. | ||||
| type CreateResult struct { | ||||
| 	commonResult | ||||
| } | ||||
|  | ||||
| // GetResult is the result of a Get operation. Call its Extract method to | ||||
| // interpret the result as a RecordSet. | ||||
| type GetResult struct { | ||||
| 	commonResult | ||||
| } | ||||
|  | ||||
| // RecordSetPage is a single page of RecordSet results. | ||||
| type RecordSetPage struct { | ||||
| 	pagination.LinkedPageBase | ||||
| } | ||||
|  | ||||
| // UpdateResult is result of an Update operation. Call its Extract method to | ||||
| // interpret the result as a RecordSet. | ||||
| type UpdateResult struct { | ||||
| 	commonResult | ||||
| } | ||||
|  | ||||
| // DeleteResult is result of a Delete operation. Call its ExtractErr method to | ||||
| // determine if the operation succeeded or failed. | ||||
| type DeleteResult struct { | ||||
| 	gophercloud.ErrResult | ||||
| } | ||||
|  | ||||
| // IsEmpty returns true if the page contains no results. | ||||
| func (r RecordSetPage) IsEmpty() (bool, error) { | ||||
| 	s, err := ExtractRecordSets(r) | ||||
| 	return len(s) == 0, err | ||||
| } | ||||
|  | ||||
| // ExtractRecordSets extracts a slice of RecordSets from a List result. | ||||
| func ExtractRecordSets(r pagination.Page) ([]RecordSet, error) { | ||||
| 	var s struct { | ||||
| 		RecordSets []RecordSet `json:"recordsets"` | ||||
| 	} | ||||
| 	err := (r.(RecordSetPage)).ExtractInto(&s) | ||||
| 	return s.RecordSets, err | ||||
| } | ||||
|  | ||||
| // RecordSet represents a DNS Record Set. | ||||
| type RecordSet struct { | ||||
| 	// ID is the unique ID of the recordset | ||||
| 	ID string `json:"id"` | ||||
|  | ||||
| 	// ZoneID is the ID of the zone the recordset belongs to. | ||||
| 	ZoneID string `json:"zone_id"` | ||||
|  | ||||
| 	// ProjectID is the ID of the project that owns the recordset. | ||||
| 	ProjectID string `json:"project_id"` | ||||
|  | ||||
| 	// Name is the name of the recordset. | ||||
| 	Name string `json:"name"` | ||||
|  | ||||
| 	// ZoneName is the name of the zone the recordset belongs to. | ||||
| 	ZoneName string `json:"zone_name"` | ||||
|  | ||||
| 	// Type is the RRTYPE of the recordset. | ||||
| 	Type string `json:"type"` | ||||
|  | ||||
| 	// Records are the DNS records of the recordset. | ||||
| 	Records []string `json:"records"` | ||||
|  | ||||
| 	// TTL is the time to live of the recordset. | ||||
| 	TTL int `json:"ttl"` | ||||
|  | ||||
| 	// Status is the status of the recordset. | ||||
| 	Status string `json:"status"` | ||||
|  | ||||
| 	// Action is the current action in progress of the recordset. | ||||
| 	Action string `json:"action"` | ||||
|  | ||||
| 	// Description is the description of the recordset. | ||||
| 	Description string `json:"description"` | ||||
|  | ||||
| 	// Version is the revision of the recordset. | ||||
| 	Version int `json:"version"` | ||||
|  | ||||
| 	// CreatedAt is the date when the recordset was created. | ||||
| 	CreatedAt time.Time `json:"-"` | ||||
|  | ||||
| 	// UpdatedAt is the date when the recordset was updated. | ||||
| 	UpdatedAt time.Time `json:"-"` | ||||
|  | ||||
| 	// Links includes HTTP references to the itself, | ||||
| 	// useful for passing along to other APIs that might want a recordset | ||||
| 	// reference. | ||||
| 	Links []gophercloud.Link `json:"-"` | ||||
| } | ||||
|  | ||||
| func (r *RecordSet) UnmarshalJSON(b []byte) error { | ||||
| 	type tmp RecordSet | ||||
| 	var s struct { | ||||
| 		tmp | ||||
| 		CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` | ||||
| 		UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` | ||||
| 		Links     map[string]interface{}          `json:"links"` | ||||
| 	} | ||||
| 	err := json.Unmarshal(b, &s) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	*r = RecordSet(s.tmp) | ||||
|  | ||||
| 	r.CreatedAt = time.Time(s.CreatedAt) | ||||
| 	r.UpdatedAt = time.Time(s.UpdatedAt) | ||||
|  | ||||
| 	if s.Links != nil { | ||||
| 		for rel, href := range s.Links { | ||||
| 			if v, ok := href.(string); ok { | ||||
| 				link := gophercloud.Link{ | ||||
| 					Rel:  rel, | ||||
| 					Href: v, | ||||
| 				} | ||||
| 				r.Links = append(r.Links, link) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return err | ||||
| } | ||||
							
								
								
									
										11
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/urls.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/urls.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| package recordsets | ||||
|  | ||||
| import "github.com/gophercloud/gophercloud" | ||||
|  | ||||
| func baseURL(c *gophercloud.ServiceClient, zoneID string) string { | ||||
| 	return c.ServiceURL("zones", zoneID, "recordsets") | ||||
| } | ||||
|  | ||||
| func rrsetURL(c *gophercloud.ServiceClient, zoneID string, rrsetID string) string { | ||||
| 	return c.ServiceURL("zones", zoneID, "recordsets", rrsetID) | ||||
| } | ||||
							
								
								
									
										48
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| /* | ||||
| Package zones provides information and interaction with the zone API | ||||
| resource for the OpenStack DNS service. | ||||
|  | ||||
| Example to List Zones | ||||
|  | ||||
| 	listOpts := zones.ListOpts{ | ||||
| 		Email: "jdoe@example.com", | ||||
| 	} | ||||
|  | ||||
| 	allPages, err := zones.List(dnsClient, listOpts).AllPages() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| 	allZones, err := zones.ExtractZones(allPages) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| 	for _, zone := range allZones { | ||||
| 		fmt.Printf("%+v\n", zone) | ||||
| 	} | ||||
|  | ||||
| Example to Create a Zone | ||||
|  | ||||
| 	createOpts := zones.CreateOpts{ | ||||
| 		Name:        "example.com.", | ||||
| 		Email:       "jdoe@example.com", | ||||
| 		Type:        "PRIMARY", | ||||
| 		TTL:         7200, | ||||
| 		Description: "This is a zone.", | ||||
| 	} | ||||
|  | ||||
| 	zone, err := zones.Create(dnsClient, createOpts).Extract() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| Example to Delete a Zone | ||||
|  | ||||
| 	zoneID := "99d10f68-5623-4491-91a0-6daafa32b60e" | ||||
| 	err := zones.Delete(dnsClient, zoneID).ExtractErr() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| */ | ||||
| package zones | ||||
							
								
								
									
										174
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/requests.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/requests.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | ||||
| package zones | ||||
|  | ||||
| import ( | ||||
| 	"github.com/gophercloud/gophercloud" | ||||
| 	"github.com/gophercloud/gophercloud/pagination" | ||||
| ) | ||||
|  | ||||
| // ListOptsBuilder allows extensions to add parameters to the List request. | ||||
| type ListOptsBuilder interface { | ||||
| 	ToZoneListQuery() (string, error) | ||||
| } | ||||
|  | ||||
| // ListOpts allows the filtering and sorting of paginated collections through | ||||
| // the API. Filtering is achieved by passing in struct field values that map to | ||||
| // the server attributes you want to see returned. Marker and Limit are used | ||||
| // for pagination. | ||||
| // https://developer.openstack.org/api-ref/dns/ | ||||
| type ListOpts struct { | ||||
| 	// Integer value for the limit of values to return. | ||||
| 	Limit int `q:"limit"` | ||||
|  | ||||
| 	// UUID of the zone at which you want to set a marker. | ||||
| 	Marker string `q:"marker"` | ||||
|  | ||||
| 	Description string `q:"description"` | ||||
| 	Email       string `q:"email"` | ||||
| 	Name        string `q:"name"` | ||||
| 	SortDir     string `q:"sort_dir"` | ||||
| 	SortKey     string `q:"sort_key"` | ||||
| 	Status      string `q:"status"` | ||||
| 	TTL         int    `q:"ttl"` | ||||
| 	Type        string `q:"type"` | ||||
| } | ||||
|  | ||||
| // ToZoneListQuery formats a ListOpts into a query string. | ||||
| func (opts ListOpts) ToZoneListQuery() (string, error) { | ||||
| 	q, err := gophercloud.BuildQueryString(opts) | ||||
| 	return q.String(), err | ||||
| } | ||||
|  | ||||
| // List implements a zone List request. | ||||
| func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { | ||||
| 	url := baseURL(client) | ||||
| 	if opts != nil { | ||||
| 		query, err := opts.ToZoneListQuery() | ||||
| 		if err != nil { | ||||
| 			return pagination.Pager{Err: err} | ||||
| 		} | ||||
| 		url += query | ||||
| 	} | ||||
| 	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { | ||||
| 		return ZonePage{pagination.LinkedPageBase{PageResult: r}} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // Get returns information about a zone, given its ID. | ||||
| func Get(client *gophercloud.ServiceClient, zoneID string) (r GetResult) { | ||||
| 	_, r.Err = client.Get(zoneURL(client, zoneID), &r.Body, nil) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // CreateOptsBuilder allows extensions to add additional attributes to the | ||||
| // Create request. | ||||
| type CreateOptsBuilder interface { | ||||
| 	ToZoneCreateMap() (map[string]interface{}, error) | ||||
| } | ||||
|  | ||||
| // CreateOpts specifies the attributes used to create a zone. | ||||
| type CreateOpts struct { | ||||
| 	// Attributes are settings that supply hints and filters for the zone. | ||||
| 	Attributes map[string]string `json:"attributes,omitempty"` | ||||
|  | ||||
| 	// Email contact of the zone. | ||||
| 	Email string `json:"email,omitempty"` | ||||
|  | ||||
| 	// Description of the zone. | ||||
| 	Description string `json:"description,omitempty"` | ||||
|  | ||||
| 	// Name of the zone. | ||||
| 	Name string `json:"name" required:"true"` | ||||
|  | ||||
| 	// Masters specifies zone masters if this is a secondary zone. | ||||
| 	Masters []string `json:"masters,omitempty"` | ||||
|  | ||||
| 	// TTL is the time to live of the zone. | ||||
| 	TTL int `json:"-"` | ||||
|  | ||||
| 	// Type specifies if this is a primary or secondary zone. | ||||
| 	Type string `json:"type,omitempty"` | ||||
| } | ||||
|  | ||||
| // ToZoneCreateMap formats an CreateOpts structure into a request body. | ||||
| func (opts CreateOpts) ToZoneCreateMap() (map[string]interface{}, error) { | ||||
| 	b, err := gophercloud.BuildRequestBody(opts, "") | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if opts.TTL > 0 { | ||||
| 		b["ttl"] = opts.TTL | ||||
| 	} | ||||
|  | ||||
| 	return b, nil | ||||
| } | ||||
|  | ||||
| // Create implements a zone create request. | ||||
| func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { | ||||
| 	b, err := opts.ToZoneCreateMap() | ||||
| 	if err != nil { | ||||
| 		r.Err = err | ||||
| 		return | ||||
| 	} | ||||
| 	_, r.Err = client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{ | ||||
| 		OkCodes: []int{201, 202}, | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // UpdateOptsBuilder allows extensions to add additional attributes to the | ||||
| // Update request. | ||||
| type UpdateOptsBuilder interface { | ||||
| 	ToZoneUpdateMap() (map[string]interface{}, error) | ||||
| } | ||||
|  | ||||
| // UpdateOpts specifies the attributes to update a zone. | ||||
| type UpdateOpts struct { | ||||
| 	// Email contact of the zone. | ||||
| 	Email string `json:"email,omitempty"` | ||||
|  | ||||
| 	// TTL is the time to live of the zone. | ||||
| 	TTL int `json:"-"` | ||||
|  | ||||
| 	// Masters specifies zone masters if this is a secondary zone. | ||||
| 	Masters []string `json:"masters,omitempty"` | ||||
|  | ||||
| 	// Description of the zone. | ||||
| 	Description *string `json:"description,omitempty"` | ||||
| } | ||||
|  | ||||
| // ToZoneUpdateMap formats an UpdateOpts structure into a request body. | ||||
| func (opts UpdateOpts) ToZoneUpdateMap() (map[string]interface{}, error) { | ||||
| 	b, err := gophercloud.BuildRequestBody(opts, "") | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if opts.TTL > 0 { | ||||
| 		b["ttl"] = opts.TTL | ||||
| 	} | ||||
|  | ||||
| 	return b, nil | ||||
| } | ||||
|  | ||||
| // Update implements a zone update request. | ||||
| func Update(client *gophercloud.ServiceClient, zoneID string, opts UpdateOptsBuilder) (r UpdateResult) { | ||||
| 	b, err := opts.ToZoneUpdateMap() | ||||
| 	if err != nil { | ||||
| 		r.Err = err | ||||
| 		return | ||||
| 	} | ||||
| 	_, r.Err = client.Patch(zoneURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ | ||||
| 		OkCodes: []int{200, 202}, | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // Delete implements a zone delete request. | ||||
| func Delete(client *gophercloud.ServiceClient, zoneID string) (r DeleteResult) { | ||||
| 	_, r.Err = client.Delete(zoneURL(client, zoneID), &gophercloud.RequestOpts{ | ||||
| 		OkCodes:      []int{202}, | ||||
| 		JSONResponse: &r.Body, | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										166
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/results.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/results.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,166 @@ | ||||
| package zones | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/gophercloud/gophercloud" | ||||
| 	"github.com/gophercloud/gophercloud/pagination" | ||||
| ) | ||||
|  | ||||
| type commonResult struct { | ||||
| 	gophercloud.Result | ||||
| } | ||||
|  | ||||
| // Extract interprets a GetResult, CreateResult or UpdateResult as a Zone. | ||||
| // An error is returned if the original call or the extraction failed. | ||||
| func (r commonResult) Extract() (*Zone, error) { | ||||
| 	var s *Zone | ||||
| 	err := r.ExtractInto(&s) | ||||
| 	return s, err | ||||
| } | ||||
|  | ||||
| // CreateResult is the result of a Create request. Call its Extract method | ||||
| // to interpret the result as a Zone. | ||||
| type CreateResult struct { | ||||
| 	commonResult | ||||
| } | ||||
|  | ||||
| // GetResult is the result of a Get request. Call its Extract method | ||||
| // to interpret the result as a Zone. | ||||
| type GetResult struct { | ||||
| 	commonResult | ||||
| } | ||||
|  | ||||
| // UpdateResult is the result of an Update request. Call its Extract method | ||||
| // to interpret the result as a Zone. | ||||
| type UpdateResult struct { | ||||
| 	commonResult | ||||
| } | ||||
|  | ||||
| // DeleteResult is the result of a Delete request. Call its ExtractErr method | ||||
| // to determine if the request succeeded or failed. | ||||
| type DeleteResult struct { | ||||
| 	commonResult | ||||
| } | ||||
|  | ||||
| // ZonePage is a single page of Zone results. | ||||
| type ZonePage struct { | ||||
| 	pagination.LinkedPageBase | ||||
| } | ||||
|  | ||||
| // IsEmpty returns true if the page contains no results. | ||||
| func (r ZonePage) IsEmpty() (bool, error) { | ||||
| 	s, err := ExtractZones(r) | ||||
| 	return len(s) == 0, err | ||||
| } | ||||
|  | ||||
| // ExtractZones extracts a slice of Zones from a List result. | ||||
| func ExtractZones(r pagination.Page) ([]Zone, error) { | ||||
| 	var s struct { | ||||
| 		Zones []Zone `json:"zones"` | ||||
| 	} | ||||
| 	err := (r.(ZonePage)).ExtractInto(&s) | ||||
| 	return s.Zones, err | ||||
| } | ||||
|  | ||||
| // Zone represents a DNS zone. | ||||
| type Zone struct { | ||||
| 	// ID uniquely identifies this zone amongst all other zones, including those | ||||
| 	// not accessible to the current tenant. | ||||
| 	ID string `json:"id"` | ||||
|  | ||||
| 	// PoolID is the ID for the pool hosting this zone. | ||||
| 	PoolID string `json:"pool_id"` | ||||
|  | ||||
| 	// ProjectID identifies the project/tenant owning this resource. | ||||
| 	ProjectID string `json:"project_id"` | ||||
|  | ||||
| 	// Name is the DNS Name for the zone. | ||||
| 	Name string `json:"name"` | ||||
|  | ||||
| 	// Email for the zone. Used in SOA records for the zone. | ||||
| 	Email string `json:"email"` | ||||
|  | ||||
| 	// Description for this zone. | ||||
| 	Description string `json:"description"` | ||||
|  | ||||
| 	// TTL is the Time to Live for the zone. | ||||
| 	TTL int `json:"ttl"` | ||||
|  | ||||
| 	// Serial is the current serial number for the zone. | ||||
| 	Serial int `json:"-"` | ||||
|  | ||||
| 	// Status is the status of the resource. | ||||
| 	Status string `json:"status"` | ||||
|  | ||||
| 	// Action is the current action in progress on the resource. | ||||
| 	Action string `json:"action"` | ||||
|  | ||||
| 	// Version of the resource. | ||||
| 	Version int `json:"version"` | ||||
|  | ||||
| 	// Attributes for the zone. | ||||
| 	Attributes map[string]string `json:"attributes"` | ||||
|  | ||||
| 	// Type of zone. Primary is controlled by Designate. | ||||
| 	// Secondary zones are slaved from another DNS Server. | ||||
| 	// Defaults to Primary. | ||||
| 	Type string `json:"type"` | ||||
|  | ||||
| 	// Masters is the servers for slave servers to get DNS information from. | ||||
| 	Masters []string `json:"masters"` | ||||
|  | ||||
| 	// CreatedAt is the date when the zone was created. | ||||
| 	CreatedAt time.Time `json:"-"` | ||||
|  | ||||
| 	// UpdatedAt is the date when the last change was made to the zone. | ||||
| 	UpdatedAt time.Time `json:"-"` | ||||
|  | ||||
| 	// TransferredAt is the last time an update was retrieved from the | ||||
| 	// master servers. | ||||
| 	TransferredAt time.Time `json:"-"` | ||||
|  | ||||
| 	// Links includes HTTP references to the itself, useful for passing along | ||||
| 	// to other APIs that might want a server reference. | ||||
| 	Links map[string]interface{} `json:"links"` | ||||
| } | ||||
|  | ||||
| func (r *Zone) UnmarshalJSON(b []byte) error { | ||||
| 	type tmp Zone | ||||
| 	var s struct { | ||||
| 		tmp | ||||
| 		CreatedAt     gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` | ||||
| 		UpdatedAt     gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` | ||||
| 		TransferredAt gophercloud.JSONRFC3339MilliNoZ `json:"transferred_at"` | ||||
| 		Serial        interface{}                     `json:"serial"` | ||||
| 	} | ||||
| 	err := json.Unmarshal(b, &s) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	*r = Zone(s.tmp) | ||||
|  | ||||
| 	r.CreatedAt = time.Time(s.CreatedAt) | ||||
| 	r.UpdatedAt = time.Time(s.UpdatedAt) | ||||
| 	r.TransferredAt = time.Time(s.TransferredAt) | ||||
|  | ||||
| 	switch t := s.Serial.(type) { | ||||
| 	case float64: | ||||
| 		r.Serial = int(t) | ||||
| 	case string: | ||||
| 		switch t { | ||||
| 		case "": | ||||
| 			r.Serial = 0 | ||||
| 		default: | ||||
| 			serial, err := strconv.ParseFloat(t, 64) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			r.Serial = int(serial) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return err | ||||
| } | ||||
							
								
								
									
										11
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/urls.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/urls.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| package zones | ||||
|  | ||||
| import "github.com/gophercloud/gophercloud" | ||||
|  | ||||
| func baseURL(c *gophercloud.ServiceClient) string { | ||||
| 	return c.ServiceURL("zones") | ||||
| } | ||||
|  | ||||
| func zoneURL(c *gophercloud.ServiceClient, zoneID string) string { | ||||
| 	return c.ServiceURL("zones", zoneID) | ||||
| } | ||||
							
								
								
									
										14
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| /* | ||||
| Package openstack contains resources for the individual OpenStack projects | ||||
| supported in Gophercloud. It also includes functions to authenticate to an | ||||
| OpenStack cloud and for provisioning various service-level clients. | ||||
|  | ||||
| Example of Creating a Service Client | ||||
|  | ||||
| 	ao, err := openstack.AuthOptionsFromEnv() | ||||
| 	provider, err := openstack.AuthenticatedClient(ao) | ||||
| 	client, err := openstack.NewNetworkV2(client, gophercloud.EndpointOpts{ | ||||
| 		Region: os.Getenv("OS_REGION_NAME"), | ||||
| 	}) | ||||
| */ | ||||
| package openstack | ||||
							
								
								
									
										107
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| package openstack | ||||
|  | ||||
| import ( | ||||
| 	"github.com/gophercloud/gophercloud" | ||||
| 	tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" | ||||
| 	tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" | ||||
| ) | ||||
|  | ||||
| /* | ||||
| V2EndpointURL discovers the endpoint URL for a specific service from a | ||||
| ServiceCatalog acquired during the v2 identity service. | ||||
|  | ||||
| The specified EndpointOpts are used to identify a unique, unambiguous endpoint | ||||
| to return. It's an error both when multiple endpoints match the provided | ||||
| criteria and when none do. The minimum that can be specified is a Type, but you | ||||
| will also often need to specify a Name and/or a Region depending on what's | ||||
| available on your OpenStack deployment. | ||||
| */ | ||||
| func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) { | ||||
| 	// Extract Endpoints from the catalog entries that match the requested Type, Name if provided, and Region if provided. | ||||
| 	var endpoints = make([]tokens2.Endpoint, 0, 1) | ||||
| 	for _, entry := range catalog.Entries { | ||||
| 		if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) { | ||||
| 			for _, endpoint := range entry.Endpoints { | ||||
| 				if opts.Region == "" || endpoint.Region == opts.Region { | ||||
| 					endpoints = append(endpoints, endpoint) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Report an error if the options were ambiguous. | ||||
| 	if len(endpoints) > 1 { | ||||
| 		err := &ErrMultipleMatchingEndpointsV2{} | ||||
| 		err.Endpoints = endpoints | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	// Extract the appropriate URL from the matching Endpoint. | ||||
| 	for _, endpoint := range endpoints { | ||||
| 		switch opts.Availability { | ||||
| 		case gophercloud.AvailabilityPublic: | ||||
| 			return gophercloud.NormalizeURL(endpoint.PublicURL), nil | ||||
| 		case gophercloud.AvailabilityInternal: | ||||
| 			return gophercloud.NormalizeURL(endpoint.InternalURL), nil | ||||
| 		case gophercloud.AvailabilityAdmin: | ||||
| 			return gophercloud.NormalizeURL(endpoint.AdminURL), nil | ||||
| 		default: | ||||
| 			err := &ErrInvalidAvailabilityProvided{} | ||||
| 			err.Argument = "Availability" | ||||
| 			err.Value = opts.Availability | ||||
| 			return "", err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Report an error if there were no matching endpoints. | ||||
| 	err := &gophercloud.ErrEndpointNotFound{} | ||||
| 	return "", err | ||||
| } | ||||
|  | ||||
| /* | ||||
| V3EndpointURL discovers the endpoint URL for a specific service from a Catalog | ||||
| acquired during the v3 identity service. | ||||
|  | ||||
| The specified EndpointOpts are used to identify a unique, unambiguous endpoint | ||||
| to return. It's an error both when multiple endpoints match the provided | ||||
| criteria and when none do. The minimum that can be specified is a Type, but you | ||||
| will also often need to specify a Name and/or a Region depending on what's | ||||
| available on your OpenStack deployment. | ||||
| */ | ||||
| func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) { | ||||
| 	// Extract Endpoints from the catalog entries that match the requested Type, Interface, | ||||
| 	// Name if provided, and Region if provided. | ||||
| 	var endpoints = make([]tokens3.Endpoint, 0, 1) | ||||
| 	for _, entry := range catalog.Entries { | ||||
| 		if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) { | ||||
| 			for _, endpoint := range entry.Endpoints { | ||||
| 				if opts.Availability != gophercloud.AvailabilityAdmin && | ||||
| 					opts.Availability != gophercloud.AvailabilityPublic && | ||||
| 					opts.Availability != gophercloud.AvailabilityInternal { | ||||
| 					err := &ErrInvalidAvailabilityProvided{} | ||||
| 					err.Argument = "Availability" | ||||
| 					err.Value = opts.Availability | ||||
| 					return "", err | ||||
| 				} | ||||
| 				if (opts.Availability == gophercloud.Availability(endpoint.Interface)) && | ||||
| 					(opts.Region == "" || endpoint.Region == opts.Region || endpoint.RegionID == opts.Region) { | ||||
| 					endpoints = append(endpoints, endpoint) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Report an error if the options were ambiguous. | ||||
| 	if len(endpoints) > 1 { | ||||
| 		return "", ErrMultipleMatchingEndpointsV3{Endpoints: endpoints} | ||||
| 	} | ||||
|  | ||||
| 	// Extract the URL from the matching Endpoint. | ||||
| 	for _, endpoint := range endpoints { | ||||
| 		return gophercloud.NormalizeURL(endpoint.URL), nil | ||||
| 	} | ||||
|  | ||||
| 	// Report an error if there were no matching endpoints. | ||||
| 	err := &gophercloud.ErrEndpointNotFound{} | ||||
| 	return "", err | ||||
| } | ||||
							
								
								
									
										71
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| package openstack | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/gophercloud/gophercloud" | ||||
| 	tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" | ||||
| 	tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" | ||||
| ) | ||||
|  | ||||
| // ErrEndpointNotFound is the error when no suitable endpoint can be found | ||||
| // in the user's catalog | ||||
| type ErrEndpointNotFound struct{ gophercloud.BaseError } | ||||
|  | ||||
| func (e ErrEndpointNotFound) Error() string { | ||||
| 	return "No suitable endpoint could be found in the service catalog." | ||||
| } | ||||
|  | ||||
| // ErrInvalidAvailabilityProvided is the error when an invalid endpoint | ||||
| // availability is provided | ||||
| type ErrInvalidAvailabilityProvided struct{ gophercloud.ErrInvalidInput } | ||||
|  | ||||
| func (e ErrInvalidAvailabilityProvided) Error() string { | ||||
| 	return fmt.Sprintf("Unexpected availability in endpoint query: %s", e.Value) | ||||
| } | ||||
|  | ||||
| // ErrMultipleMatchingEndpointsV2 is the error when more than one endpoint | ||||
| // for the given options is found in the v2 catalog | ||||
| type ErrMultipleMatchingEndpointsV2 struct { | ||||
| 	gophercloud.BaseError | ||||
| 	Endpoints []tokens2.Endpoint | ||||
| } | ||||
|  | ||||
| func (e ErrMultipleMatchingEndpointsV2) Error() string { | ||||
| 	return fmt.Sprintf("Discovered %d matching endpoints: %#v", len(e.Endpoints), e.Endpoints) | ||||
| } | ||||
|  | ||||
| // ErrMultipleMatchingEndpointsV3 is the error when more than one endpoint | ||||
| // for the given options is found in the v3 catalog | ||||
| type ErrMultipleMatchingEndpointsV3 struct { | ||||
| 	gophercloud.BaseError | ||||
| 	Endpoints []tokens3.Endpoint | ||||
| } | ||||
|  | ||||
| func (e ErrMultipleMatchingEndpointsV3) Error() string { | ||||
| 	return fmt.Sprintf("Discovered %d matching endpoints: %#v", len(e.Endpoints), e.Endpoints) | ||||
| } | ||||
|  | ||||
| // ErrNoAuthURL is the error when the OS_AUTH_URL environment variable is not | ||||
| // found | ||||
| type ErrNoAuthURL struct{ gophercloud.ErrInvalidInput } | ||||
|  | ||||
| func (e ErrNoAuthURL) Error() string { | ||||
| 	return "Environment variable OS_AUTH_URL needs to be set." | ||||
| } | ||||
|  | ||||
| // ErrNoUsername is the error when the OS_USERNAME environment variable is not | ||||
| // found | ||||
| type ErrNoUsername struct{ gophercloud.ErrInvalidInput } | ||||
|  | ||||
| func (e ErrNoUsername) Error() string { | ||||
| 	return "Environment variable OS_USERNAME needs to be set." | ||||
| } | ||||
|  | ||||
| // ErrNoPassword is the error when the OS_PASSWORD environment variable is not | ||||
| // found | ||||
| type ErrNoPassword struct{ gophercloud.ErrInvalidInput } | ||||
|  | ||||
| func (e ErrNoPassword) Error() string { | ||||
| 	return "Environment variable OS_PASSWORD needs to be set." | ||||
| } | ||||
							
								
								
									
										65
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| /* | ||||
| Package tenants provides information and interaction with the | ||||
| tenants API resource for the OpenStack Identity service. | ||||
|  | ||||
| See http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2 | ||||
| and http://developer.openstack.org/api-ref-identity-v2.html#admin-tenants | ||||
| for more information. | ||||
|  | ||||
| Example to List Tenants | ||||
|  | ||||
| 	listOpts := tenants.ListOpts{ | ||||
| 		Limit: 2, | ||||
| 	} | ||||
|  | ||||
| 	allPages, err := tenants.List(identityClient, listOpts).AllPages() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| 	allTenants, err := tenants.ExtractTenants(allPages) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| 	for _, tenant := range allTenants { | ||||
| 		fmt.Printf("%+v\n", tenant) | ||||
| 	} | ||||
|  | ||||
| Example to Create a Tenant | ||||
|  | ||||
| 	createOpts := tenants.CreateOpts{ | ||||
| 		Name:        "tenant_name", | ||||
| 		Description: "this is a tenant", | ||||
| 		Enabled:     gophercloud.Enabled, | ||||
| 	} | ||||
|  | ||||
| 	tenant, err := tenants.Create(identityClient, createOpts).Extract() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| Example to Update a Tenant | ||||
|  | ||||
| 	tenantID := "e6db6ed6277c461a853458589063b295" | ||||
|  | ||||
| 	updateOpts := tenants.UpdateOpts{ | ||||
| 		Description: "this is a new description", | ||||
| 		Enabled:     gophercloud.Disabled, | ||||
| 	} | ||||
|  | ||||
| 	tenant, err := tenants.Update(identityClient, tenantID, updateOpts).Extract() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| Example to Delete a Tenant | ||||
|  | ||||
| 	tenantID := "e6db6ed6277c461a853458589063b295" | ||||
|  | ||||
| 	err := tenants.Delete(identitYClient, tenantID).ExtractErr() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| */ | ||||
| package tenants | ||||
							
								
								
									
										116
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,116 @@ | ||||
| package tenants | ||||
|  | ||||
| import ( | ||||
| 	"github.com/gophercloud/gophercloud" | ||||
| 	"github.com/gophercloud/gophercloud/pagination" | ||||
| ) | ||||
|  | ||||
| // ListOpts filters the Tenants that are returned by the List call. | ||||
| type ListOpts struct { | ||||
| 	// Marker is the ID of the last Tenant on the previous page. | ||||
| 	Marker string `q:"marker"` | ||||
|  | ||||
| 	// Limit specifies the page size. | ||||
| 	Limit int `q:"limit"` | ||||
| } | ||||
|  | ||||
| // List enumerates the Tenants to which the current token has access. | ||||
| func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager { | ||||
| 	url := listURL(client) | ||||
| 	if opts != nil { | ||||
| 		q, err := gophercloud.BuildQueryString(opts) | ||||
| 		if err != nil { | ||||
| 			return pagination.Pager{Err: err} | ||||
| 		} | ||||
| 		url += q.String() | ||||
| 	} | ||||
| 	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { | ||||
| 		return TenantPage{pagination.LinkedPageBase{PageResult: r}} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // CreateOpts represents the options needed when creating new tenant. | ||||
| type CreateOpts struct { | ||||
| 	// Name is the name of the tenant. | ||||
| 	Name string `json:"name" required:"true"` | ||||
|  | ||||
| 	// Description is the description of the tenant. | ||||
| 	Description string `json:"description,omitempty"` | ||||
|  | ||||
| 	// Enabled sets the tenant status to enabled or disabled. | ||||
| 	Enabled *bool `json:"enabled,omitempty"` | ||||
| } | ||||
|  | ||||
| // CreateOptsBuilder enables extensions to add additional parameters to the | ||||
| // Create request. | ||||
| type CreateOptsBuilder interface { | ||||
| 	ToTenantCreateMap() (map[string]interface{}, error) | ||||
| } | ||||
|  | ||||
| // ToTenantCreateMap assembles a request body based on the contents of | ||||
| // a CreateOpts. | ||||
| func (opts CreateOpts) ToTenantCreateMap() (map[string]interface{}, error) { | ||||
| 	return gophercloud.BuildRequestBody(opts, "tenant") | ||||
| } | ||||
|  | ||||
| // Create is the operation responsible for creating new tenant. | ||||
| func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { | ||||
| 	b, err := opts.ToTenantCreateMap() | ||||
| 	if err != nil { | ||||
| 		r.Err = err | ||||
| 		return | ||||
| 	} | ||||
| 	_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ | ||||
| 		OkCodes: []int{200, 201}, | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // Get requests details on a single tenant by ID. | ||||
| func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { | ||||
| 	_, r.Err = client.Get(getURL(client, id), &r.Body, nil) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // UpdateOptsBuilder allows extensions to add additional parameters to the | ||||
| // Update request. | ||||
| type UpdateOptsBuilder interface { | ||||
| 	ToTenantUpdateMap() (map[string]interface{}, error) | ||||
| } | ||||
|  | ||||
| // UpdateOpts specifies the base attributes that may be updated on an existing | ||||
| // tenant. | ||||
| type UpdateOpts struct { | ||||
| 	// Name is the name of the tenant. | ||||
| 	Name string `json:"name,omitempty"` | ||||
|  | ||||
| 	// Description is the description of the tenant. | ||||
| 	Description *string `json:"description,omitempty"` | ||||
|  | ||||
| 	// Enabled sets the tenant status to enabled or disabled. | ||||
| 	Enabled *bool `json:"enabled,omitempty"` | ||||
| } | ||||
|  | ||||
| // ToTenantUpdateMap formats an UpdateOpts structure into a request body. | ||||
| func (opts UpdateOpts) ToTenantUpdateMap() (map[string]interface{}, error) { | ||||
| 	return gophercloud.BuildRequestBody(opts, "tenant") | ||||
| } | ||||
|  | ||||
| // Update is the operation responsible for updating exist tenants by their TenantID. | ||||
| func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { | ||||
| 	b, err := opts.ToTenantUpdateMap() | ||||
| 	if err != nil { | ||||
| 		r.Err = err | ||||
| 		return | ||||
| 	} | ||||
| 	_, r.Err = client.Put(updateURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{ | ||||
| 		OkCodes: []int{200}, | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // Delete is the operation responsible for permanently deleting a tenant. | ||||
| func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { | ||||
| 	_, r.Err = client.Delete(deleteURL(client, id), nil) | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										91
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | ||||
| package tenants | ||||
|  | ||||
| import ( | ||||
| 	"github.com/gophercloud/gophercloud" | ||||
| 	"github.com/gophercloud/gophercloud/pagination" | ||||
| ) | ||||
|  | ||||
| // Tenant is a grouping of users in the identity service. | ||||
| type Tenant struct { | ||||
| 	// ID is a unique identifier for this tenant. | ||||
| 	ID string `json:"id"` | ||||
|  | ||||
| 	// Name is a friendlier user-facing name for this tenant. | ||||
| 	Name string `json:"name"` | ||||
|  | ||||
| 	// Description is a human-readable explanation of this Tenant's purpose. | ||||
| 	Description string `json:"description"` | ||||
|  | ||||
| 	// Enabled indicates whether or not a tenant is active. | ||||
| 	Enabled bool `json:"enabled"` | ||||
| } | ||||
|  | ||||
| // TenantPage is a single page of Tenant results. | ||||
| type TenantPage struct { | ||||
| 	pagination.LinkedPageBase | ||||
| } | ||||
|  | ||||
| // IsEmpty determines whether or not a page of Tenants contains any results. | ||||
| func (r TenantPage) IsEmpty() (bool, error) { | ||||
| 	tenants, err := ExtractTenants(r) | ||||
| 	return len(tenants) == 0, err | ||||
| } | ||||
|  | ||||
| // NextPageURL extracts the "next" link from the tenants_links section of the result. | ||||
| func (r TenantPage) NextPageURL() (string, error) { | ||||
| 	var s struct { | ||||
| 		Links []gophercloud.Link `json:"tenants_links"` | ||||
| 	} | ||||
| 	err := r.ExtractInto(&s) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	return gophercloud.ExtractNextURL(s.Links) | ||||
| } | ||||
|  | ||||
| // ExtractTenants returns a slice of Tenants contained in a single page of | ||||
| // results. | ||||
| func ExtractTenants(r pagination.Page) ([]Tenant, error) { | ||||
| 	var s struct { | ||||
| 		Tenants []Tenant `json:"tenants"` | ||||
| 	} | ||||
| 	err := (r.(TenantPage)).ExtractInto(&s) | ||||
| 	return s.Tenants, err | ||||
| } | ||||
|  | ||||
| type tenantResult struct { | ||||
| 	gophercloud.Result | ||||
| } | ||||
|  | ||||
| // Extract interprets any tenantResults as a Tenant. | ||||
| func (r tenantResult) Extract() (*Tenant, error) { | ||||
| 	var s struct { | ||||
| 		Tenant *Tenant `json:"tenant"` | ||||
| 	} | ||||
| 	err := r.ExtractInto(&s) | ||||
| 	return s.Tenant, err | ||||
| } | ||||
|  | ||||
| // GetResult is the response from a Get request. Call its Extract method to | ||||
| // interpret it as a Tenant. | ||||
| type GetResult struct { | ||||
| 	tenantResult | ||||
| } | ||||
|  | ||||
| // CreateResult is the response from a Create request. Call its Extract method | ||||
| // to interpret it as a Tenant. | ||||
| type CreateResult struct { | ||||
| 	tenantResult | ||||
| } | ||||
|  | ||||
| // DeleteResult is the response from a Get request. Call its ExtractErr method | ||||
| // to determine if the call succeeded or failed. | ||||
| type DeleteResult struct { | ||||
| 	gophercloud.ErrResult | ||||
| } | ||||
|  | ||||
| // UpdateResult is the response from a Update request. Call its Extract method | ||||
| // to interpret it as a Tenant. | ||||
| type UpdateResult struct { | ||||
| 	tenantResult | ||||
| } | ||||
							
								
								
									
										23
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/urls.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/urls.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| package tenants | ||||
|  | ||||
| import "github.com/gophercloud/gophercloud" | ||||
|  | ||||
| func listURL(client *gophercloud.ServiceClient) string { | ||||
| 	return client.ServiceURL("tenants") | ||||
| } | ||||
|  | ||||
| func getURL(client *gophercloud.ServiceClient, tenantID string) string { | ||||
| 	return client.ServiceURL("tenants", tenantID) | ||||
| } | ||||
|  | ||||
| func createURL(client *gophercloud.ServiceClient) string { | ||||
| 	return client.ServiceURL("tenants") | ||||
| } | ||||
|  | ||||
| func deleteURL(client *gophercloud.ServiceClient, tenantID string) string { | ||||
| 	return client.ServiceURL("tenants", tenantID) | ||||
| } | ||||
|  | ||||
| func updateURL(client *gophercloud.ServiceClient, tenantID string) string { | ||||
| 	return client.ServiceURL("tenants", tenantID) | ||||
| } | ||||
							
								
								
									
										46
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| /* | ||||
| Package tokens provides information and interaction with the token API | ||||
| resource for the OpenStack Identity service. | ||||
|  | ||||
| For more information, see: | ||||
| http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2 | ||||
|  | ||||
| Example to Create an Unscoped Token from a Password | ||||
|  | ||||
| 	authOpts := gophercloud.AuthOptions{ | ||||
| 		Username: "user", | ||||
| 		Password: "pass" | ||||
| 	} | ||||
|  | ||||
| 	token, err := tokens.Create(identityClient, authOpts).ExtractToken() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| Example to Create a Token from a Tenant ID and Password | ||||
|  | ||||
| 	authOpts := gophercloud.AuthOptions{ | ||||
| 		Username: "user", | ||||
| 		Password: "password", | ||||
| 		TenantID: "fc394f2ab2df4114bde39905f800dc57" | ||||
| 	} | ||||
|  | ||||
| 	token, err := tokens.Create(identityClient, authOpts).ExtractToken() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| Example to Create a Token from a Tenant Name and Password | ||||
|  | ||||
| 	authOpts := gophercloud.AuthOptions{ | ||||
| 		Username:   "user", | ||||
| 		Password:   "password", | ||||
| 		TenantName: "tenantname" | ||||
| 	} | ||||
|  | ||||
| 	token, err := tokens.Create(identityClient, authOpts).ExtractToken() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| */ | ||||
| package tokens | ||||
							
								
								
									
										103
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| package tokens | ||||
|  | ||||
| import "github.com/gophercloud/gophercloud" | ||||
|  | ||||
| // PasswordCredentialsV2 represents the required options to authenticate | ||||
| // with a username and password. | ||||
| type PasswordCredentialsV2 struct { | ||||
| 	Username string `json:"username" required:"true"` | ||||
| 	Password string `json:"password" required:"true"` | ||||
| } | ||||
|  | ||||
| // TokenCredentialsV2 represents the required options to authenticate | ||||
| // with a token. | ||||
| type TokenCredentialsV2 struct { | ||||
| 	ID string `json:"id,omitempty" required:"true"` | ||||
| } | ||||
|  | ||||
| // AuthOptionsV2 wraps a gophercloud AuthOptions in order to adhere to the | ||||
| // AuthOptionsBuilder interface. | ||||
| type AuthOptionsV2 struct { | ||||
| 	PasswordCredentials *PasswordCredentialsV2 `json:"passwordCredentials,omitempty" xor:"TokenCredentials"` | ||||
|  | ||||
| 	// The TenantID and TenantName fields are optional for the Identity V2 API. | ||||
| 	// Some providers allow you to specify a TenantName instead of the TenantId. | ||||
| 	// Some require both. Your provider's authentication policies will determine | ||||
| 	// how these fields influence authentication. | ||||
| 	TenantID   string `json:"tenantId,omitempty"` | ||||
| 	TenantName string `json:"tenantName,omitempty"` | ||||
|  | ||||
| 	// TokenCredentials allows users to authenticate (possibly as another user) | ||||
| 	// with an authentication token ID. | ||||
| 	TokenCredentials *TokenCredentialsV2 `json:"token,omitempty" xor:"PasswordCredentials"` | ||||
| } | ||||
|  | ||||
| // AuthOptionsBuilder allows extensions to add additional parameters to the | ||||
| // token create request. | ||||
| type AuthOptionsBuilder interface { | ||||
| 	// ToTokenCreateMap assembles the Create request body, returning an error | ||||
| 	// if parameters are missing or inconsistent. | ||||
| 	ToTokenV2CreateMap() (map[string]interface{}, error) | ||||
| } | ||||
|  | ||||
| // AuthOptions are the valid options for Openstack Identity v2 authentication. | ||||
| // For field descriptions, see gophercloud.AuthOptions. | ||||
| type AuthOptions struct { | ||||
| 	IdentityEndpoint string `json:"-"` | ||||
| 	Username         string `json:"username,omitempty"` | ||||
| 	Password         string `json:"password,omitempty"` | ||||
| 	TenantID         string `json:"tenantId,omitempty"` | ||||
| 	TenantName       string `json:"tenantName,omitempty"` | ||||
| 	AllowReauth      bool   `json:"-"` | ||||
| 	TokenID          string | ||||
| } | ||||
|  | ||||
| // ToTokenV2CreateMap builds a token request body from the given AuthOptions. | ||||
| func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { | ||||
| 	v2Opts := AuthOptionsV2{ | ||||
| 		TenantID:   opts.TenantID, | ||||
| 		TenantName: opts.TenantName, | ||||
| 	} | ||||
|  | ||||
| 	if opts.Password != "" { | ||||
| 		v2Opts.PasswordCredentials = &PasswordCredentialsV2{ | ||||
| 			Username: opts.Username, | ||||
| 			Password: opts.Password, | ||||
| 		} | ||||
| 	} else { | ||||
| 		v2Opts.TokenCredentials = &TokenCredentialsV2{ | ||||
| 			ID: opts.TokenID, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	b, err := gophercloud.BuildRequestBody(v2Opts, "auth") | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return b, nil | ||||
| } | ||||
|  | ||||
| // Create authenticates to the identity service and attempts to acquire a Token. | ||||
| // Generally, rather than interact with this call directly, end users should | ||||
| // call openstack.AuthenticatedClient(), which abstracts all of the gory details | ||||
| // about navigating service catalogs and such. | ||||
| func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r CreateResult) { | ||||
| 	b, err := auth.ToTokenV2CreateMap() | ||||
| 	if err != nil { | ||||
| 		r.Err = err | ||||
| 		return | ||||
| 	} | ||||
| 	_, r.Err = client.Post(CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ | ||||
| 		OkCodes:     []int{200, 203}, | ||||
| 		MoreHeaders: map[string]string{"X-Auth-Token": ""}, | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // Get validates and retrieves information for user's token. | ||||
| func Get(client *gophercloud.ServiceClient, token string) (r GetResult) { | ||||
| 	_, r.Err = client.Get(GetURL(client, token), &r.Body, &gophercloud.RequestOpts{ | ||||
| 		OkCodes: []int{200, 203}, | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										159
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,159 @@ | ||||
| package tokens | ||||
|  | ||||
| import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/gophercloud/gophercloud" | ||||
| 	"github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" | ||||
| ) | ||||
|  | ||||
| // Token provides only the most basic information related to an authentication | ||||
| // token. | ||||
| type Token struct { | ||||
| 	// ID provides the primary means of identifying a user to the OpenStack API. | ||||
| 	// OpenStack defines this field as an opaque value, so do not depend on its | ||||
| 	// content. It is safe, however, to compare for equality. | ||||
| 	ID string | ||||
|  | ||||
| 	// ExpiresAt provides a timestamp in ISO 8601 format, indicating when the | ||||
| 	// authentication token becomes invalid. After this point in time, future | ||||
| 	// API requests made using this  authentication token will respond with | ||||
| 	// errors. Either the caller will need to reauthenticate manually, or more | ||||
| 	// preferably, the caller should exploit automatic re-authentication. | ||||
| 	// See the AuthOptions structure for more details. | ||||
| 	ExpiresAt time.Time | ||||
|  | ||||
| 	// Tenant provides information about the tenant to which this token grants | ||||
| 	// access. | ||||
| 	Tenant tenants.Tenant | ||||
| } | ||||
|  | ||||
| // Role is a role for a user. | ||||
| type Role struct { | ||||
| 	Name string `json:"name"` | ||||
| } | ||||
|  | ||||
| // User is an OpenStack user. | ||||
| type User struct { | ||||
| 	ID       string `json:"id"` | ||||
| 	Name     string `json:"name"` | ||||
| 	UserName string `json:"username"` | ||||
| 	Roles    []Role `json:"roles"` | ||||
| } | ||||
|  | ||||
| // Endpoint represents a single API endpoint offered by a service. | ||||
| // It provides the public and internal URLs, if supported, along with a region | ||||
| // specifier, again if provided. | ||||
| // | ||||
| // The significance of the Region field will depend upon your provider. | ||||
| // | ||||
| // In addition, the interface offered by the service will have version | ||||
| // information associated with it through the VersionId, VersionInfo, and | ||||
| // VersionList fields, if provided or supported. | ||||
| // | ||||
| // In all cases, fields which aren't supported by the provider and service | ||||
| // combined will assume a zero-value (""). | ||||
| type Endpoint struct { | ||||
| 	TenantID    string `json:"tenantId"` | ||||
| 	PublicURL   string `json:"publicURL"` | ||||
| 	InternalURL string `json:"internalURL"` | ||||
| 	AdminURL    string `json:"adminURL"` | ||||
| 	Region      string `json:"region"` | ||||
| 	VersionID   string `json:"versionId"` | ||||
| 	VersionInfo string `json:"versionInfo"` | ||||
| 	VersionList string `json:"versionList"` | ||||
| } | ||||
|  | ||||
| // CatalogEntry provides a type-safe interface to an Identity API V2 service | ||||
| // catalog listing. | ||||
| // | ||||
| // Each class of service, such as cloud DNS or block storage services, will have | ||||
| // a single CatalogEntry representing it. | ||||
| // | ||||
| // Note: when looking for the desired service, try, whenever possible, to key | ||||
| // off the type field. Otherwise, you'll tie the representation of the service | ||||
| // to a specific provider. | ||||
| type CatalogEntry struct { | ||||
| 	// Name will contain the provider-specified name for the service. | ||||
| 	Name string `json:"name"` | ||||
|  | ||||
| 	// Type will contain a type string if OpenStack defines a type for the | ||||
| 	// service. Otherwise, for provider-specific services, the provider may assign | ||||
| 	// their own type strings. | ||||
| 	Type string `json:"type"` | ||||
|  | ||||
| 	// Endpoints will let the caller iterate over all the different endpoints that | ||||
| 	// may exist for the service. | ||||
| 	Endpoints []Endpoint `json:"endpoints"` | ||||
| } | ||||
|  | ||||
| // ServiceCatalog provides a view into the service catalog from a previous, | ||||
| // successful authentication. | ||||
| type ServiceCatalog struct { | ||||
| 	Entries []CatalogEntry | ||||
| } | ||||
|  | ||||
| // CreateResult is the response from a Create request. Use ExtractToken() to | ||||
| // interpret it as a Token, or ExtractServiceCatalog() to interpret it as a | ||||
| // service catalog. | ||||
| type CreateResult struct { | ||||
| 	gophercloud.Result | ||||
| } | ||||
|  | ||||
| // GetResult is the deferred response from a Get call, which is the same with a | ||||
| // Created token. Use ExtractUser() to interpret it as a User. | ||||
| type GetResult struct { | ||||
| 	CreateResult | ||||
| } | ||||
|  | ||||
| // ExtractToken returns the just-created Token from a CreateResult. | ||||
| func (r CreateResult) ExtractToken() (*Token, error) { | ||||
| 	var s struct { | ||||
| 		Access struct { | ||||
| 			Token struct { | ||||
| 				Expires string         `json:"expires"` | ||||
| 				ID      string         `json:"id"` | ||||
| 				Tenant  tenants.Tenant `json:"tenant"` | ||||
| 			} `json:"token"` | ||||
| 		} `json:"access"` | ||||
| 	} | ||||
|  | ||||
| 	err := r.ExtractInto(&s) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	expiresTs, err := time.Parse(gophercloud.RFC3339Milli, s.Access.Token.Expires) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return &Token{ | ||||
| 		ID:        s.Access.Token.ID, | ||||
| 		ExpiresAt: expiresTs, | ||||
| 		Tenant:    s.Access.Token.Tenant, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // ExtractServiceCatalog returns the ServiceCatalog that was generated along | ||||
| // with the user's Token. | ||||
| func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) { | ||||
| 	var s struct { | ||||
| 		Access struct { | ||||
| 			Entries []CatalogEntry `json:"serviceCatalog"` | ||||
| 		} `json:"access"` | ||||
| 	} | ||||
| 	err := r.ExtractInto(&s) | ||||
| 	return &ServiceCatalog{Entries: s.Access.Entries}, err | ||||
| } | ||||
|  | ||||
| // ExtractUser returns the User from a GetResult. | ||||
| func (r GetResult) ExtractUser() (*User, error) { | ||||
| 	var s struct { | ||||
| 		Access struct { | ||||
| 			User User `json:"user"` | ||||
| 		} `json:"access"` | ||||
| 	} | ||||
| 	err := r.ExtractInto(&s) | ||||
| 	return &s.Access.User, err | ||||
| } | ||||
							
								
								
									
										13
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/urls.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/urls.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| package tokens | ||||
|  | ||||
| import "github.com/gophercloud/gophercloud" | ||||
|  | ||||
| // CreateURL generates the URL used to create new Tokens. | ||||
| func CreateURL(client *gophercloud.ServiceClient) string { | ||||
| 	return client.ServiceURL("tokens") | ||||
| } | ||||
|  | ||||
| // GetURL generates the URL used to Validate Tokens. | ||||
| func GetURL(client *gophercloud.ServiceClient, token string) string { | ||||
| 	return client.ServiceURL("tokens", token) | ||||
| } | ||||
							
								
								
									
										108
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| /* | ||||
| Package tokens provides information and interaction with the token API | ||||
| resource for the OpenStack Identity service. | ||||
|  | ||||
| For more information, see: | ||||
| http://developer.openstack.org/api-ref-identity-v3.html#tokens-v3 | ||||
|  | ||||
| Example to Create a Token From a Username and Password | ||||
|  | ||||
| 	authOptions := tokens.AuthOptions{ | ||||
| 		UserID:   "username", | ||||
| 		Password: "password", | ||||
| 	} | ||||
|  | ||||
| 	token, err := tokens.Create(identityClient, authOptions).ExtractToken() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| Example to Create a Token From a Username, Password, and Domain | ||||
|  | ||||
| 	authOptions := tokens.AuthOptions{ | ||||
| 		UserID:   "username", | ||||
| 		Password: "password", | ||||
| 		DomainID: "default", | ||||
| 	} | ||||
|  | ||||
| 	token, err := tokens.Create(identityClient, authOptions).ExtractToken() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| 	authOptions = tokens.AuthOptions{ | ||||
| 		UserID:     "username", | ||||
| 		Password:   "password", | ||||
| 		DomainName: "default", | ||||
| 	} | ||||
|  | ||||
| 	token, err = tokens.Create(identityClient, authOptions).ExtractToken() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| Example to Create a Token From a Token | ||||
|  | ||||
| 	authOptions := tokens.AuthOptions{ | ||||
| 		TokenID: "token_id", | ||||
| 	} | ||||
|  | ||||
| 	token, err := tokens.Create(identityClient, authOptions).ExtractToken() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| Example to Create a Token from a Username and Password with Project ID Scope | ||||
|  | ||||
| 	scope := tokens.Scope{ | ||||
| 		ProjectID: "0fe36e73809d46aeae6705c39077b1b3", | ||||
| 	} | ||||
|  | ||||
| 	authOptions := tokens.AuthOptions{ | ||||
| 		Scope:    &scope, | ||||
| 		UserID:   "username", | ||||
| 		Password: "password", | ||||
| 	} | ||||
|  | ||||
| 	token, err = tokens.Create(identityClient, authOptions).ExtractToken() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| Example to Create a Token from a Username and Password with Domain ID Scope | ||||
|  | ||||
| 	scope := tokens.Scope{ | ||||
| 		DomainID: "default", | ||||
| 	} | ||||
|  | ||||
| 	authOptions := tokens.AuthOptions{ | ||||
| 		Scope:    &scope, | ||||
| 		UserID:   "username", | ||||
| 		Password: "password", | ||||
| 	} | ||||
|  | ||||
| 	token, err = tokens.Create(identityClient, authOptions).ExtractToken() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| Example to Create a Token from a Username and Password with Project Name Scope | ||||
|  | ||||
| 	scope := tokens.Scope{ | ||||
| 		ProjectName: "project_name", | ||||
| 		DomainID:    "default", | ||||
| 	} | ||||
|  | ||||
| 	authOptions := tokens.AuthOptions{ | ||||
| 		Scope:    &scope, | ||||
| 		UserID:   "username", | ||||
| 		Password: "password", | ||||
| 	} | ||||
|  | ||||
| 	token, err = tokens.Create(identityClient, authOptions).ExtractToken() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| */ | ||||
| package tokens | ||||
							
								
								
									
										162
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,162 @@ | ||||
| package tokens | ||||
|  | ||||
| import "github.com/gophercloud/gophercloud" | ||||
|  | ||||
| // Scope allows a created token to be limited to a specific domain or project. | ||||
| type Scope struct { | ||||
| 	ProjectID   string | ||||
| 	ProjectName string | ||||
| 	DomainID    string | ||||
| 	DomainName  string | ||||
| } | ||||
|  | ||||
| // AuthOptionsBuilder provides the ability for extensions to add additional | ||||
| // parameters to AuthOptions. Extensions must satisfy all required methods. | ||||
| type AuthOptionsBuilder interface { | ||||
| 	// ToTokenV3CreateMap assembles the Create request body, returning an error | ||||
| 	// if parameters are missing or inconsistent. | ||||
| 	ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) | ||||
| 	ToTokenV3ScopeMap() (map[string]interface{}, error) | ||||
| 	CanReauth() bool | ||||
| } | ||||
|  | ||||
| // AuthOptions represents options for authenticating a user. | ||||
| type AuthOptions struct { | ||||
| 	// IdentityEndpoint specifies the HTTP endpoint that is required to work with | ||||
| 	// the Identity API of the appropriate version. While it's ultimately needed | ||||
| 	// by all of the identity services, it will often be populated by a | ||||
| 	// provider-level function. | ||||
| 	IdentityEndpoint string `json:"-"` | ||||
|  | ||||
| 	// Username is required if using Identity V2 API. Consult with your provider's | ||||
| 	// control panel to discover your account's username. In Identity V3, either | ||||
| 	// UserID or a combination of Username and DomainID or DomainName are needed. | ||||
| 	Username string `json:"username,omitempty"` | ||||
| 	UserID   string `json:"id,omitempty"` | ||||
|  | ||||
| 	Password string `json:"password,omitempty"` | ||||
|  | ||||
| 	// At most one of DomainID and DomainName must be provided if using Username | ||||
| 	// with Identity V3. Otherwise, either are optional. | ||||
| 	DomainID   string `json:"-"` | ||||
| 	DomainName string `json:"name,omitempty"` | ||||
|  | ||||
| 	// AllowReauth should be set to true if you grant permission for Gophercloud | ||||
| 	// to cache your credentials in memory, and to allow Gophercloud to attempt | ||||
| 	// to re-authenticate automatically if/when your token expires.  If you set | ||||
| 	// it to false, it will not cache these settings, but re-authentication will | ||||
| 	// not be possible.  This setting defaults to false. | ||||
| 	AllowReauth bool `json:"-"` | ||||
|  | ||||
| 	// TokenID allows users to authenticate (possibly as another user) with an | ||||
| 	// authentication token ID. | ||||
| 	TokenID string `json:"-"` | ||||
|  | ||||
| 	// Authentication through Application Credentials requires supplying name, project and secret | ||||
| 	// For project we can use TenantID | ||||
| 	ApplicationCredentialID     string `json:"-"` | ||||
| 	ApplicationCredentialName   string `json:"-"` | ||||
| 	ApplicationCredentialSecret string `json:"-"` | ||||
|  | ||||
| 	Scope Scope `json:"-"` | ||||
| } | ||||
|  | ||||
| // ToTokenV3CreateMap builds a request body from AuthOptions. | ||||
| func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { | ||||
| 	gophercloudAuthOpts := gophercloud.AuthOptions{ | ||||
| 		Username:                    opts.Username, | ||||
| 		UserID:                      opts.UserID, | ||||
| 		Password:                    opts.Password, | ||||
| 		DomainID:                    opts.DomainID, | ||||
| 		DomainName:                  opts.DomainName, | ||||
| 		AllowReauth:                 opts.AllowReauth, | ||||
| 		TokenID:                     opts.TokenID, | ||||
| 		ApplicationCredentialID:     opts.ApplicationCredentialID, | ||||
| 		ApplicationCredentialName:   opts.ApplicationCredentialName, | ||||
| 		ApplicationCredentialSecret: opts.ApplicationCredentialSecret, | ||||
| 	} | ||||
|  | ||||
| 	return gophercloudAuthOpts.ToTokenV3CreateMap(scope) | ||||
| } | ||||
|  | ||||
| // ToTokenV3CreateMap builds a scope request body from AuthOptions. | ||||
| func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { | ||||
| 	scope := gophercloud.AuthScope(opts.Scope) | ||||
|  | ||||
| 	gophercloudAuthOpts := gophercloud.AuthOptions{ | ||||
| 		Scope:      &scope, | ||||
| 		DomainID:   opts.DomainID, | ||||
| 		DomainName: opts.DomainName, | ||||
| 	} | ||||
|  | ||||
| 	return gophercloudAuthOpts.ToTokenV3ScopeMap() | ||||
| } | ||||
|  | ||||
| func (opts *AuthOptions) CanReauth() bool { | ||||
| 	return opts.AllowReauth | ||||
| } | ||||
|  | ||||
| func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string { | ||||
| 	return map[string]string{ | ||||
| 		"X-Subject-Token": subjectToken, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Create authenticates and either generates a new token, or changes the Scope | ||||
| // of an existing token. | ||||
| func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) { | ||||
| 	scope, err := opts.ToTokenV3ScopeMap() | ||||
| 	if err != nil { | ||||
| 		r.Err = err | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	b, err := opts.ToTokenV3CreateMap(scope) | ||||
| 	if err != nil { | ||||
| 		r.Err = err | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	resp, err := c.Post(tokenURL(c), b, &r.Body, &gophercloud.RequestOpts{ | ||||
| 		MoreHeaders: map[string]string{"X-Auth-Token": ""}, | ||||
| 	}) | ||||
| 	r.Err = err | ||||
| 	if resp != nil { | ||||
| 		r.Header = resp.Header | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // Get validates and retrieves information about another token. | ||||
| func Get(c *gophercloud.ServiceClient, token string) (r GetResult) { | ||||
| 	resp, err := c.Get(tokenURL(c), &r.Body, &gophercloud.RequestOpts{ | ||||
| 		MoreHeaders: subjectTokenHeaders(c, token), | ||||
| 		OkCodes:     []int{200, 203}, | ||||
| 	}) | ||||
| 	if resp != nil { | ||||
| 		r.Err = err | ||||
| 		r.Header = resp.Header | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // Validate determines if a specified token is valid or not. | ||||
| func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { | ||||
| 	resp, err := c.Head(tokenURL(c), &gophercloud.RequestOpts{ | ||||
| 		MoreHeaders: subjectTokenHeaders(c, token), | ||||
| 		OkCodes:     []int{200, 204, 404}, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return false, err | ||||
| 	} | ||||
|  | ||||
| 	return resp.StatusCode == 200 || resp.StatusCode == 204, nil | ||||
| } | ||||
|  | ||||
| // Revoke immediately makes specified token invalid. | ||||
| func Revoke(c *gophercloud.ServiceClient, token string) (r RevokeResult) { | ||||
| 	_, r.Err = c.Delete(tokenURL(c), &gophercloud.RequestOpts{ | ||||
| 		MoreHeaders: subjectTokenHeaders(c, token), | ||||
| 	}) | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										171
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,171 @@ | ||||
| package tokens | ||||
|  | ||||
| import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/gophercloud/gophercloud" | ||||
| ) | ||||
|  | ||||
| // Endpoint represents a single API endpoint offered by a service. | ||||
| // It matches either a public, internal or admin URL. | ||||
| // If supported, it contains a region specifier, again if provided. | ||||
| // The significance of the Region field will depend upon your provider. | ||||
| type Endpoint struct { | ||||
| 	ID        string `json:"id"` | ||||
| 	Region    string `json:"region"` | ||||
| 	RegionID  string `json:"region_id"` | ||||
| 	Interface string `json:"interface"` | ||||
| 	URL       string `json:"url"` | ||||
| } | ||||
|  | ||||
| // CatalogEntry provides a type-safe interface to an Identity API V3 service | ||||
| // catalog listing. Each class of service, such as cloud DNS or block storage | ||||
| // services, could have multiple CatalogEntry representing it (one by interface | ||||
| // type, e.g public, admin or internal). | ||||
| // | ||||
| // Note: when looking for the desired service, try, whenever possible, to key | ||||
| // off the type field. Otherwise, you'll tie the representation of the service | ||||
| // to a specific provider. | ||||
| type CatalogEntry struct { | ||||
| 	// Service ID | ||||
| 	ID string `json:"id"` | ||||
|  | ||||
| 	// Name will contain the provider-specified name for the service. | ||||
| 	Name string `json:"name"` | ||||
|  | ||||
| 	// Type will contain a type string if OpenStack defines a type for the | ||||
| 	// service. Otherwise, for provider-specific services, the provider may | ||||
| 	// assign their own type strings. | ||||
| 	Type string `json:"type"` | ||||
|  | ||||
| 	// Endpoints will let the caller iterate over all the different endpoints that | ||||
| 	// may exist for the service. | ||||
| 	Endpoints []Endpoint `json:"endpoints"` | ||||
| } | ||||
|  | ||||
| // ServiceCatalog provides a view into the service catalog from a previous, | ||||
| // successful authentication. | ||||
| type ServiceCatalog struct { | ||||
| 	Entries []CatalogEntry `json:"catalog"` | ||||
| } | ||||
|  | ||||
| // Domain provides information about the domain to which this token grants | ||||
| // access. | ||||
| type Domain struct { | ||||
| 	ID   string `json:"id"` | ||||
| 	Name string `json:"name"` | ||||
| } | ||||
|  | ||||
| // User represents a user resource that exists in the Identity Service. | ||||
| type User struct { | ||||
| 	Domain Domain `json:"domain"` | ||||
| 	ID     string `json:"id"` | ||||
| 	Name   string `json:"name"` | ||||
| } | ||||
|  | ||||
| // Role provides information about roles to which User is authorized. | ||||
| type Role struct { | ||||
| 	ID   string `json:"id"` | ||||
| 	Name string `json:"name"` | ||||
| } | ||||
|  | ||||
| // Project provides information about project to which User is authorized. | ||||
| type Project struct { | ||||
| 	Domain Domain `json:"domain"` | ||||
| 	ID     string `json:"id"` | ||||
| 	Name   string `json:"name"` | ||||
| } | ||||
|  | ||||
| // commonResult is the response from a request. A commonResult has various | ||||
| // methods which can be used to extract different details about the result. | ||||
| type commonResult struct { | ||||
| 	gophercloud.Result | ||||
| } | ||||
|  | ||||
| // Extract is a shortcut for ExtractToken. | ||||
| // This function is deprecated and still present for backward compatibility. | ||||
| func (r commonResult) Extract() (*Token, error) { | ||||
| 	return r.ExtractToken() | ||||
| } | ||||
|  | ||||
| // ExtractToken interprets a commonResult as a Token. | ||||
| func (r commonResult) ExtractToken() (*Token, error) { | ||||
| 	var s Token | ||||
| 	err := r.ExtractInto(&s) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// Parse the token itself from the stored headers. | ||||
| 	s.ID = r.Header.Get("X-Subject-Token") | ||||
|  | ||||
| 	return &s, err | ||||
| } | ||||
|  | ||||
| // ExtractServiceCatalog returns the ServiceCatalog that was generated along | ||||
| // with the user's Token. | ||||
| func (r commonResult) ExtractServiceCatalog() (*ServiceCatalog, error) { | ||||
| 	var s ServiceCatalog | ||||
| 	err := r.ExtractInto(&s) | ||||
| 	return &s, err | ||||
| } | ||||
|  | ||||
| // ExtractUser returns the User that is the owner of the Token. | ||||
| func (r commonResult) ExtractUser() (*User, error) { | ||||
| 	var s struct { | ||||
| 		User *User `json:"user"` | ||||
| 	} | ||||
| 	err := r.ExtractInto(&s) | ||||
| 	return s.User, err | ||||
| } | ||||
|  | ||||
| // ExtractRoles returns Roles to which User is authorized. | ||||
| func (r commonResult) ExtractRoles() ([]Role, error) { | ||||
| 	var s struct { | ||||
| 		Roles []Role `json:"roles"` | ||||
| 	} | ||||
| 	err := r.ExtractInto(&s) | ||||
| 	return s.Roles, err | ||||
| } | ||||
|  | ||||
| // ExtractProject returns Project to which User is authorized. | ||||
| func (r commonResult) ExtractProject() (*Project, error) { | ||||
| 	var s struct { | ||||
| 		Project *Project `json:"project"` | ||||
| 	} | ||||
| 	err := r.ExtractInto(&s) | ||||
| 	return s.Project, err | ||||
| } | ||||
|  | ||||
| // CreateResult is the response from a Create request. Use ExtractToken() | ||||
| // to interpret it as a Token, or ExtractServiceCatalog() to interpret it | ||||
| // as a service catalog. | ||||
| type CreateResult struct { | ||||
| 	commonResult | ||||
| } | ||||
|  | ||||
| // GetResult is the response from a Get request. Use ExtractToken() | ||||
| // to interpret it as a Token, or ExtractServiceCatalog() to interpret it | ||||
| // as a service catalog. | ||||
| type GetResult struct { | ||||
| 	commonResult | ||||
| } | ||||
|  | ||||
| // RevokeResult is response from a Revoke request. | ||||
| type RevokeResult struct { | ||||
| 	commonResult | ||||
| } | ||||
|  | ||||
| // Token is a string that grants a user access to a controlled set of services | ||||
| // in an OpenStack provider. Each Token is valid for a set length of time. | ||||
| type Token struct { | ||||
| 	// ID is the issued token. | ||||
| 	ID string `json:"id"` | ||||
|  | ||||
| 	// ExpiresAt is the timestamp at which this token will no longer be accepted. | ||||
| 	ExpiresAt time.Time `json:"expires_at"` | ||||
| } | ||||
|  | ||||
| func (r commonResult) ExtractInto(v interface{}) error { | ||||
| 	return r.ExtractIntoStructPtr(v, "token") | ||||
| } | ||||
							
								
								
									
										7
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/urls.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/urls.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| package tokens | ||||
|  | ||||
| import "github.com/gophercloud/gophercloud" | ||||
|  | ||||
| func tokenURL(c *gophercloud.ServiceClient) string { | ||||
| 	return c.ServiceURL("auth", "tokens") | ||||
| } | ||||
							
								
								
									
										28
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/utils/base_endpoint.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/utils/base_endpoint.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| package utils | ||||
|  | ||||
| import ( | ||||
| 	"net/url" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| // BaseEndpoint will return a URL without the /vX.Y | ||||
| // portion of the URL. | ||||
| func BaseEndpoint(endpoint string) (string, error) { | ||||
| 	u, err := url.Parse(endpoint) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	u.RawQuery, u.Fragment = "", "" | ||||
|  | ||||
| 	path := u.Path | ||||
| 	versionRe := regexp.MustCompile("v[0-9.]+/?") | ||||
|  | ||||
| 	if version := versionRe.FindString(path); version != "" { | ||||
| 		versionIndex := strings.Index(path, version) | ||||
| 		u.Path = path[:versionIndex] | ||||
| 	} | ||||
|  | ||||
| 	return u.String(), nil | ||||
| } | ||||
							
								
								
									
										111
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/utils/choose_version.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								vendor/github.com/gophercloud/gophercloud/openstack/utils/choose_version.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| package utils | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/gophercloud/gophercloud" | ||||
| ) | ||||
|  | ||||
| // Version is a supported API version, corresponding to a vN package within the appropriate service. | ||||
| type Version struct { | ||||
| 	ID       string | ||||
| 	Suffix   string | ||||
| 	Priority int | ||||
| } | ||||
|  | ||||
| var goodStatus = map[string]bool{ | ||||
| 	"current":   true, | ||||
| 	"supported": true, | ||||
| 	"stable":    true, | ||||
| } | ||||
|  | ||||
| // ChooseVersion queries the base endpoint of an API to choose the most recent non-experimental alternative from a service's | ||||
| // published versions. | ||||
| // It returns the highest-Priority Version among the alternatives that are provided, as well as its corresponding endpoint. | ||||
| func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (*Version, string, error) { | ||||
| 	type linkResp struct { | ||||
| 		Href string `json:"href"` | ||||
| 		Rel  string `json:"rel"` | ||||
| 	} | ||||
|  | ||||
| 	type valueResp struct { | ||||
| 		ID     string     `json:"id"` | ||||
| 		Status string     `json:"status"` | ||||
| 		Links  []linkResp `json:"links"` | ||||
| 	} | ||||
|  | ||||
| 	type versionsResp struct { | ||||
| 		Values []valueResp `json:"values"` | ||||
| 	} | ||||
|  | ||||
| 	type response struct { | ||||
| 		Versions versionsResp `json:"versions"` | ||||
| 	} | ||||
|  | ||||
| 	normalize := func(endpoint string) string { | ||||
| 		if !strings.HasSuffix(endpoint, "/") { | ||||
| 			return endpoint + "/" | ||||
| 		} | ||||
| 		return endpoint | ||||
| 	} | ||||
| 	identityEndpoint := normalize(client.IdentityEndpoint) | ||||
|  | ||||
| 	// If a full endpoint is specified, check version suffixes for a match first. | ||||
| 	for _, v := range recognized { | ||||
| 		if strings.HasSuffix(identityEndpoint, v.Suffix) { | ||||
| 			return v, identityEndpoint, nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	var resp response | ||||
| 	_, err := client.Request("GET", client.IdentityBase, &gophercloud.RequestOpts{ | ||||
| 		JSONResponse: &resp, | ||||
| 		OkCodes:      []int{200, 300}, | ||||
| 	}) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return nil, "", err | ||||
| 	} | ||||
|  | ||||
| 	var highest *Version | ||||
| 	var endpoint string | ||||
|  | ||||
| 	for _, value := range resp.Versions.Values { | ||||
| 		href := "" | ||||
| 		for _, link := range value.Links { | ||||
| 			if link.Rel == "self" { | ||||
| 				href = normalize(link.Href) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		for _, version := range recognized { | ||||
| 			if strings.Contains(value.ID, version.ID) { | ||||
| 				// Prefer a version that exactly matches the provided endpoint. | ||||
| 				if href == identityEndpoint { | ||||
| 					if href == "" { | ||||
| 						return nil, "", fmt.Errorf("Endpoint missing in version %s response from %s", value.ID, client.IdentityBase) | ||||
| 					} | ||||
| 					return version, href, nil | ||||
| 				} | ||||
|  | ||||
| 				// Otherwise, find the highest-priority version with a whitelisted status. | ||||
| 				if goodStatus[strings.ToLower(value.Status)] { | ||||
| 					if highest == nil || version.Priority > highest.Priority { | ||||
| 						highest = version | ||||
| 						endpoint = href | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if highest == nil { | ||||
| 		return nil, "", fmt.Errorf("No supported version available from endpoint %s", client.IdentityBase) | ||||
| 	} | ||||
| 	if endpoint == "" { | ||||
| 		return nil, "", fmt.Errorf("Endpoint missing in version %s response from %s", highest.ID, client.IdentityBase) | ||||
| 	} | ||||
|  | ||||
| 	return highest, endpoint, nil | ||||
| } | ||||
							
								
								
									
										60
									
								
								vendor/github.com/gophercloud/gophercloud/pagination/http.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								vendor/github.com/gophercloud/gophercloud/pagination/http.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| package pagination | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/gophercloud/gophercloud" | ||||
| ) | ||||
|  | ||||
| // PageResult stores the HTTP response that returned the current page of results. | ||||
| type PageResult struct { | ||||
| 	gophercloud.Result | ||||
| 	url.URL | ||||
| } | ||||
|  | ||||
| // PageResultFrom parses an HTTP response as JSON and returns a PageResult containing the | ||||
| // results, interpreting it as JSON if the content type indicates. | ||||
| func PageResultFrom(resp *http.Response) (PageResult, error) { | ||||
| 	var parsedBody interface{} | ||||
|  | ||||
| 	defer resp.Body.Close() | ||||
| 	rawBody, err := ioutil.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		return PageResult{}, err | ||||
| 	} | ||||
|  | ||||
| 	if strings.HasPrefix(resp.Header.Get("Content-Type"), "application/json") { | ||||
| 		err = json.Unmarshal(rawBody, &parsedBody) | ||||
| 		if err != nil { | ||||
| 			return PageResult{}, err | ||||
| 		} | ||||
| 	} else { | ||||
| 		parsedBody = rawBody | ||||
| 	} | ||||
|  | ||||
| 	return PageResultFromParsed(resp, parsedBody), err | ||||
| } | ||||
|  | ||||
| // PageResultFromParsed constructs a PageResult from an HTTP response that has already had its | ||||
| // body parsed as JSON (and closed). | ||||
| func PageResultFromParsed(resp *http.Response, body interface{}) PageResult { | ||||
| 	return PageResult{ | ||||
| 		Result: gophercloud.Result{ | ||||
| 			Body:   body, | ||||
| 			Header: resp.Header, | ||||
| 		}, | ||||
| 		URL: *resp.Request.URL, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Request performs an HTTP request and extracts the http.Response from the result. | ||||
| func Request(client *gophercloud.ServiceClient, headers map[string]string, url string) (*http.Response, error) { | ||||
| 	return client.Get(url, nil, &gophercloud.RequestOpts{ | ||||
| 		MoreHeaders: headers, | ||||
| 		OkCodes:     []int{200, 204, 300}, | ||||
| 	}) | ||||
| } | ||||
							
								
								
									
										92
									
								
								vendor/github.com/gophercloud/gophercloud/pagination/linked.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								vendor/github.com/gophercloud/gophercloud/pagination/linked.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| package pagination | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
|  | ||||
| 	"github.com/gophercloud/gophercloud" | ||||
| ) | ||||
|  | ||||
| // LinkedPageBase may be embedded to implement a page that provides navigational "Next" and "Previous" links within its result. | ||||
| type LinkedPageBase struct { | ||||
| 	PageResult | ||||
|  | ||||
| 	// LinkPath lists the keys that should be traversed within a response to arrive at the "next" pointer. | ||||
| 	// If any link along the path is missing, an empty URL will be returned. | ||||
| 	// If any link results in an unexpected value type, an error will be returned. | ||||
| 	// When left as "nil", []string{"links", "next"} will be used as a default. | ||||
| 	LinkPath []string | ||||
| } | ||||
|  | ||||
| // NextPageURL extracts the pagination structure from a JSON response and returns the "next" link, if one is present. | ||||
| // It assumes that the links are available in a "links" element of the top-level response object. | ||||
| // If this is not the case, override NextPageURL on your result type. | ||||
| func (current LinkedPageBase) NextPageURL() (string, error) { | ||||
| 	var path []string | ||||
| 	var key string | ||||
|  | ||||
| 	if current.LinkPath == nil { | ||||
| 		path = []string{"links", "next"} | ||||
| 	} else { | ||||
| 		path = current.LinkPath | ||||
| 	} | ||||
|  | ||||
| 	submap, ok := current.Body.(map[string]interface{}) | ||||
| 	if !ok { | ||||
| 		err := gophercloud.ErrUnexpectedType{} | ||||
| 		err.Expected = "map[string]interface{}" | ||||
| 		err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	for { | ||||
| 		key, path = path[0], path[1:len(path)] | ||||
|  | ||||
| 		value, ok := submap[key] | ||||
| 		if !ok { | ||||
| 			return "", nil | ||||
| 		} | ||||
|  | ||||
| 		if len(path) > 0 { | ||||
| 			submap, ok = value.(map[string]interface{}) | ||||
| 			if !ok { | ||||
| 				err := gophercloud.ErrUnexpectedType{} | ||||
| 				err.Expected = "map[string]interface{}" | ||||
| 				err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value)) | ||||
| 				return "", err | ||||
| 			} | ||||
| 		} else { | ||||
| 			if value == nil { | ||||
| 				// Actual null element. | ||||
| 				return "", nil | ||||
| 			} | ||||
|  | ||||
| 			url, ok := value.(string) | ||||
| 			if !ok { | ||||
| 				err := gophercloud.ErrUnexpectedType{} | ||||
| 				err.Expected = "string" | ||||
| 				err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value)) | ||||
| 				return "", err | ||||
| 			} | ||||
|  | ||||
| 			return url, nil | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // IsEmpty satisifies the IsEmpty method of the Page interface | ||||
| func (current LinkedPageBase) IsEmpty() (bool, error) { | ||||
| 	if b, ok := current.Body.([]interface{}); ok { | ||||
| 		return len(b) == 0, nil | ||||
| 	} | ||||
| 	err := gophercloud.ErrUnexpectedType{} | ||||
| 	err.Expected = "[]interface{}" | ||||
| 	err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) | ||||
| 	return true, err | ||||
| } | ||||
|  | ||||
| // GetBody returns the linked page's body. This method is needed to satisfy the | ||||
| // Page interface. | ||||
| func (current LinkedPageBase) GetBody() interface{} { | ||||
| 	return current.Body | ||||
| } | ||||
							
								
								
									
										58
									
								
								vendor/github.com/gophercloud/gophercloud/pagination/marker.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								vendor/github.com/gophercloud/gophercloud/pagination/marker.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| package pagination | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
|  | ||||
| 	"github.com/gophercloud/gophercloud" | ||||
| ) | ||||
|  | ||||
| // MarkerPage is a stricter Page interface that describes additional functionality required for use with NewMarkerPager. | ||||
| // For convenience, embed the MarkedPageBase struct. | ||||
| type MarkerPage interface { | ||||
| 	Page | ||||
|  | ||||
| 	// LastMarker returns the last "marker" value on this page. | ||||
| 	LastMarker() (string, error) | ||||
| } | ||||
|  | ||||
| // MarkerPageBase is a page in a collection that's paginated by "limit" and "marker" query parameters. | ||||
| type MarkerPageBase struct { | ||||
| 	PageResult | ||||
|  | ||||
| 	// Owner is a reference to the embedding struct. | ||||
| 	Owner MarkerPage | ||||
| } | ||||
|  | ||||
| // NextPageURL generates the URL for the page of results after this one. | ||||
| func (current MarkerPageBase) NextPageURL() (string, error) { | ||||
| 	currentURL := current.URL | ||||
|  | ||||
| 	mark, err := current.Owner.LastMarker() | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	q := currentURL.Query() | ||||
| 	q.Set("marker", mark) | ||||
| 	currentURL.RawQuery = q.Encode() | ||||
|  | ||||
| 	return currentURL.String(), nil | ||||
| } | ||||
|  | ||||
| // IsEmpty satisifies the IsEmpty method of the Page interface | ||||
| func (current MarkerPageBase) IsEmpty() (bool, error) { | ||||
| 	if b, ok := current.Body.([]interface{}); ok { | ||||
| 		return len(b) == 0, nil | ||||
| 	} | ||||
| 	err := gophercloud.ErrUnexpectedType{} | ||||
| 	err.Expected = "[]interface{}" | ||||
| 	err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) | ||||
| 	return true, err | ||||
| } | ||||
|  | ||||
| // GetBody returns the linked page's body. This method is needed to satisfy the | ||||
| // Page interface. | ||||
| func (current MarkerPageBase) GetBody() interface{} { | ||||
| 	return current.Body | ||||
| } | ||||
							
								
								
									
										251
									
								
								vendor/github.com/gophercloud/gophercloud/pagination/pager.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								vendor/github.com/gophercloud/gophercloud/pagination/pager.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,251 @@ | ||||
| package pagination | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"reflect" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/gophercloud/gophercloud" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	// ErrPageNotAvailable is returned from a Pager when a next or previous page is requested, but does not exist. | ||||
| 	ErrPageNotAvailable = errors.New("The requested page does not exist.") | ||||
| ) | ||||
|  | ||||
| // Page must be satisfied by the result type of any resource collection. | ||||
| // It allows clients to interact with the resource uniformly, regardless of whether or not or how it's paginated. | ||||
| // Generally, rather than implementing this interface directly, implementors should embed one of the concrete PageBase structs, | ||||
| // instead. | ||||
| // Depending on the pagination strategy of a particular resource, there may be an additional subinterface that the result type | ||||
| // will need to implement. | ||||
| type Page interface { | ||||
| 	// NextPageURL generates the URL for the page of data that follows this collection. | ||||
| 	// Return "" if no such page exists. | ||||
| 	NextPageURL() (string, error) | ||||
|  | ||||
| 	// IsEmpty returns true if this Page has no items in it. | ||||
| 	IsEmpty() (bool, error) | ||||
|  | ||||
| 	// GetBody returns the Page Body. This is used in the `AllPages` method. | ||||
| 	GetBody() interface{} | ||||
| } | ||||
|  | ||||
| // Pager knows how to advance through a specific resource collection, one page at a time. | ||||
| type Pager struct { | ||||
| 	client *gophercloud.ServiceClient | ||||
|  | ||||
| 	initialURL string | ||||
|  | ||||
| 	createPage func(r PageResult) Page | ||||
|  | ||||
| 	firstPage Page | ||||
|  | ||||
| 	Err error | ||||
|  | ||||
| 	// Headers supplies additional HTTP headers to populate on each paged request. | ||||
| 	Headers map[string]string | ||||
| } | ||||
|  | ||||
| // NewPager constructs a manually-configured pager. | ||||
| // Supply the URL for the first page, a function that requests a specific page given a URL, and a function that counts a page. | ||||
| func NewPager(client *gophercloud.ServiceClient, initialURL string, createPage func(r PageResult) Page) Pager { | ||||
| 	return Pager{ | ||||
| 		client:     client, | ||||
| 		initialURL: initialURL, | ||||
| 		createPage: createPage, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithPageCreator returns a new Pager that substitutes a different page creation function. This is | ||||
| // useful for overriding List functions in delegation. | ||||
| func (p Pager) WithPageCreator(createPage func(r PageResult) Page) Pager { | ||||
| 	return Pager{ | ||||
| 		client:     p.client, | ||||
| 		initialURL: p.initialURL, | ||||
| 		createPage: createPage, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (p Pager) fetchNextPage(url string) (Page, error) { | ||||
| 	resp, err := Request(p.client, p.Headers, url) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	remembered, err := PageResultFrom(resp) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return p.createPage(remembered), nil | ||||
| } | ||||
|  | ||||
| // EachPage iterates over each page returned by a Pager, yielding one at a time to a handler function. | ||||
| // Return "false" from the handler to prematurely stop iterating. | ||||
| func (p Pager) EachPage(handler func(Page) (bool, error)) error { | ||||
| 	if p.Err != nil { | ||||
| 		return p.Err | ||||
| 	} | ||||
| 	currentURL := p.initialURL | ||||
| 	for { | ||||
| 		var currentPage Page | ||||
|  | ||||
| 		// if first page has already been fetched, no need to fetch it again | ||||
| 		if p.firstPage != nil { | ||||
| 			currentPage = p.firstPage | ||||
| 			p.firstPage = nil | ||||
| 		} else { | ||||
| 			var err error | ||||
| 			currentPage, err = p.fetchNextPage(currentURL) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		empty, err := currentPage.IsEmpty() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if empty { | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		ok, err := handler(currentPage) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if !ok { | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		currentURL, err = currentPage.NextPageURL() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if currentURL == "" { | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // AllPages returns all the pages from a `List` operation in a single page, | ||||
| // allowing the user to retrieve all the pages at once. | ||||
| func (p Pager) AllPages() (Page, error) { | ||||
| 	// pagesSlice holds all the pages until they get converted into as Page Body. | ||||
| 	var pagesSlice []interface{} | ||||
| 	// body will contain the final concatenated Page body. | ||||
| 	var body reflect.Value | ||||
|  | ||||
| 	// Grab a first page to ascertain the page body type. | ||||
| 	firstPage, err := p.fetchNextPage(p.initialURL) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	// Store the page type so we can use reflection to create a new mega-page of | ||||
| 	// that type. | ||||
| 	pageType := reflect.TypeOf(firstPage) | ||||
|  | ||||
| 	// if it's a single page, just return the firstPage (first page) | ||||
| 	if _, found := pageType.FieldByName("SinglePageBase"); found { | ||||
| 		return firstPage, nil | ||||
| 	} | ||||
|  | ||||
| 	// store the first page to avoid getting it twice | ||||
| 	p.firstPage = firstPage | ||||
|  | ||||
| 	// Switch on the page body type. Recognized types are `map[string]interface{}`, | ||||
| 	// `[]byte`, and `[]interface{}`. | ||||
| 	switch pb := firstPage.GetBody().(type) { | ||||
| 	case map[string]interface{}: | ||||
| 		// key is the map key for the page body if the body type is `map[string]interface{}`. | ||||
| 		var key string | ||||
| 		// Iterate over the pages to concatenate the bodies. | ||||
| 		err = p.EachPage(func(page Page) (bool, error) { | ||||
| 			b := page.GetBody().(map[string]interface{}) | ||||
| 			for k, v := range b { | ||||
| 				// If it's a linked page, we don't want the `links`, we want the other one. | ||||
| 				if !strings.HasSuffix(k, "links") { | ||||
| 					// check the field's type. we only want []interface{} (which is really []map[string]interface{}) | ||||
| 					switch vt := v.(type) { | ||||
| 					case []interface{}: | ||||
| 						key = k | ||||
| 						pagesSlice = append(pagesSlice, vt...) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			return true, nil | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		// Set body to value of type `map[string]interface{}` | ||||
| 		body = reflect.MakeMap(reflect.MapOf(reflect.TypeOf(key), reflect.TypeOf(pagesSlice))) | ||||
| 		body.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(pagesSlice)) | ||||
| 	case []byte: | ||||
| 		// Iterate over the pages to concatenate the bodies. | ||||
| 		err = p.EachPage(func(page Page) (bool, error) { | ||||
| 			b := page.GetBody().([]byte) | ||||
| 			pagesSlice = append(pagesSlice, b) | ||||
| 			// seperate pages with a comma | ||||
| 			pagesSlice = append(pagesSlice, []byte{10}) | ||||
| 			return true, nil | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		if len(pagesSlice) > 0 { | ||||
| 			// Remove the trailing comma. | ||||
| 			pagesSlice = pagesSlice[:len(pagesSlice)-1] | ||||
| 		} | ||||
| 		var b []byte | ||||
| 		// Combine the slice of slices in to a single slice. | ||||
| 		for _, slice := range pagesSlice { | ||||
| 			b = append(b, slice.([]byte)...) | ||||
| 		} | ||||
| 		// Set body to value of type `bytes`. | ||||
| 		body = reflect.New(reflect.TypeOf(b)).Elem() | ||||
| 		body.SetBytes(b) | ||||
| 	case []interface{}: | ||||
| 		// Iterate over the pages to concatenate the bodies. | ||||
| 		err = p.EachPage(func(page Page) (bool, error) { | ||||
| 			b := page.GetBody().([]interface{}) | ||||
| 			pagesSlice = append(pagesSlice, b...) | ||||
| 			return true, nil | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		// Set body to value of type `[]interface{}` | ||||
| 		body = reflect.MakeSlice(reflect.TypeOf(pagesSlice), len(pagesSlice), len(pagesSlice)) | ||||
| 		for i, s := range pagesSlice { | ||||
| 			body.Index(i).Set(reflect.ValueOf(s)) | ||||
| 		} | ||||
| 	default: | ||||
| 		err := gophercloud.ErrUnexpectedType{} | ||||
| 		err.Expected = "map[string]interface{}/[]byte/[]interface{}" | ||||
| 		err.Actual = fmt.Sprintf("%T", pb) | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// Each `Extract*` function is expecting a specific type of page coming back, | ||||
| 	// otherwise the type assertion in those functions will fail. pageType is needed | ||||
| 	// to create a type in this method that has the same type that the `Extract*` | ||||
| 	// function is expecting and set the Body of that object to the concatenated | ||||
| 	// pages. | ||||
| 	page := reflect.New(pageType) | ||||
| 	// Set the page body to be the concatenated pages. | ||||
| 	page.Elem().FieldByName("Body").Set(body) | ||||
| 	// Set any additional headers that were pass along. The `objectstorage` pacakge, | ||||
| 	// for example, passes a Content-Type header. | ||||
| 	h := make(http.Header) | ||||
| 	for k, v := range p.Headers { | ||||
| 		h.Add(k, v) | ||||
| 	} | ||||
| 	page.Elem().FieldByName("Header").Set(reflect.ValueOf(h)) | ||||
| 	// Type assert the page to a Page interface so that the type assertion in the | ||||
| 	// `Extract*` methods will work. | ||||
| 	return page.Elem().Interface().(Page), err | ||||
| } | ||||
							
								
								
									
										4
									
								
								vendor/github.com/gophercloud/gophercloud/pagination/pkg.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/gophercloud/gophercloud/pagination/pkg.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| /* | ||||
| Package pagination contains utilities and convenience structs that implement common pagination idioms within OpenStack APIs. | ||||
| */ | ||||
| package pagination | ||||
							
								
								
									
										33
									
								
								vendor/github.com/gophercloud/gophercloud/pagination/single.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/gophercloud/gophercloud/pagination/single.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| package pagination | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
|  | ||||
| 	"github.com/gophercloud/gophercloud" | ||||
| ) | ||||
|  | ||||
| // SinglePageBase may be embedded in a Page that contains all of the results from an operation at once. | ||||
| type SinglePageBase PageResult | ||||
|  | ||||
| // NextPageURL always returns "" to indicate that there are no more pages to return. | ||||
| func (current SinglePageBase) NextPageURL() (string, error) { | ||||
| 	return "", nil | ||||
| } | ||||
|  | ||||
| // IsEmpty satisifies the IsEmpty method of the Page interface | ||||
| func (current SinglePageBase) IsEmpty() (bool, error) { | ||||
| 	if b, ok := current.Body.([]interface{}); ok { | ||||
| 		return len(b) == 0, nil | ||||
| 	} | ||||
| 	err := gophercloud.ErrUnexpectedType{} | ||||
| 	err.Expected = "[]interface{}" | ||||
| 	err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) | ||||
| 	return true, err | ||||
| } | ||||
|  | ||||
| // GetBody returns the single page's body. This method is needed to satisfy the | ||||
| // Page interface. | ||||
| func (current SinglePageBase) GetBody() interface{} { | ||||
| 	return current.Body | ||||
| } | ||||
							
								
								
									
										491
									
								
								vendor/github.com/gophercloud/gophercloud/params.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										491
									
								
								vendor/github.com/gophercloud/gophercloud/params.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,491 @@ | ||||
| package gophercloud | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net/url" | ||||
| 	"reflect" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| /* | ||||
| BuildRequestBody builds a map[string]interface from the given `struct`. If | ||||
| parent is not an empty string, the final map[string]interface returned will | ||||
| encapsulate the built one. For example: | ||||
|  | ||||
|   disk := 1 | ||||
|   createOpts := flavors.CreateOpts{ | ||||
|     ID:         "1", | ||||
|     Name:       "m1.tiny", | ||||
|     Disk:       &disk, | ||||
|     RAM:        512, | ||||
|     VCPUs:      1, | ||||
|     RxTxFactor: 1.0, | ||||
|   } | ||||
|  | ||||
|   body, err := gophercloud.BuildRequestBody(createOpts, "flavor") | ||||
|  | ||||
| The above example can be run as-is, however it is recommended to look at how | ||||
| BuildRequestBody is used within Gophercloud to more fully understand how it | ||||
| fits within the request process as a whole rather than use it directly as shown | ||||
| above. | ||||
| */ | ||||
| func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, error) { | ||||
| 	optsValue := reflect.ValueOf(opts) | ||||
| 	if optsValue.Kind() == reflect.Ptr { | ||||
| 		optsValue = optsValue.Elem() | ||||
| 	} | ||||
|  | ||||
| 	optsType := reflect.TypeOf(opts) | ||||
| 	if optsType.Kind() == reflect.Ptr { | ||||
| 		optsType = optsType.Elem() | ||||
| 	} | ||||
|  | ||||
| 	optsMap := make(map[string]interface{}) | ||||
| 	if optsValue.Kind() == reflect.Struct { | ||||
| 		//fmt.Printf("optsValue.Kind() is a reflect.Struct: %+v\n", optsValue.Kind()) | ||||
| 		for i := 0; i < optsValue.NumField(); i++ { | ||||
| 			v := optsValue.Field(i) | ||||
| 			f := optsType.Field(i) | ||||
|  | ||||
| 			if f.Name != strings.Title(f.Name) { | ||||
| 				//fmt.Printf("Skipping field: %s...\n", f.Name) | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			//fmt.Printf("Starting on field: %s...\n", f.Name) | ||||
|  | ||||
| 			zero := isZero(v) | ||||
| 			//fmt.Printf("v is zero?: %v\n", zero) | ||||
|  | ||||
| 			// if the field has a required tag that's set to "true" | ||||
| 			if requiredTag := f.Tag.Get("required"); requiredTag == "true" { | ||||
| 				//fmt.Printf("Checking required field [%s]:\n\tv: %+v\n\tisZero:%v\n", f.Name, v.Interface(), zero) | ||||
| 				// if the field's value is zero, return a missing-argument error | ||||
| 				if zero { | ||||
| 					// if the field has a 'required' tag, it can't have a zero-value | ||||
| 					err := ErrMissingInput{} | ||||
| 					err.Argument = f.Name | ||||
| 					return nil, err | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if xorTag := f.Tag.Get("xor"); xorTag != "" { | ||||
| 				//fmt.Printf("Checking `xor` tag for field [%s] with value %+v:\n\txorTag: %s\n", f.Name, v, xorTag) | ||||
| 				xorField := optsValue.FieldByName(xorTag) | ||||
| 				var xorFieldIsZero bool | ||||
| 				if reflect.ValueOf(xorField.Interface()) == reflect.Zero(xorField.Type()) { | ||||
| 					xorFieldIsZero = true | ||||
| 				} else { | ||||
| 					if xorField.Kind() == reflect.Ptr { | ||||
| 						xorField = xorField.Elem() | ||||
| 					} | ||||
| 					xorFieldIsZero = isZero(xorField) | ||||
| 				} | ||||
| 				if !(zero != xorFieldIsZero) { | ||||
| 					err := ErrMissingInput{} | ||||
| 					err.Argument = fmt.Sprintf("%s/%s", f.Name, xorTag) | ||||
| 					err.Info = fmt.Sprintf("Exactly one of %s and %s must be provided", f.Name, xorTag) | ||||
| 					return nil, err | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if orTag := f.Tag.Get("or"); orTag != "" { | ||||
| 				//fmt.Printf("Checking `or` tag for field with:\n\tname: %+v\n\torTag:%s\n", f.Name, orTag) | ||||
| 				//fmt.Printf("field is zero?: %v\n", zero) | ||||
| 				if zero { | ||||
| 					orField := optsValue.FieldByName(orTag) | ||||
| 					var orFieldIsZero bool | ||||
| 					if reflect.ValueOf(orField.Interface()) == reflect.Zero(orField.Type()) { | ||||
| 						orFieldIsZero = true | ||||
| 					} else { | ||||
| 						if orField.Kind() == reflect.Ptr { | ||||
| 							orField = orField.Elem() | ||||
| 						} | ||||
| 						orFieldIsZero = isZero(orField) | ||||
| 					} | ||||
| 					if orFieldIsZero { | ||||
| 						err := ErrMissingInput{} | ||||
| 						err.Argument = fmt.Sprintf("%s/%s", f.Name, orTag) | ||||
| 						err.Info = fmt.Sprintf("At least one of %s and %s must be provided", f.Name, orTag) | ||||
| 						return nil, err | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			jsonTag := f.Tag.Get("json") | ||||
| 			if jsonTag == "-" { | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			if v.Kind() == reflect.Slice || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Slice) { | ||||
| 				sliceValue := v | ||||
| 				if sliceValue.Kind() == reflect.Ptr { | ||||
| 					sliceValue = sliceValue.Elem() | ||||
| 				} | ||||
|  | ||||
| 				for i := 0; i < sliceValue.Len(); i++ { | ||||
| 					element := sliceValue.Index(i) | ||||
| 					if element.Kind() == reflect.Struct || (element.Kind() == reflect.Ptr && element.Elem().Kind() == reflect.Struct) { | ||||
| 						_, err := BuildRequestBody(element.Interface(), "") | ||||
| 						if err != nil { | ||||
| 							return nil, err | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			if v.Kind() == reflect.Struct || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct) { | ||||
| 				if zero { | ||||
| 					//fmt.Printf("value before change: %+v\n", optsValue.Field(i)) | ||||
| 					if jsonTag != "" { | ||||
| 						jsonTagPieces := strings.Split(jsonTag, ",") | ||||
| 						if len(jsonTagPieces) > 1 && jsonTagPieces[1] == "omitempty" { | ||||
| 							if v.CanSet() { | ||||
| 								if !v.IsNil() { | ||||
| 									if v.Kind() == reflect.Ptr { | ||||
| 										v.Set(reflect.Zero(v.Type())) | ||||
| 									} | ||||
| 								} | ||||
| 								//fmt.Printf("value after change: %+v\n", optsValue.Field(i)) | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 					continue | ||||
| 				} | ||||
|  | ||||
| 				//fmt.Printf("Calling BuildRequestBody with:\n\tv: %+v\n\tf.Name:%s\n", v.Interface(), f.Name) | ||||
| 				_, err := BuildRequestBody(v.Interface(), f.Name) | ||||
| 				if err != nil { | ||||
| 					return nil, err | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		//fmt.Printf("opts: %+v \n", opts) | ||||
|  | ||||
| 		b, err := json.Marshal(opts) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		//fmt.Printf("string(b): %s\n", string(b)) | ||||
|  | ||||
| 		err = json.Unmarshal(b, &optsMap) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		//fmt.Printf("optsMap: %+v\n", optsMap) | ||||
|  | ||||
| 		if parent != "" { | ||||
| 			optsMap = map[string]interface{}{parent: optsMap} | ||||
| 		} | ||||
| 		//fmt.Printf("optsMap after parent added: %+v\n", optsMap) | ||||
| 		return optsMap, nil | ||||
| 	} | ||||
| 	// Return an error if the underlying type of 'opts' isn't a struct. | ||||
| 	return nil, fmt.Errorf("Options type is not a struct.") | ||||
| } | ||||
|  | ||||
| // EnabledState is a convenience type, mostly used in Create and Update | ||||
| // operations. Because the zero value of a bool is FALSE, we need to use a | ||||
| // pointer instead to indicate zero-ness. | ||||
| type EnabledState *bool | ||||
|  | ||||
| // Convenience vars for EnabledState values. | ||||
| var ( | ||||
| 	iTrue  = true | ||||
| 	iFalse = false | ||||
|  | ||||
| 	Enabled  EnabledState = &iTrue | ||||
| 	Disabled EnabledState = &iFalse | ||||
| ) | ||||
|  | ||||
| // IPVersion is a type for the possible IP address versions. Valid instances | ||||
| // are IPv4 and IPv6 | ||||
| type IPVersion int | ||||
|  | ||||
| const ( | ||||
| 	// IPv4 is used for IP version 4 addresses | ||||
| 	IPv4 IPVersion = 4 | ||||
| 	// IPv6 is used for IP version 6 addresses | ||||
| 	IPv6 IPVersion = 6 | ||||
| ) | ||||
|  | ||||
| // IntToPointer is a function for converting integers into integer pointers. | ||||
| // This is useful when passing in options to operations. | ||||
| func IntToPointer(i int) *int { | ||||
| 	return &i | ||||
| } | ||||
|  | ||||
| /* | ||||
| MaybeString is an internal function to be used by request methods in individual | ||||
| resource packages. | ||||
|  | ||||
| It takes a string that might be a zero value and returns either a pointer to its | ||||
| address or nil. This is useful for allowing users to conveniently omit values | ||||
| from an options struct by leaving them zeroed, but still pass nil to the JSON | ||||
| serializer so they'll be omitted from the request body. | ||||
| */ | ||||
| func MaybeString(original string) *string { | ||||
| 	if original != "" { | ||||
| 		return &original | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| /* | ||||
| MaybeInt is an internal function to be used by request methods in individual | ||||
| resource packages. | ||||
|  | ||||
| Like MaybeString, it accepts an int that may or may not be a zero value, and | ||||
| returns either a pointer to its address or nil. It's intended to hint that the | ||||
| JSON serializer should omit its field. | ||||
| */ | ||||
| func MaybeInt(original int) *int { | ||||
| 	if original != 0 { | ||||
| 		return &original | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| /* | ||||
| func isUnderlyingStructZero(v reflect.Value) bool { | ||||
| 	switch v.Kind() { | ||||
| 	case reflect.Ptr: | ||||
| 		return isUnderlyingStructZero(v.Elem()) | ||||
| 	default: | ||||
| 		return isZero(v) | ||||
| 	} | ||||
| } | ||||
| */ | ||||
|  | ||||
| var t time.Time | ||||
|  | ||||
| func isZero(v reflect.Value) bool { | ||||
| 	//fmt.Printf("\n\nchecking isZero for value: %+v\n", v) | ||||
| 	switch v.Kind() { | ||||
| 	case reflect.Ptr: | ||||
| 		if v.IsNil() { | ||||
| 			return true | ||||
| 		} | ||||
| 		return false | ||||
| 	case reflect.Func, reflect.Map, reflect.Slice: | ||||
| 		return v.IsNil() | ||||
| 	case reflect.Array: | ||||
| 		z := true | ||||
| 		for i := 0; i < v.Len(); i++ { | ||||
| 			z = z && isZero(v.Index(i)) | ||||
| 		} | ||||
| 		return z | ||||
| 	case reflect.Struct: | ||||
| 		if v.Type() == reflect.TypeOf(t) { | ||||
| 			if v.Interface().(time.Time).IsZero() { | ||||
| 				return true | ||||
| 			} | ||||
| 			return false | ||||
| 		} | ||||
| 		z := true | ||||
| 		for i := 0; i < v.NumField(); i++ { | ||||
| 			z = z && isZero(v.Field(i)) | ||||
| 		} | ||||
| 		return z | ||||
| 	} | ||||
| 	// Compare other types directly: | ||||
| 	z := reflect.Zero(v.Type()) | ||||
| 	//fmt.Printf("zero type for value: %+v\n\n\n", z) | ||||
| 	return v.Interface() == z.Interface() | ||||
| } | ||||
|  | ||||
| /* | ||||
| BuildQueryString is an internal function to be used by request methods in | ||||
| individual resource packages. | ||||
|  | ||||
| It accepts a tagged structure and expands it into a URL struct. Field names are | ||||
| converted into query parameters based on a "q" tag. For example: | ||||
|  | ||||
| 	type struct Something { | ||||
| 	   Bar string `q:"x_bar"` | ||||
| 	   Baz int    `q:"lorem_ipsum"` | ||||
| 	} | ||||
|  | ||||
| 	instance := Something{ | ||||
| 	   Bar: "AAA", | ||||
| 	   Baz: "BBB", | ||||
| 	} | ||||
|  | ||||
| will be converted into "?x_bar=AAA&lorem_ipsum=BBB". | ||||
|  | ||||
| The struct's fields may be strings, integers, or boolean values. Fields left at | ||||
| their type's zero value will be omitted from the query. | ||||
| */ | ||||
| func BuildQueryString(opts interface{}) (*url.URL, error) { | ||||
| 	optsValue := reflect.ValueOf(opts) | ||||
| 	if optsValue.Kind() == reflect.Ptr { | ||||
| 		optsValue = optsValue.Elem() | ||||
| 	} | ||||
|  | ||||
| 	optsType := reflect.TypeOf(opts) | ||||
| 	if optsType.Kind() == reflect.Ptr { | ||||
| 		optsType = optsType.Elem() | ||||
| 	} | ||||
|  | ||||
| 	params := url.Values{} | ||||
|  | ||||
| 	if optsValue.Kind() == reflect.Struct { | ||||
| 		for i := 0; i < optsValue.NumField(); i++ { | ||||
| 			v := optsValue.Field(i) | ||||
| 			f := optsType.Field(i) | ||||
| 			qTag := f.Tag.Get("q") | ||||
|  | ||||
| 			// if the field has a 'q' tag, it goes in the query string | ||||
| 			if qTag != "" { | ||||
| 				tags := strings.Split(qTag, ",") | ||||
|  | ||||
| 				// if the field is set, add it to the slice of query pieces | ||||
| 				if !isZero(v) { | ||||
| 				loop: | ||||
| 					switch v.Kind() { | ||||
| 					case reflect.Ptr: | ||||
| 						v = v.Elem() | ||||
| 						goto loop | ||||
| 					case reflect.String: | ||||
| 						params.Add(tags[0], v.String()) | ||||
| 					case reflect.Int: | ||||
| 						params.Add(tags[0], strconv.FormatInt(v.Int(), 10)) | ||||
| 					case reflect.Bool: | ||||
| 						params.Add(tags[0], strconv.FormatBool(v.Bool())) | ||||
| 					case reflect.Slice: | ||||
| 						switch v.Type().Elem() { | ||||
| 						case reflect.TypeOf(0): | ||||
| 							for i := 0; i < v.Len(); i++ { | ||||
| 								params.Add(tags[0], strconv.FormatInt(v.Index(i).Int(), 10)) | ||||
| 							} | ||||
| 						default: | ||||
| 							for i := 0; i < v.Len(); i++ { | ||||
| 								params.Add(tags[0], v.Index(i).String()) | ||||
| 							} | ||||
| 						} | ||||
| 					case reflect.Map: | ||||
| 						if v.Type().Key().Kind() == reflect.String && v.Type().Elem().Kind() == reflect.String { | ||||
| 							var s []string | ||||
| 							for _, k := range v.MapKeys() { | ||||
| 								value := v.MapIndex(k).String() | ||||
| 								s = append(s, fmt.Sprintf("'%s':'%s'", k.String(), value)) | ||||
| 							} | ||||
| 							params.Add(tags[0], fmt.Sprintf("{%s}", strings.Join(s, ", "))) | ||||
| 						} | ||||
| 					} | ||||
| 				} else { | ||||
| 					// if the field has a 'required' tag, it can't have a zero-value | ||||
| 					if requiredTag := f.Tag.Get("required"); requiredTag == "true" { | ||||
| 						return &url.URL{}, fmt.Errorf("Required query parameter [%s] not set.", f.Name) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return &url.URL{RawQuery: params.Encode()}, nil | ||||
| 	} | ||||
| 	// Return an error if the underlying type of 'opts' isn't a struct. | ||||
| 	return nil, fmt.Errorf("Options type is not a struct.") | ||||
| } | ||||
|  | ||||
| /* | ||||
| BuildHeaders is an internal function to be used by request methods in | ||||
| individual resource packages. | ||||
|  | ||||
| It accepts an arbitrary tagged structure and produces a string map that's | ||||
| suitable for use as the HTTP headers of an outgoing request. Field names are | ||||
| mapped to header names based in "h" tags. | ||||
|  | ||||
|   type struct Something { | ||||
|     Bar string `h:"x_bar"` | ||||
|     Baz int    `h:"lorem_ipsum"` | ||||
|   } | ||||
|  | ||||
|   instance := Something{ | ||||
|     Bar: "AAA", | ||||
|     Baz: "BBB", | ||||
|   } | ||||
|  | ||||
| will be converted into: | ||||
|  | ||||
|   map[string]string{ | ||||
|     "x_bar": "AAA", | ||||
|     "lorem_ipsum": "BBB", | ||||
|   } | ||||
|  | ||||
| Untagged fields and fields left at their zero values are skipped. Integers, | ||||
| booleans and string values are supported. | ||||
| */ | ||||
| func BuildHeaders(opts interface{}) (map[string]string, error) { | ||||
| 	optsValue := reflect.ValueOf(opts) | ||||
| 	if optsValue.Kind() == reflect.Ptr { | ||||
| 		optsValue = optsValue.Elem() | ||||
| 	} | ||||
|  | ||||
| 	optsType := reflect.TypeOf(opts) | ||||
| 	if optsType.Kind() == reflect.Ptr { | ||||
| 		optsType = optsType.Elem() | ||||
| 	} | ||||
|  | ||||
| 	optsMap := make(map[string]string) | ||||
| 	if optsValue.Kind() == reflect.Struct { | ||||
| 		for i := 0; i < optsValue.NumField(); i++ { | ||||
| 			v := optsValue.Field(i) | ||||
| 			f := optsType.Field(i) | ||||
| 			hTag := f.Tag.Get("h") | ||||
|  | ||||
| 			// if the field has a 'h' tag, it goes in the header | ||||
| 			if hTag != "" { | ||||
| 				tags := strings.Split(hTag, ",") | ||||
|  | ||||
| 				// if the field is set, add it to the slice of query pieces | ||||
| 				if !isZero(v) { | ||||
| 					switch v.Kind() { | ||||
| 					case reflect.String: | ||||
| 						optsMap[tags[0]] = v.String() | ||||
| 					case reflect.Int: | ||||
| 						optsMap[tags[0]] = strconv.FormatInt(v.Int(), 10) | ||||
| 					case reflect.Bool: | ||||
| 						optsMap[tags[0]] = strconv.FormatBool(v.Bool()) | ||||
| 					} | ||||
| 				} else { | ||||
| 					// if the field has a 'required' tag, it can't have a zero-value | ||||
| 					if requiredTag := f.Tag.Get("required"); requiredTag == "true" { | ||||
| 						return optsMap, fmt.Errorf("Required header [%s] not set.", f.Name) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
| 		return optsMap, nil | ||||
| 	} | ||||
| 	// Return an error if the underlying type of 'opts' isn't a struct. | ||||
| 	return optsMap, fmt.Errorf("Options type is not a struct.") | ||||
| } | ||||
|  | ||||
| // IDSliceToQueryString takes a slice of elements and converts them into a query | ||||
| // string. For example, if name=foo and slice=[]int{20, 40, 60}, then the | ||||
| // result would be `?name=20&name=40&name=60' | ||||
| func IDSliceToQueryString(name string, ids []int) string { | ||||
| 	str := "" | ||||
| 	for k, v := range ids { | ||||
| 		if k == 0 { | ||||
| 			str += "?" | ||||
| 		} else { | ||||
| 			str += "&" | ||||
| 		} | ||||
| 		str += fmt.Sprintf("%s=%s", name, strconv.Itoa(v)) | ||||
| 	} | ||||
| 	return str | ||||
| } | ||||
|  | ||||
| // IntWithinRange returns TRUE if an integer falls within a defined range, and | ||||
| // FALSE if not. | ||||
| func IntWithinRange(val, min, max int) bool { | ||||
| 	return val > min && val < max | ||||
| } | ||||
							
								
								
									
										431
									
								
								vendor/github.com/gophercloud/gophercloud/provider_client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										431
									
								
								vendor/github.com/gophercloud/gophercloud/provider_client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,431 @@ | ||||
| package gophercloud | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| ) | ||||
|  | ||||
| // DefaultUserAgent is the default User-Agent string set in the request header. | ||||
| const DefaultUserAgent = "gophercloud/2.0.0" | ||||
|  | ||||
| // UserAgent represents a User-Agent header. | ||||
| type UserAgent struct { | ||||
| 	// prepend is the slice of User-Agent strings to prepend to DefaultUserAgent. | ||||
| 	// All the strings to prepend are accumulated and prepended in the Join method. | ||||
| 	prepend []string | ||||
| } | ||||
|  | ||||
| // Prepend prepends a user-defined string to the default User-Agent string. Users | ||||
| // may pass in one or more strings to prepend. | ||||
| func (ua *UserAgent) Prepend(s ...string) { | ||||
| 	ua.prepend = append(s, ua.prepend...) | ||||
| } | ||||
|  | ||||
| // Join concatenates all the user-defined User-Agend strings with the default | ||||
| // Gophercloud User-Agent string. | ||||
| func (ua *UserAgent) Join() string { | ||||
| 	uaSlice := append(ua.prepend, DefaultUserAgent) | ||||
| 	return strings.Join(uaSlice, " ") | ||||
| } | ||||
|  | ||||
| // ProviderClient stores details that are required to interact with any | ||||
| // services within a specific provider's API. | ||||
| // | ||||
| // Generally, you acquire a ProviderClient by calling the NewClient method in | ||||
| // the appropriate provider's child package, providing whatever authentication | ||||
| // credentials are required. | ||||
| type ProviderClient struct { | ||||
| 	// IdentityBase is the base URL used for a particular provider's identity | ||||
| 	// service - it will be used when issuing authenticatation requests. It | ||||
| 	// should point to the root resource of the identity service, not a specific | ||||
| 	// identity version. | ||||
| 	IdentityBase string | ||||
|  | ||||
| 	// IdentityEndpoint is the identity endpoint. This may be a specific version | ||||
| 	// of the identity service. If this is the case, this endpoint is used rather | ||||
| 	// than querying versions first. | ||||
| 	IdentityEndpoint string | ||||
|  | ||||
| 	// TokenID is the ID of the most recently issued valid token. | ||||
| 	// NOTE: Aside from within a custom ReauthFunc, this field shouldn't be set by an application. | ||||
| 	// To safely read or write this value, call `Token` or `SetToken`, respectively | ||||
| 	TokenID string | ||||
|  | ||||
| 	// EndpointLocator describes how this provider discovers the endpoints for | ||||
| 	// its constituent services. | ||||
| 	EndpointLocator EndpointLocator | ||||
|  | ||||
| 	// HTTPClient allows users to interject arbitrary http, https, or other transit behaviors. | ||||
| 	HTTPClient http.Client | ||||
|  | ||||
| 	// UserAgent represents the User-Agent header in the HTTP request. | ||||
| 	UserAgent UserAgent | ||||
|  | ||||
| 	// ReauthFunc is the function used to re-authenticate the user if the request | ||||
| 	// fails with a 401 HTTP response code. This a needed because there may be multiple | ||||
| 	// authentication functions for different Identity service versions. | ||||
| 	ReauthFunc func() error | ||||
|  | ||||
| 	// Throwaway determines whether if this client is a throw-away client. It's a copy of user's provider client | ||||
| 	// with the token and reauth func zeroed. Such client can be used to perform reauthorization. | ||||
| 	Throwaway bool | ||||
|  | ||||
| 	mut *sync.RWMutex | ||||
|  | ||||
| 	reauthmut *reauthlock | ||||
| } | ||||
|  | ||||
| type reauthlock struct { | ||||
| 	sync.RWMutex | ||||
| 	reauthing    bool | ||||
| 	reauthingErr error | ||||
| 	done         *sync.Cond | ||||
| } | ||||
|  | ||||
| // AuthenticatedHeaders returns a map of HTTP headers that are common for all | ||||
| // authenticated service requests. Blocks if Reauthenticate is in progress. | ||||
| func (client *ProviderClient) AuthenticatedHeaders() (m map[string]string) { | ||||
| 	if client.IsThrowaway() { | ||||
| 		return | ||||
| 	} | ||||
| 	if client.reauthmut != nil { | ||||
| 		client.reauthmut.Lock() | ||||
| 		for client.reauthmut.reauthing { | ||||
| 			client.reauthmut.done.Wait() | ||||
| 		} | ||||
| 		client.reauthmut.Unlock() | ||||
| 	} | ||||
| 	t := client.Token() | ||||
| 	if t == "" { | ||||
| 		return | ||||
| 	} | ||||
| 	return map[string]string{"X-Auth-Token": t} | ||||
| } | ||||
|  | ||||
| // UseTokenLock creates a mutex that is used to allow safe concurrent access to the auth token. | ||||
| // If the application's ProviderClient is not used concurrently, this doesn't need to be called. | ||||
| func (client *ProviderClient) UseTokenLock() { | ||||
| 	client.mut = new(sync.RWMutex) | ||||
| 	client.reauthmut = new(reauthlock) | ||||
| } | ||||
|  | ||||
| // Token safely reads the value of the auth token from the ProviderClient. Applications should | ||||
| // call this method to access the token instead of the TokenID field | ||||
| func (client *ProviderClient) Token() string { | ||||
| 	if client.mut != nil { | ||||
| 		client.mut.RLock() | ||||
| 		defer client.mut.RUnlock() | ||||
| 	} | ||||
| 	return client.TokenID | ||||
| } | ||||
|  | ||||
| // SetToken safely sets the value of the auth token in the ProviderClient. Applications may | ||||
| // use this method in a custom ReauthFunc | ||||
| func (client *ProviderClient) SetToken(t string) { | ||||
| 	if client.mut != nil { | ||||
| 		client.mut.Lock() | ||||
| 		defer client.mut.Unlock() | ||||
| 	} | ||||
| 	client.TokenID = t | ||||
| } | ||||
|  | ||||
| // IsThrowaway safely reads the value of the client Throwaway field. | ||||
| func (client *ProviderClient) IsThrowaway() bool { | ||||
| 	if client.reauthmut != nil { | ||||
| 		client.reauthmut.RLock() | ||||
| 		defer client.reauthmut.RUnlock() | ||||
| 	} | ||||
| 	return client.Throwaway | ||||
| } | ||||
|  | ||||
| // SetThrowaway safely sets the value of the client Throwaway field. | ||||
| func (client *ProviderClient) SetThrowaway(v bool) { | ||||
| 	if client.reauthmut != nil { | ||||
| 		client.reauthmut.Lock() | ||||
| 		defer client.reauthmut.Unlock() | ||||
| 	} | ||||
| 	client.Throwaway = v | ||||
| } | ||||
|  | ||||
| // Reauthenticate calls client.ReauthFunc in a thread-safe way. If this is | ||||
| // called because of a 401 response, the caller may pass the previous token. In | ||||
| // this case, the reauthentication can be skipped if another thread has already | ||||
| // reauthenticated in the meantime. If no previous token is known, an empty | ||||
| // string should be passed instead to force unconditional reauthentication. | ||||
| func (client *ProviderClient) Reauthenticate(previousToken string) (err error) { | ||||
| 	if client.ReauthFunc == nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if client.mut == nil { | ||||
| 		return client.ReauthFunc() | ||||
| 	} | ||||
|  | ||||
| 	client.reauthmut.Lock() | ||||
| 	if client.reauthmut.reauthing { | ||||
| 		for !client.reauthmut.reauthing { | ||||
| 			client.reauthmut.done.Wait() | ||||
| 		} | ||||
| 		err = client.reauthmut.reauthingErr | ||||
| 		client.reauthmut.Unlock() | ||||
| 		return err | ||||
| 	} | ||||
| 	client.reauthmut.Unlock() | ||||
|  | ||||
| 	client.mut.Lock() | ||||
| 	defer client.mut.Unlock() | ||||
|  | ||||
| 	client.reauthmut.Lock() | ||||
| 	client.reauthmut.reauthing = true | ||||
| 	client.reauthmut.done = sync.NewCond(client.reauthmut) | ||||
| 	client.reauthmut.reauthingErr = nil | ||||
| 	client.reauthmut.Unlock() | ||||
|  | ||||
| 	if previousToken == "" || client.TokenID == previousToken { | ||||
| 		err = client.ReauthFunc() | ||||
| 	} | ||||
|  | ||||
| 	client.reauthmut.Lock() | ||||
| 	client.reauthmut.reauthing = false | ||||
| 	client.reauthmut.reauthingErr = err | ||||
| 	client.reauthmut.done.Broadcast() | ||||
| 	client.reauthmut.Unlock() | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // RequestOpts customizes the behavior of the provider.Request() method. | ||||
| type RequestOpts struct { | ||||
| 	// JSONBody, if provided, will be encoded as JSON and used as the body of the HTTP request. The | ||||
| 	// content type of the request will default to "application/json" unless overridden by MoreHeaders. | ||||
| 	// It's an error to specify both a JSONBody and a RawBody. | ||||
| 	JSONBody interface{} | ||||
| 	// RawBody contains an io.Reader that will be consumed by the request directly. No content-type | ||||
| 	// will be set unless one is provided explicitly by MoreHeaders. | ||||
| 	RawBody io.Reader | ||||
| 	// JSONResponse, if provided, will be populated with the contents of the response body parsed as | ||||
| 	// JSON. | ||||
| 	JSONResponse interface{} | ||||
| 	// OkCodes contains a list of numeric HTTP status codes that should be interpreted as success. If | ||||
| 	// the response has a different code, an error will be returned. | ||||
| 	OkCodes []int | ||||
| 	// MoreHeaders specifies additional HTTP headers to be provide on the request. If a header is | ||||
| 	// provided with a blank value (""), that header will be *omitted* instead: use this to suppress | ||||
| 	// the default Accept header or an inferred Content-Type, for example. | ||||
| 	MoreHeaders map[string]string | ||||
| 	// ErrorContext specifies the resource error type to return if an error is encountered. | ||||
| 	// This lets resources override default error messages based on the response status code. | ||||
| 	ErrorContext error | ||||
| } | ||||
|  | ||||
| var applicationJSON = "application/json" | ||||
|  | ||||
| // Request performs an HTTP request using the ProviderClient's current HTTPClient. An authentication | ||||
| // header will automatically be provided. | ||||
| func (client *ProviderClient) Request(method, url string, options *RequestOpts) (*http.Response, error) { | ||||
| 	var body io.Reader | ||||
| 	var contentType *string | ||||
|  | ||||
| 	// Derive the content body by either encoding an arbitrary object as JSON, or by taking a provided | ||||
| 	// io.ReadSeeker as-is. Default the content-type to application/json. | ||||
| 	if options.JSONBody != nil { | ||||
| 		if options.RawBody != nil { | ||||
| 			return nil, errors.New("please provide only one of JSONBody or RawBody to gophercloud.Request()") | ||||
| 		} | ||||
|  | ||||
| 		rendered, err := json.Marshal(options.JSONBody) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		body = bytes.NewReader(rendered) | ||||
| 		contentType = &applicationJSON | ||||
| 	} | ||||
|  | ||||
| 	if options.RawBody != nil { | ||||
| 		body = options.RawBody | ||||
| 	} | ||||
|  | ||||
| 	// Construct the http.Request. | ||||
| 	req, err := http.NewRequest(method, url, body) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// Populate the request headers. Apply options.MoreHeaders last, to give the caller the chance to | ||||
| 	// modify or omit any header. | ||||
| 	if contentType != nil { | ||||
| 		req.Header.Set("Content-Type", *contentType) | ||||
| 	} | ||||
| 	req.Header.Set("Accept", applicationJSON) | ||||
|  | ||||
| 	// Set the User-Agent header | ||||
| 	req.Header.Set("User-Agent", client.UserAgent.Join()) | ||||
|  | ||||
| 	if options.MoreHeaders != nil { | ||||
| 		for k, v := range options.MoreHeaders { | ||||
| 			if v != "" { | ||||
| 				req.Header.Set(k, v) | ||||
| 			} else { | ||||
| 				req.Header.Del(k) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// get latest token from client | ||||
| 	for k, v := range client.AuthenticatedHeaders() { | ||||
| 		req.Header.Set(k, v) | ||||
| 	} | ||||
|  | ||||
| 	// Set connection parameter to close the connection immediately when we've got the response | ||||
| 	req.Close = true | ||||
|  | ||||
| 	prereqtok := req.Header.Get("X-Auth-Token") | ||||
|  | ||||
| 	// Issue the request. | ||||
| 	resp, err := client.HTTPClient.Do(req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// Allow default OkCodes if none explicitly set | ||||
| 	okc := options.OkCodes | ||||
| 	if okc == nil { | ||||
| 		okc = defaultOkCodes(method) | ||||
| 	} | ||||
|  | ||||
| 	// Validate the HTTP response status. | ||||
| 	var ok bool | ||||
| 	for _, code := range okc { | ||||
| 		if resp.StatusCode == code { | ||||
| 			ok = true | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if !ok { | ||||
| 		body, _ := ioutil.ReadAll(resp.Body) | ||||
| 		resp.Body.Close() | ||||
| 		respErr := ErrUnexpectedResponseCode{ | ||||
| 			URL:      url, | ||||
| 			Method:   method, | ||||
| 			Expected: options.OkCodes, | ||||
| 			Actual:   resp.StatusCode, | ||||
| 			Body:     body, | ||||
| 		} | ||||
|  | ||||
| 		errType := options.ErrorContext | ||||
| 		switch resp.StatusCode { | ||||
| 		case http.StatusBadRequest: | ||||
| 			err = ErrDefault400{respErr} | ||||
| 			if error400er, ok := errType.(Err400er); ok { | ||||
| 				err = error400er.Error400(respErr) | ||||
| 			} | ||||
| 		case http.StatusUnauthorized: | ||||
| 			if client.ReauthFunc != nil { | ||||
| 				err = client.Reauthenticate(prereqtok) | ||||
| 				if err != nil { | ||||
| 					e := &ErrUnableToReauthenticate{} | ||||
| 					e.ErrOriginal = respErr | ||||
| 					return nil, e | ||||
| 				} | ||||
| 				if options.RawBody != nil { | ||||
| 					if seeker, ok := options.RawBody.(io.Seeker); ok { | ||||
| 						seeker.Seek(0, 0) | ||||
| 					} | ||||
| 				} | ||||
| 				resp, err = client.Request(method, url, options) | ||||
| 				if err != nil { | ||||
| 					switch err.(type) { | ||||
| 					case *ErrUnexpectedResponseCode: | ||||
| 						e := &ErrErrorAfterReauthentication{} | ||||
| 						e.ErrOriginal = err.(*ErrUnexpectedResponseCode) | ||||
| 						return nil, e | ||||
| 					default: | ||||
| 						e := &ErrErrorAfterReauthentication{} | ||||
| 						e.ErrOriginal = err | ||||
| 						return nil, e | ||||
| 					} | ||||
| 				} | ||||
| 				return resp, nil | ||||
| 			} | ||||
| 			err = ErrDefault401{respErr} | ||||
| 			if error401er, ok := errType.(Err401er); ok { | ||||
| 				err = error401er.Error401(respErr) | ||||
| 			} | ||||
| 		case http.StatusForbidden: | ||||
| 			err = ErrDefault403{respErr} | ||||
| 			if error403er, ok := errType.(Err403er); ok { | ||||
| 				err = error403er.Error403(respErr) | ||||
| 			} | ||||
| 		case http.StatusNotFound: | ||||
| 			err = ErrDefault404{respErr} | ||||
| 			if error404er, ok := errType.(Err404er); ok { | ||||
| 				err = error404er.Error404(respErr) | ||||
| 			} | ||||
| 		case http.StatusMethodNotAllowed: | ||||
| 			err = ErrDefault405{respErr} | ||||
| 			if error405er, ok := errType.(Err405er); ok { | ||||
| 				err = error405er.Error405(respErr) | ||||
| 			} | ||||
| 		case http.StatusRequestTimeout: | ||||
| 			err = ErrDefault408{respErr} | ||||
| 			if error408er, ok := errType.(Err408er); ok { | ||||
| 				err = error408er.Error408(respErr) | ||||
| 			} | ||||
| 		case 429: | ||||
| 			err = ErrDefault429{respErr} | ||||
| 			if error429er, ok := errType.(Err429er); ok { | ||||
| 				err = error429er.Error429(respErr) | ||||
| 			} | ||||
| 		case http.StatusInternalServerError: | ||||
| 			err = ErrDefault500{respErr} | ||||
| 			if error500er, ok := errType.(Err500er); ok { | ||||
| 				err = error500er.Error500(respErr) | ||||
| 			} | ||||
| 		case http.StatusServiceUnavailable: | ||||
| 			err = ErrDefault503{respErr} | ||||
| 			if error503er, ok := errType.(Err503er); ok { | ||||
| 				err = error503er.Error503(respErr) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if err == nil { | ||||
| 			err = respErr | ||||
| 		} | ||||
|  | ||||
| 		return resp, err | ||||
| 	} | ||||
|  | ||||
| 	// Parse the response body as JSON, if requested to do so. | ||||
| 	if options.JSONResponse != nil { | ||||
| 		defer resp.Body.Close() | ||||
| 		if err := json.NewDecoder(resp.Body).Decode(options.JSONResponse); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return resp, nil | ||||
| } | ||||
|  | ||||
| func defaultOkCodes(method string) []int { | ||||
| 	switch { | ||||
| 	case method == "GET": | ||||
| 		return []int{200} | ||||
| 	case method == "POST": | ||||
| 		return []int{201, 202} | ||||
| 	case method == "PUT": | ||||
| 		return []int{201, 202} | ||||
| 	case method == "PATCH": | ||||
| 		return []int{200, 202, 204} | ||||
| 	case method == "DELETE": | ||||
| 		return []int{202, 204} | ||||
| 	} | ||||
|  | ||||
| 	return []int{} | ||||
| } | ||||
							
								
								
									
										448
									
								
								vendor/github.com/gophercloud/gophercloud/results.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										448
									
								
								vendor/github.com/gophercloud/gophercloud/results.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,448 @@ | ||||
| package gophercloud | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 	"reflect" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| /* | ||||
| Result is an internal type to be used by individual resource packages, but its | ||||
| methods will be available on a wide variety of user-facing embedding types. | ||||
|  | ||||
| It acts as a base struct that other Result types, returned from request | ||||
| functions, can embed for convenience. All Results capture basic information | ||||
| from the HTTP transaction that was performed, including the response body, | ||||
| HTTP headers, and any errors that happened. | ||||
|  | ||||
| Generally, each Result type will have an Extract method that can be used to | ||||
| further interpret the result's payload in a specific context. Extensions or | ||||
| providers can then provide additional extraction functions to pull out | ||||
| provider- or extension-specific information as well. | ||||
| */ | ||||
| type Result struct { | ||||
| 	// Body is the payload of the HTTP response from the server. In most cases, | ||||
| 	// this will be the deserialized JSON structure. | ||||
| 	Body interface{} | ||||
|  | ||||
| 	// Header contains the HTTP header structure from the original response. | ||||
| 	Header http.Header | ||||
|  | ||||
| 	// Err is an error that occurred during the operation. It's deferred until | ||||
| 	// extraction to make it easier to chain the Extract call. | ||||
| 	Err error | ||||
| } | ||||
|  | ||||
| // ExtractInto allows users to provide an object into which `Extract` will extract | ||||
| // the `Result.Body`. This would be useful for OpenStack providers that have | ||||
| // different fields in the response object than OpenStack proper. | ||||
| func (r Result) ExtractInto(to interface{}) error { | ||||
| 	if r.Err != nil { | ||||
| 		return r.Err | ||||
| 	} | ||||
|  | ||||
| 	if reader, ok := r.Body.(io.Reader); ok { | ||||
| 		if readCloser, ok := reader.(io.Closer); ok { | ||||
| 			defer readCloser.Close() | ||||
| 		} | ||||
| 		return json.NewDecoder(reader).Decode(to) | ||||
| 	} | ||||
|  | ||||
| 	b, err := json.Marshal(r.Body) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	err = json.Unmarshal(b, to) | ||||
|  | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (r Result) extractIntoPtr(to interface{}, label string) error { | ||||
| 	if label == "" { | ||||
| 		return r.ExtractInto(&to) | ||||
| 	} | ||||
|  | ||||
| 	var m map[string]interface{} | ||||
| 	err := r.ExtractInto(&m) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	b, err := json.Marshal(m[label]) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	toValue := reflect.ValueOf(to) | ||||
| 	if toValue.Kind() == reflect.Ptr { | ||||
| 		toValue = toValue.Elem() | ||||
| 	} | ||||
|  | ||||
| 	switch toValue.Kind() { | ||||
| 	case reflect.Slice: | ||||
| 		typeOfV := toValue.Type().Elem() | ||||
| 		if typeOfV.Kind() == reflect.Struct { | ||||
| 			if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous { | ||||
| 				newSlice := reflect.MakeSlice(reflect.SliceOf(typeOfV), 0, 0) | ||||
|  | ||||
| 				if mSlice, ok := m[label].([]interface{}); ok { | ||||
| 					for _, v := range mSlice { | ||||
| 						// For each iteration of the slice, we create a new struct. | ||||
| 						// This is to work around a bug where elements of a slice | ||||
| 						// are reused and not overwritten when the same copy of the | ||||
| 						// struct is used: | ||||
| 						// | ||||
| 						// https://github.com/golang/go/issues/21092 | ||||
| 						// https://github.com/golang/go/issues/24155 | ||||
| 						// https://play.golang.org/p/NHo3ywlPZli | ||||
| 						newType := reflect.New(typeOfV).Elem() | ||||
|  | ||||
| 						b, err := json.Marshal(v) | ||||
| 						if err != nil { | ||||
| 							return err | ||||
| 						} | ||||
|  | ||||
| 						// This is needed for structs with an UnmarshalJSON method. | ||||
| 						// Technically this is just unmarshalling the response into | ||||
| 						// a struct that is never used, but it's good enough to | ||||
| 						// trigger the UnmarshalJSON method. | ||||
| 						for i := 0; i < newType.NumField(); i++ { | ||||
| 							s := newType.Field(i).Addr().Interface() | ||||
|  | ||||
| 							// Unmarshal is used rather than NewDecoder to also work | ||||
| 							// around the above-mentioned bug. | ||||
| 							err = json.Unmarshal(b, s) | ||||
| 							if err != nil { | ||||
| 								return err | ||||
| 							} | ||||
| 						} | ||||
|  | ||||
| 						newSlice = reflect.Append(newSlice, newType) | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				// "to" should now be properly modeled to receive the | ||||
| 				// JSON response body and unmarshal into all the correct | ||||
| 				// fields of the struct or composed extension struct | ||||
| 				// at the end of this method. | ||||
| 				toValue.Set(newSlice) | ||||
| 			} | ||||
| 		} | ||||
| 	case reflect.Struct: | ||||
| 		typeOfV := toValue.Type() | ||||
| 		if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous { | ||||
| 			for i := 0; i < toValue.NumField(); i++ { | ||||
| 				toField := toValue.Field(i) | ||||
| 				if toField.Kind() == reflect.Struct { | ||||
| 					s := toField.Addr().Interface() | ||||
| 					err = json.NewDecoder(bytes.NewReader(b)).Decode(s) | ||||
| 					if err != nil { | ||||
| 						return err | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	err = json.Unmarshal(b, &to) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // ExtractIntoStructPtr will unmarshal the Result (r) into the provided | ||||
| // interface{} (to). | ||||
| // | ||||
| // NOTE: For internal use only | ||||
| // | ||||
| // `to` must be a pointer to an underlying struct type | ||||
| // | ||||
| // If provided, `label` will be filtered out of the response | ||||
| // body prior to `r` being unmarshalled into `to`. | ||||
| func (r Result) ExtractIntoStructPtr(to interface{}, label string) error { | ||||
| 	if r.Err != nil { | ||||
| 		return r.Err | ||||
| 	} | ||||
|  | ||||
| 	t := reflect.TypeOf(to) | ||||
| 	if k := t.Kind(); k != reflect.Ptr { | ||||
| 		return fmt.Errorf("Expected pointer, got %v", k) | ||||
| 	} | ||||
| 	switch t.Elem().Kind() { | ||||
| 	case reflect.Struct: | ||||
| 		return r.extractIntoPtr(to, label) | ||||
| 	default: | ||||
| 		return fmt.Errorf("Expected pointer to struct, got: %v", t) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // ExtractIntoSlicePtr will unmarshal the Result (r) into the provided | ||||
| // interface{} (to). | ||||
| // | ||||
| // NOTE: For internal use only | ||||
| // | ||||
| // `to` must be a pointer to an underlying slice type | ||||
| // | ||||
| // If provided, `label` will be filtered out of the response | ||||
| // body prior to `r` being unmarshalled into `to`. | ||||
| func (r Result) ExtractIntoSlicePtr(to interface{}, label string) error { | ||||
| 	if r.Err != nil { | ||||
| 		return r.Err | ||||
| 	} | ||||
|  | ||||
| 	t := reflect.TypeOf(to) | ||||
| 	if k := t.Kind(); k != reflect.Ptr { | ||||
| 		return fmt.Errorf("Expected pointer, got %v", k) | ||||
| 	} | ||||
| 	switch t.Elem().Kind() { | ||||
| 	case reflect.Slice: | ||||
| 		return r.extractIntoPtr(to, label) | ||||
| 	default: | ||||
| 		return fmt.Errorf("Expected pointer to slice, got: %v", t) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // PrettyPrintJSON creates a string containing the full response body as | ||||
| // pretty-printed JSON. It's useful for capturing test fixtures and for | ||||
| // debugging extraction bugs. If you include its output in an issue related to | ||||
| // a buggy extraction function, we will all love you forever. | ||||
| func (r Result) PrettyPrintJSON() string { | ||||
| 	pretty, err := json.MarshalIndent(r.Body, "", "  ") | ||||
| 	if err != nil { | ||||
| 		panic(err.Error()) | ||||
| 	} | ||||
| 	return string(pretty) | ||||
| } | ||||
|  | ||||
| // ErrResult is an internal type to be used by individual resource packages, but | ||||
| // its methods will be available on a wide variety of user-facing embedding | ||||
| // types. | ||||
| // | ||||
| // It represents results that only contain a potential error and | ||||
| // nothing else. Usually, if the operation executed successfully, the Err field | ||||
| // will be nil; otherwise it will be stocked with a relevant error. Use the | ||||
| // ExtractErr method | ||||
| // to cleanly pull it out. | ||||
| type ErrResult struct { | ||||
| 	Result | ||||
| } | ||||
|  | ||||
| // ExtractErr is a function that extracts error information, or nil, from a result. | ||||
| func (r ErrResult) ExtractErr() error { | ||||
| 	return r.Err | ||||
| } | ||||
|  | ||||
| /* | ||||
| HeaderResult is an internal type to be used by individual resource packages, but | ||||
| its methods will be available on a wide variety of user-facing embedding types. | ||||
|  | ||||
| It represents a result that only contains an error (possibly nil) and an | ||||
| http.Header. This is used, for example, by the objectstorage packages in | ||||
| openstack, because most of the operations don't return response bodies, but do | ||||
| have relevant information in headers. | ||||
| */ | ||||
| type HeaderResult struct { | ||||
| 	Result | ||||
| } | ||||
|  | ||||
| // ExtractInto allows users to provide an object into which `Extract` will | ||||
| // extract the http.Header headers of the result. | ||||
| func (r HeaderResult) ExtractInto(to interface{}) error { | ||||
| 	if r.Err != nil { | ||||
| 		return r.Err | ||||
| 	} | ||||
|  | ||||
| 	tmpHeaderMap := map[string]string{} | ||||
| 	for k, v := range r.Header { | ||||
| 		if len(v) > 0 { | ||||
| 			tmpHeaderMap[k] = v[0] | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	b, err := json.Marshal(tmpHeaderMap) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	err = json.Unmarshal(b, to) | ||||
|  | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // RFC3339Milli describes a common time format used by some API responses. | ||||
| const RFC3339Milli = "2006-01-02T15:04:05.999999Z" | ||||
|  | ||||
| type JSONRFC3339Milli time.Time | ||||
|  | ||||
| func (jt *JSONRFC3339Milli) UnmarshalJSON(data []byte) error { | ||||
| 	b := bytes.NewBuffer(data) | ||||
| 	dec := json.NewDecoder(b) | ||||
| 	var s string | ||||
| 	if err := dec.Decode(&s); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	t, err := time.Parse(RFC3339Milli, s) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	*jt = JSONRFC3339Milli(t) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| const RFC3339MilliNoZ = "2006-01-02T15:04:05.999999" | ||||
|  | ||||
| type JSONRFC3339MilliNoZ time.Time | ||||
|  | ||||
| func (jt *JSONRFC3339MilliNoZ) UnmarshalJSON(data []byte) error { | ||||
| 	var s string | ||||
| 	if err := json.Unmarshal(data, &s); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if s == "" { | ||||
| 		return nil | ||||
| 	} | ||||
| 	t, err := time.Parse(RFC3339MilliNoZ, s) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	*jt = JSONRFC3339MilliNoZ(t) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| type JSONRFC1123 time.Time | ||||
|  | ||||
| func (jt *JSONRFC1123) UnmarshalJSON(data []byte) error { | ||||
| 	var s string | ||||
| 	if err := json.Unmarshal(data, &s); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if s == "" { | ||||
| 		return nil | ||||
| 	} | ||||
| 	t, err := time.Parse(time.RFC1123, s) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	*jt = JSONRFC1123(t) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| type JSONUnix time.Time | ||||
|  | ||||
| func (jt *JSONUnix) UnmarshalJSON(data []byte) error { | ||||
| 	var s string | ||||
| 	if err := json.Unmarshal(data, &s); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if s == "" { | ||||
| 		return nil | ||||
| 	} | ||||
| 	unix, err := strconv.ParseInt(s, 10, 64) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	t = time.Unix(unix, 0) | ||||
| 	*jt = JSONUnix(t) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // RFC3339NoZ is the time format used in Heat (Orchestration). | ||||
| const RFC3339NoZ = "2006-01-02T15:04:05" | ||||
|  | ||||
| type JSONRFC3339NoZ time.Time | ||||
|  | ||||
| func (jt *JSONRFC3339NoZ) UnmarshalJSON(data []byte) error { | ||||
| 	var s string | ||||
| 	if err := json.Unmarshal(data, &s); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if s == "" { | ||||
| 		return nil | ||||
| 	} | ||||
| 	t, err := time.Parse(RFC3339NoZ, s) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	*jt = JSONRFC3339NoZ(t) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // RFC3339ZNoT is the time format used in Zun (Containers Service). | ||||
| const RFC3339ZNoT = "2006-01-02 15:04:05-07:00" | ||||
|  | ||||
| type JSONRFC3339ZNoT time.Time | ||||
|  | ||||
| func (jt *JSONRFC3339ZNoT) UnmarshalJSON(data []byte) error { | ||||
| 	var s string | ||||
| 	if err := json.Unmarshal(data, &s); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if s == "" { | ||||
| 		return nil | ||||
| 	} | ||||
| 	t, err := time.Parse(RFC3339ZNoT, s) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	*jt = JSONRFC3339ZNoT(t) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // RFC3339ZNoTNoZ is another time format used in Zun (Containers Service). | ||||
| const RFC3339ZNoTNoZ = "2006-01-02 15:04:05" | ||||
|  | ||||
| type JSONRFC3339ZNoTNoZ time.Time | ||||
|  | ||||
| func (jt *JSONRFC3339ZNoTNoZ) UnmarshalJSON(data []byte) error { | ||||
| 	var s string | ||||
| 	if err := json.Unmarshal(data, &s); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if s == "" { | ||||
| 		return nil | ||||
| 	} | ||||
| 	t, err := time.Parse(RFC3339ZNoTNoZ, s) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	*jt = JSONRFC3339ZNoTNoZ(t) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| /* | ||||
| Link is an internal type to be used in packages of collection resources that are | ||||
| paginated in a certain way. | ||||
|  | ||||
| It's a response substructure common to many paginated collection results that is | ||||
| used to point to related pages. Usually, the one we care about is the one with | ||||
| Rel field set to "next". | ||||
| */ | ||||
| type Link struct { | ||||
| 	Href string `json:"href"` | ||||
| 	Rel  string `json:"rel"` | ||||
| } | ||||
|  | ||||
| /* | ||||
| ExtractNextURL is an internal function useful for packages of collection | ||||
| resources that are paginated in a certain way. | ||||
|  | ||||
| It attempts to extract the "next" URL from slice of Link structs, or | ||||
| "" if no such URL is present. | ||||
| */ | ||||
| func ExtractNextURL(links []Link) (string, error) { | ||||
| 	var url string | ||||
|  | ||||
| 	for _, l := range links { | ||||
| 		if l.Rel == "next" { | ||||
| 			url = l.Href | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if url == "" { | ||||
| 		return "", nil | ||||
| 	} | ||||
|  | ||||
| 	return url, nil | ||||
| } | ||||
							
								
								
									
										150
									
								
								vendor/github.com/gophercloud/gophercloud/service_client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								vendor/github.com/gophercloud/gophercloud/service_client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,150 @@ | ||||
| package gophercloud | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| // ServiceClient stores details required to interact with a specific service API implemented by a provider. | ||||
| // Generally, you'll acquire these by calling the appropriate `New` method on a ProviderClient. | ||||
| type ServiceClient struct { | ||||
| 	// ProviderClient is a reference to the provider that implements this service. | ||||
| 	*ProviderClient | ||||
|  | ||||
| 	// Endpoint is the base URL of the service's API, acquired from a service catalog. | ||||
| 	// It MUST end with a /. | ||||
| 	Endpoint string | ||||
|  | ||||
| 	// ResourceBase is the base URL shared by the resources within a service's API. It should include | ||||
| 	// the API version and, like Endpoint, MUST end with a / if set. If not set, the Endpoint is used | ||||
| 	// as-is, instead. | ||||
| 	ResourceBase string | ||||
|  | ||||
| 	// This is the service client type (e.g. compute, sharev2). | ||||
| 	// NOTE: FOR INTERNAL USE ONLY. DO NOT SET. GOPHERCLOUD WILL SET THIS. | ||||
| 	// It is only exported because it gets set in a different package. | ||||
| 	Type string | ||||
|  | ||||
| 	// The microversion of the service to use. Set this to use a particular microversion. | ||||
| 	Microversion string | ||||
|  | ||||
| 	// MoreHeaders allows users (or Gophercloud) to set service-wide headers on requests. Put another way, | ||||
| 	// values set in this field will be set on all the HTTP requests the service client sends. | ||||
| 	MoreHeaders map[string]string | ||||
| } | ||||
|  | ||||
| // ResourceBaseURL returns the base URL of any resources used by this service. It MUST end with a /. | ||||
| func (client *ServiceClient) ResourceBaseURL() string { | ||||
| 	if client.ResourceBase != "" { | ||||
| 		return client.ResourceBase | ||||
| 	} | ||||
| 	return client.Endpoint | ||||
| } | ||||
|  | ||||
| // ServiceURL constructs a URL for a resource belonging to this provider. | ||||
| func (client *ServiceClient) ServiceURL(parts ...string) string { | ||||
| 	return client.ResourceBaseURL() + strings.Join(parts, "/") | ||||
| } | ||||
|  | ||||
| func (client *ServiceClient) initReqOpts(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) { | ||||
| 	if v, ok := (JSONBody).(io.Reader); ok { | ||||
| 		opts.RawBody = v | ||||
| 	} else if JSONBody != nil { | ||||
| 		opts.JSONBody = JSONBody | ||||
| 	} | ||||
|  | ||||
| 	if JSONResponse != nil { | ||||
| 		opts.JSONResponse = JSONResponse | ||||
| 	} | ||||
|  | ||||
| 	if opts.MoreHeaders == nil { | ||||
| 		opts.MoreHeaders = make(map[string]string) | ||||
| 	} | ||||
|  | ||||
| 	if client.Microversion != "" { | ||||
| 		client.setMicroversionHeader(opts) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Get calls `Request` with the "GET" HTTP verb. | ||||
| func (client *ServiceClient) Get(url string, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { | ||||
| 	if opts == nil { | ||||
| 		opts = new(RequestOpts) | ||||
| 	} | ||||
| 	client.initReqOpts(url, nil, JSONResponse, opts) | ||||
| 	return client.Request("GET", url, opts) | ||||
| } | ||||
|  | ||||
| // Post calls `Request` with the "POST" HTTP verb. | ||||
| func (client *ServiceClient) Post(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { | ||||
| 	if opts == nil { | ||||
| 		opts = new(RequestOpts) | ||||
| 	} | ||||
| 	client.initReqOpts(url, JSONBody, JSONResponse, opts) | ||||
| 	return client.Request("POST", url, opts) | ||||
| } | ||||
|  | ||||
| // Put calls `Request` with the "PUT" HTTP verb. | ||||
| func (client *ServiceClient) Put(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { | ||||
| 	if opts == nil { | ||||
| 		opts = new(RequestOpts) | ||||
| 	} | ||||
| 	client.initReqOpts(url, JSONBody, JSONResponse, opts) | ||||
| 	return client.Request("PUT", url, opts) | ||||
| } | ||||
|  | ||||
| // Patch calls `Request` with the "PATCH" HTTP verb. | ||||
| func (client *ServiceClient) Patch(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { | ||||
| 	if opts == nil { | ||||
| 		opts = new(RequestOpts) | ||||
| 	} | ||||
| 	client.initReqOpts(url, JSONBody, JSONResponse, opts) | ||||
| 	return client.Request("PATCH", url, opts) | ||||
| } | ||||
|  | ||||
| // Delete calls `Request` with the "DELETE" HTTP verb. | ||||
| func (client *ServiceClient) Delete(url string, opts *RequestOpts) (*http.Response, error) { | ||||
| 	if opts == nil { | ||||
| 		opts = new(RequestOpts) | ||||
| 	} | ||||
| 	client.initReqOpts(url, nil, nil, opts) | ||||
| 	return client.Request("DELETE", url, opts) | ||||
| } | ||||
|  | ||||
| // Head calls `Request` with the "HEAD" HTTP verb. | ||||
| func (client *ServiceClient) Head(url string, opts *RequestOpts) (*http.Response, error) { | ||||
| 	if opts == nil { | ||||
| 		opts = new(RequestOpts) | ||||
| 	} | ||||
| 	client.initReqOpts(url, nil, nil, opts) | ||||
| 	return client.Request("HEAD", url, opts) | ||||
| } | ||||
|  | ||||
| func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) { | ||||
| 	switch client.Type { | ||||
| 	case "compute": | ||||
| 		opts.MoreHeaders["X-OpenStack-Nova-API-Version"] = client.Microversion | ||||
| 	case "sharev2": | ||||
| 		opts.MoreHeaders["X-OpenStack-Manila-API-Version"] = client.Microversion | ||||
| 	case "volume": | ||||
| 		opts.MoreHeaders["X-OpenStack-Volume-API-Version"] = client.Microversion | ||||
| 	} | ||||
|  | ||||
| 	if client.Type != "" { | ||||
| 		opts.MoreHeaders["OpenStack-API-Version"] = client.Type + " " + client.Microversion | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Request carries out the HTTP operation for the service client | ||||
| func (client *ServiceClient) Request(method, url string, options *RequestOpts) (*http.Response, error) { | ||||
| 	if len(client.MoreHeaders) > 0 { | ||||
| 		if options == nil { | ||||
| 			options = new(RequestOpts) | ||||
| 		} | ||||
| 		for k, v := range client.MoreHeaders { | ||||
| 			options.MoreHeaders[k] = v | ||||
| 		} | ||||
| 	} | ||||
| 	return client.ProviderClient.Request(method, url, options) | ||||
| } | ||||
							
								
								
									
										102
									
								
								vendor/github.com/gophercloud/gophercloud/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								vendor/github.com/gophercloud/gophercloud/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | ||||
| package gophercloud | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/url" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // WaitFor polls a predicate function, once per second, up to a timeout limit. | ||||
| // This is useful to wait for a resource to transition to a certain state. | ||||
| // To handle situations when the predicate might hang indefinitely, the | ||||
| // predicate will be prematurely cancelled after the timeout. | ||||
| // Resource packages will wrap this in a more convenient function that's | ||||
| // specific to a certain resource, but it can also be useful on its own. | ||||
| func WaitFor(timeout int, predicate func() (bool, error)) error { | ||||
| 	type WaitForResult struct { | ||||
| 		Success bool | ||||
| 		Error   error | ||||
| 	} | ||||
|  | ||||
| 	start := time.Now().Unix() | ||||
|  | ||||
| 	for { | ||||
| 		// If a timeout is set, and that's been exceeded, shut it down. | ||||
| 		if timeout >= 0 && time.Now().Unix()-start >= int64(timeout) { | ||||
| 			return fmt.Errorf("A timeout occurred") | ||||
| 		} | ||||
|  | ||||
| 		time.Sleep(1 * time.Second) | ||||
|  | ||||
| 		var result WaitForResult | ||||
| 		ch := make(chan bool, 1) | ||||
| 		go func() { | ||||
| 			defer close(ch) | ||||
| 			satisfied, err := predicate() | ||||
| 			result.Success = satisfied | ||||
| 			result.Error = err | ||||
| 		}() | ||||
|  | ||||
| 		select { | ||||
| 		case <-ch: | ||||
| 			if result.Error != nil { | ||||
| 				return result.Error | ||||
| 			} | ||||
| 			if result.Success { | ||||
| 				return nil | ||||
| 			} | ||||
| 		// If the predicate has not finished by the timeout, cancel it. | ||||
| 		case <-time.After(time.Duration(timeout) * time.Second): | ||||
| 			return fmt.Errorf("A timeout occurred") | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // NormalizeURL is an internal function to be used by provider clients. | ||||
| // | ||||
| // It ensures that each endpoint URL has a closing `/`, as expected by | ||||
| // ServiceClient's methods. | ||||
| func NormalizeURL(url string) string { | ||||
| 	if !strings.HasSuffix(url, "/") { | ||||
| 		return url + "/" | ||||
| 	} | ||||
| 	return url | ||||
| } | ||||
|  | ||||
| // NormalizePathURL is used to convert rawPath to a fqdn, using basePath as | ||||
| // a reference in the filesystem, if necessary. basePath is assumed to contain | ||||
| // either '.' when first used, or the file:// type fqdn of the parent resource. | ||||
| // e.g. myFavScript.yaml => file://opt/lib/myFavScript.yaml | ||||
| func NormalizePathURL(basePath, rawPath string) (string, error) { | ||||
| 	u, err := url.Parse(rawPath) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	// if a scheme is defined, it must be a fqdn already | ||||
| 	if u.Scheme != "" { | ||||
| 		return u.String(), nil | ||||
| 	} | ||||
| 	// if basePath is a url, then child resources are assumed to be relative to it | ||||
| 	bu, err := url.Parse(basePath) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	var basePathSys, absPathSys string | ||||
| 	if bu.Scheme != "" { | ||||
| 		basePathSys = filepath.FromSlash(bu.Path) | ||||
| 		absPathSys = filepath.Join(basePathSys, rawPath) | ||||
| 		bu.Path = filepath.ToSlash(absPathSys) | ||||
| 		return bu.String(), nil | ||||
| 	} | ||||
|  | ||||
| 	absPathSys = filepath.Join(basePath, rawPath) | ||||
| 	u.Path = filepath.ToSlash(absPathSys) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	u.Scheme = "file" | ||||
| 	return u.String(), nil | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user