diff --git a/docs/platform-openstack.md b/docs/platform-openstack.md index 991a713dd..84f44e6bc 100644 --- a/docs/platform-openstack.md +++ b/docs/platform-openstack.md @@ -11,17 +11,7 @@ Supported (tested) versions of the relevant OpenStack APIs are: ## Authentication -LinuxKit's support for OpenStack handles two ways of providing the endpoint and authentication details. You can either set the standard set of environment variables and the commands detailed below will inherit those, or you can explicitly provide them on the command-line as options to `push` and `run`. The examples below use the latter, but if you prefer the former then you'll need to set the following: - -```shell -OS_USERNAME="admin" -OS_PASSWORD="xxx" -OS_TENANT_NAME="linuxkit" -OS_AUTH_URL="https://keystone.com:5000/v3" -OS_USER_DOMAIN_NAME=default -OS_CACERT=/path/to/cacert.pem -OS_INSECURE=false -``` +LinuxKit's support for OpenStack includes configuring access to your cloud as detailed in the official [os-client-config](https://docs.openstack.org/os-client-config/latest/user/configuration.html) documentation. ## Push @@ -40,32 +30,17 @@ Images generated with Moby can be uploaded into OpenStack's image service with ` ```shell ./linuxkit push openstack \ - -authurl=https://keystone.example.com:5000/v3 \ - -username=admin \ - -password=XXXXXXXXXXX \ - -project=linuxkit \ -img-name=LinuxKitTest ./linuxkit.iso ``` -If successful, this will return the image's UUID. If you've set your environment variables up as described above, this command can then be simplified: - -```shell -./linuxkit push openstack \ - -img-name "LinuxKitTest" \ - ~/Desktop/linuxkitmage.qcow2 -``` - ## Run Virtual machines can be launched using `linuxkit run openstack`. As an example: ```shell linuxkit run openstack \ - -authurl https://keystone.example.com:5000/v3 \ - -username=admin \ - -password=xxx \ - -project=linuxkit \ + -flavor=hotdog -keyname=deadline_ed25519 \ -sec-groups=allow_ssh,nginx \ -network c5d02c5f-c625-4539-8aed-1dab3aa85a0a \ diff --git a/src/cmd/linuxkit/push_openstack.go b/src/cmd/linuxkit/push_openstack.go index 68d13c419..deba95b9a 100644 --- a/src/cmd/linuxkit/push_openstack.go +++ b/src/cmd/linuxkit/push_openstack.go @@ -9,9 +9,10 @@ import ( "strings" "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" + "github.com/gophercloud/utils/openstack/clientconfig" + log "github.com/sirupsen/logrus" ) @@ -25,14 +26,7 @@ func pushOpenstack(args []string) { fmt.Printf("Options:\n\n") flags.PrintDefaults() } - authurlFlag := flags.String("authurl", "", "The URL of the OpenStack identity service, i.e https://keystone.example.com:5000/v3") imageName := flags.String("img-name", "", "A unique name for the image, if blank the filename will be used") - passwordFlag := flags.String("password", "", "Password for the specified username") - projectNameFlag := flags.String("project", "", "Name of the Project (aka Tenant) to be used") - userDomainFlag := flags.String("domain", "Default", "Domain name") - usernameFlag := flags.String("username", "", "Username with permissions to upload image") - cacertFlag := flags.String("cacert", "", "CA certificate bundle file") - insecureFlag := flags.Bool("insecure", false, "Disable server certificate verification") if err := flags.Parse(args); err != nil { log.Fatal("Unable to parse args") @@ -48,41 +42,15 @@ func pushOpenstack(args []string) { // Check that the file both exists, and can be read checkFile(filePath) - authurl := getStringValue(authurlVar, *authurlFlag, "") - password := getStringValue(passwordVar, *passwordFlag, "") - projectName := getStringValue(projectNameVar, *projectNameFlag, "") - userDomain := getStringValue(userDomainVar, *userDomainFlag, "") - username := getStringValue(usernameVar, *usernameFlag, "") - cacert := getStringValue(cacertVar, *cacertFlag, "") - insecure := getBoolValue(insecureVar, *insecureFlag) - - authOpts := gophercloud.AuthOptions{ - DomainName: userDomain, - IdentityEndpoint: authurl, - Password: password, - TenantName: projectName, - Username: username, - } - - provider, err := openstack.NewClient(authOpts.IdentityEndpoint) + client, err := clientconfig.NewServiceClient("image", nil) if err != nil { - log.Fatalf("Failed to connect to OpenStack: %s", err) + log.Fatalf("Error connecting to your OpenStack cloud: %s", err) } - provider.HTTPClient, err = openstackHTTPClient(cacert, insecure) - if err != nil { - log.Fatalf("Failed to authenticate with OpenStack: %s", err) - } - - err = openstack.Authenticate(provider, authOpts) - if err != nil { - log.Fatalf("Failed to authenticate with OpenStack: %s", err) - } - - createOpenStackImage(filePath, *imageName, provider) + createOpenStackImage(filePath, *imageName, client) } -func createOpenStackImage(filePath string, imageName string, provider *gophercloud.ProviderClient) { +func createOpenStackImage(filePath string, imageName string, client *gophercloud.ServiceClient) { // Image formats that are supported by both LinuxKit and OpenStack Glance V2 formats := []string{"ami", "vhd", "vhdx", "vmdk", "raw", "qcow2", "iso"} @@ -106,10 +74,6 @@ func createOpenStackImage(filePath string, imageName string, provider *gopherclo imageName = fileName } - client, err := openstack.NewImageServiceV2(provider, gophercloud.EndpointOpts{}) - if err != nil { - log.Fatalf("Unable to create Image V2 client: %s", err) - } imageOpts := images.CreateOpts{ Name: imageName, ContainerFormat: "bare", diff --git a/src/cmd/linuxkit/run_openstack.go b/src/cmd/linuxkit/run_openstack.go index 7ec1583bb..8f28c0ad9 100644 --- a/src/cmd/linuxkit/run_openstack.go +++ b/src/cmd/linuxkit/run_openstack.go @@ -1,57 +1,23 @@ package main import ( - "crypto/tls" - "crypto/x509" - "errors" "flag" "fmt" - "io/ioutil" - "net/http" "os" "path/filepath" "strings" - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + "github.com/gophercloud/utils/openstack/clientconfig" + log "github.com/sirupsen/logrus" ) const ( defaultOSFlavor = "m1.tiny" - authurlVar = "OS_AUTH_URL" - usernameVar = "OS_USERNAME" - passwordVar = "OS_PASSWORD" - projectNameVar = "OS_PROJECT_NAME" - userDomainVar = "OS_USER_DOMAIN_NAME" - cacertVar = "OS_CACERT" - insecureVar = "OS_INSECURE" ) -func openstackHTTPClient(cacert string, insecure bool) (http.Client, error) { - if cacert == "" { - return http.Client{}, nil - } - - caCertPool := x509.NewCertPool() - caCert, err := ioutil.ReadFile(cacert) - if err != nil { - return http.Client{}, errors.New("Can't read certificate file") - } - caCertPool.AppendCertsFromPEM(caCert) - - tlsConfig := &tls.Config{ - RootCAs: caCertPool, - InsecureSkipVerify: insecure, - } - tlsConfig.BuildNameToCertificate() - transport := &http.Transport{TLSClientConfig: tlsConfig} - - return http.Client{Transport: transport}, nil -} - func runOpenStack(args []string) { flags := flag.NewFlagSet("openstack", flag.ExitOnError) invoked := filepath.Base(os.Args[0]) @@ -62,18 +28,11 @@ func runOpenStack(args []string) { fmt.Printf("Options:\n\n") flags.PrintDefaults() } - authurlFlag := flags.String("authurl", "", "The URL of the OpenStack identity service, i.e https://keystone.example.com:5000/v3") flavorName := flags.String("flavor", defaultOSFlavor, "Instance size (flavor)") instanceName := flags.String("instancename", "", "Name of instance. Defaults to the name of the image if not specified") networkID := flags.String("network", "", "The ID of the network to attach the instance to") - secGroups := flags.String("sec-groups", "", "Security Group names separated by comma") + secGroups := flags.String("sec-groups", "default", "Security Group names separated by comma") keyName := flags.String("keyname", "", "The name of the SSH keypair to associate with the instance") - passwordFlag := flags.String("password", "", "Password for the specified username") - projectNameFlag := flags.String("project", "", "Name of the Project (aka Tenant) to be used") - userDomainFlag := flags.String("domain", "Default", "Domain name") - usernameFlag := flags.String("username", "", "Username with permissions to create an instance") - cacertFlag := flags.String("cacert", "", "CA certificate bundle file") - insecureFlag := flags.Bool("insecure", false, "Disable server certificate verification") if err := flags.Parse(args); err != nil { log.Fatal("Unable to parse args") @@ -91,40 +50,9 @@ func runOpenStack(args []string) { *instanceName = name } - authurl := getStringValue(authurlVar, *authurlFlag, "") - password := getStringValue(passwordVar, *passwordFlag, "") - projectName := getStringValue(projectNameVar, *projectNameFlag, "") - userDomain := getStringValue(userDomainVar, *userDomainFlag, "") - username := getStringValue(usernameVar, *usernameFlag, "") - cacert := getStringValue(cacertVar, *cacertFlag, "") - insecure := getBoolValue(insecureVar, *insecureFlag) - - authOpts := gophercloud.AuthOptions{ - DomainName: userDomain, - IdentityEndpoint: authurl, - Password: password, - TenantName: projectName, - Username: username, - } - - provider, err := openstack.NewClient(authOpts.IdentityEndpoint) + client, err := clientconfig.NewServiceClient("compute", nil) if err != nil { - log.Fatalf("Failed to connect to OpenStack: %s", err) - } - - provider.HTTPClient, err = openstackHTTPClient(cacert, insecure) - if err != nil { - log.Fatalf("Failed to authenticate with OpenStack: %s", err) - } - - err = openstack.Authenticate(provider, authOpts) - if err != nil { - log.Fatalf("Failed to authenticate with OpenStack: %s", err) - } - - client, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{}) - if err != nil { - log.Fatalf("Unable to create Compute V2 client, %s", err) + log.Fatalf("Unable to create Compute client, %s", err) } network := servers.Network{ diff --git a/src/cmd/linuxkit/vendor.conf b/src/cmd/linuxkit/vendor.conf index 16cfc2e84..4485429d3 100644 --- a/src/cmd/linuxkit/vendor.conf +++ b/src/cmd/linuxkit/vendor.conf @@ -26,7 +26,8 @@ github.com/gogo/protobuf v1.0.0 github.com/golang/protobuf v1.1.0 github.com/google/uuid 7e072fc3a7be179aee6d3359e46015aa8c995314 github.com/googleapis/gax-go 8c5154c0fe5bf18cf649634d4c6df50897a32751 -github.com/gophercloud/gophercloud 2804b72cf099b41d2e25c8afcca786f9f962ddee +github.com/gophercloud/gophercloud b9ea9cb68cf5803ea1567c404b549a783c8264b2 +github.com/gophercloud/utils 34f5991525d116b3832e0d9409492274f1c06bda github.com/gorilla/context v1.1 github.com/gorilla/mux v1.1 github.com/gorilla/websocket 21ab95fa12b9bdd8fecf5fa3586aad941cc98785 diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/README.md b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/README.md index 60ca479de..ad29041d9 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/README.md +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/README.md @@ -127,7 +127,7 @@ new resource in the `server` variable (a ## Advanced Usage -Have a look at the [FAQ](./FAQ.md) for some tips on customizing the way Gophercloud works. +Have a look at the [FAQ](./docs/FAQ.md) for some tips on customizing the way Gophercloud works. ## Backwards-Compatibility Guarantees @@ -140,4 +140,20 @@ See the [contributing guide](./.github/CONTRIBUTING.md). ## Help and feedback If you're struggling with something or have spotted a potential bug, feel free -to submit an issue to our [bug tracker](/issues). +to submit an issue to our [bug tracker](https://github.com/gophercloud/gophercloud/issues). + +## Thank You + +We'd like to extend special thanks and appreciation to the following: + +### OpenLab + + + +OpenLab is providing a full CI environment to test each PR and merge for a variety of OpenStack releases. + +### VEXXHOST + + + +VEXXHOST is providing their services to assist with the development and testing of Gophercloud. diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/auth_options.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/auth_options.go index 19c08341a..f28c8d2ad 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/auth_options.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/auth_options.go @@ -9,12 +9,32 @@ 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 @@ -39,7 +59,7 @@ type AuthOptions struct { // 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 to the TenantID + // accomplish that, the ProjectID will need to be provided as the TenantID // option. TenantID string `json:"tenantId,omitempty"` TenantName string `json:"tenantName,omitempty"` @@ -50,15 +70,34 @@ type AuthOptions struct { // 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 + // 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 @@ -109,7 +148,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s type userReq struct { ID *string `json:"id,omitempty"` Name *string `json:"name,omitempty"` - Password string `json:"password"` + Password string `json:"password,omitempty"` Domain *domainReq `json:"domain,omitempty"` } @@ -121,10 +160,18 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s 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"` + Methods []string `json:"methods"` + Password *passwordReq `json:"password,omitempty"` + Token *tokenReq `json:"token,omitempty"` + ApplicationCredential *applicationCredentialReq `json:"application_credential,omitempty"` } type authReq struct { @@ -138,6 +185,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s // Populate the request structure based on the provided arguments. Create and return an error // if insufficient or incompatible information is present. var req request + var userRequest userReq if opts.Password == "" { if opts.TokenID != "" { @@ -161,8 +209,49 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s 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{} + } + // make sure that only one of DomainName or DomainID were provided + if opts.DomainID == "" && opts.DomainName == "" { + return nil, ErrDomainIDOrDomainName{} + } + req.Auth.Identity.Methods = []string{"application_credential"} + if opts.DomainID != "" { + userRequest = userReq{ + Name: &opts.Username, + Domain: &domainReq{ID: &opts.DomainID}, + } + } else if opts.DomainName != "" { + userRequest = userReq{ + Name: &opts.Username, + Domain: &domainReq{Name: &opts.DomainName}, + } + } + req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{ + Name: &opts.ApplicationCredentialName, + User: &userRequest, + Secret: &opts.ApplicationCredentialSecret, + } } else { - // If no password or token ID are available, authentication can't continue. + // If no password or token ID or ApplicationCredential are available, authentication can't continue. return nil, ErrMissingPassword{} } } else { @@ -241,82 +330,85 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s } func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { - - var scope struct { - ProjectID string - ProjectName string - DomainID string - DomainName string - } - - if opts.TenantID != "" { - scope.ProjectID = opts.TenantID - } else { - if opts.TenantName != "" { - scope.ProjectName = opts.TenantName - scope.DomainID = opts.DomainID - scope.DomainName = opts.DomainName + // 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 scope.ProjectName != "" { + if opts.Scope.ProjectName != "" { // ProjectName provided: either DomainID or DomainName must also be supplied. // ProjectID may not be supplied. - if scope.DomainID == "" && scope.DomainName == "" { + if opts.Scope.DomainID == "" && opts.Scope.DomainName == "" { return nil, ErrScopeDomainIDOrDomainName{} } - if scope.ProjectID != "" { + if opts.Scope.ProjectID != "" { return nil, ErrScopeProjectIDOrProjectName{} } - if scope.DomainID != "" { + if opts.Scope.DomainID != "" { // ProjectName + DomainID return map[string]interface{}{ "project": map[string]interface{}{ - "name": &scope.ProjectName, - "domain": map[string]interface{}{"id": &scope.DomainID}, + "name": &opts.Scope.ProjectName, + "domain": map[string]interface{}{"id": &opts.Scope.DomainID}, }, }, nil } - if scope.DomainName != "" { + if opts.Scope.DomainName != "" { // ProjectName + DomainName return map[string]interface{}{ "project": map[string]interface{}{ - "name": &scope.ProjectName, - "domain": map[string]interface{}{"name": &scope.DomainName}, + "name": &opts.Scope.ProjectName, + "domain": map[string]interface{}{"name": &opts.Scope.DomainName}, }, }, nil } - } else if scope.ProjectID != "" { + } else if opts.Scope.ProjectID != "" { // ProjectID provided. ProjectName, DomainID, and DomainName may not be provided. - if scope.DomainID != "" { + if opts.Scope.DomainID != "" { return nil, ErrScopeProjectIDAlone{} } - if scope.DomainName != "" { + if opts.Scope.DomainName != "" { return nil, ErrScopeProjectIDAlone{} } // ProjectID return map[string]interface{}{ "project": map[string]interface{}{ - "id": &scope.ProjectID, + "id": &opts.Scope.ProjectID, }, }, nil - } else if scope.DomainID != "" { + } else if opts.Scope.DomainID != "" { // DomainID provided. ProjectID, ProjectName, and DomainName may not be provided. - if scope.DomainName != "" { + if opts.Scope.DomainName != "" { return nil, ErrScopeDomainIDOrDomainName{} } // DomainID return map[string]interface{}{ "domain": map[string]interface{}{ - "id": &scope.DomainID, + "id": &opts.Scope.DomainID, + }, + }, nil + } else if opts.Scope.DomainName != "" { + // DomainName + return map[string]interface{}{ + "domain": map[string]interface{}{ + "name": &opts.Scope.DomainName, }, }, nil - } else if scope.DomainName != "" { - return nil, ErrScopeDomainName{} } return nil, nil diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/doc.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/doc.go index b559516f9..131cc8e30 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/doc.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/doc.go @@ -3,11 +3,17 @@ Package gophercloud provides a multi-vendor interface to OpenStack-compatible clouds. The library has a three-level hierarchy: providers, services, and resources. -Provider structs represent the service providers that offer and manage a -collection of services. The IdentityEndpoint is typically refered to as -"auth_url" in information provided by the cloud operator. Additionally, -the cloud may refer to TenantID or TenantName as project_id and project_name. -These are defined like so: +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", @@ -18,6 +24,16 @@ These are defined like so: 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 @@ -25,7 +41,9 @@ pass in the parent provider, like so: opts := gophercloud.EndpointOpts{Region: "RegionOne"} - client := openstack.NewComputeV2(provider, opts) + 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: @@ -62,6 +80,12 @@ of results: 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. diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/endpoint_search.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/endpoint_search.go index 9887947f6..2fbc3c97f 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/endpoint_search.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/endpoint_search.go @@ -27,7 +27,7 @@ const ( // unambiguously identify one, and only one, endpoint within the catalog. // // Usually, these are passed to service client factory functions in a provider -// package, like "rackspace.NewComputeV2()". +// 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 diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/errors.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/errors.go index e0fe7c1e0..4bf102468 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/errors.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/errors.go @@ -1,6 +1,9 @@ package gophercloud -import "fmt" +import ( + "fmt" + "strings" +) // BaseError is an error type that all other error types embed. type BaseError struct { @@ -43,6 +46,33 @@ func (e ErrInvalidInput) Error() string { 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 { @@ -72,6 +102,11 @@ 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 @@ -103,11 +138,22 @@ type ErrDefault503 struct { } func (e ErrDefault400) Error() string { - return "Invalid request due to incorrect syntax or missing required parameters." + 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" } @@ -141,6 +187,12 @@ 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 { @@ -393,16 +445,16 @@ func (e ErrScopeProjectIDAlone) Error() string { return "ProjectID must be supplied alone in a Scope" } -// ErrScopeDomainName indicates that a DomainName was provided alone in a Scope. -type ErrScopeDomainName struct{ BaseError } - -func (e ErrScopeDomainName) Error() string { - return "DomainName must be supplied with a ProjectName or ProjectID 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" +} diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go index f6d2eb194..33c9aec44 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go @@ -8,10 +8,27 @@ import ( 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 and OS_TENANT_NAME are optional. +/* +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") @@ -21,31 +38,60 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { 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.ErrMissingInput{Argument: "authURL"} + err := gophercloud.ErrMissingEnvironmentVariable{ + EnvironmentVariable: "OS_AUTH_URL", + } return nilOptions, err } if username == "" && userID == "" { - err := gophercloud.ErrMissingInput{Argument: "username"} + err := gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ + EnvironmentVariables: []string{"OS_USERNAME", "OS_USERID"}, + } return nilOptions, err } - if password == "" { - err := gophercloud.ErrMissingInput{Argument: "password"} + 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 } ao := gophercloud.AuthOptions{ - IdentityEndpoint: authURL, - UserID: userID, - Username: username, - Password: password, - TenantID: tenantID, - TenantName: tenantName, - DomainID: domainID, - DomainName: domainName, + 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 diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/client.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/client.go index 09120e8fa..6d93af5b4 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/client.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/client.go @@ -2,7 +2,6 @@ package openstack import ( "fmt" - "net/url" "reflect" "github.com/gophercloud/gophercloud" @@ -12,43 +11,66 @@ import ( ) const ( - v20 = "v2.0" - v30 = "v3.0" + // 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. +/* +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) { - u, err := url.Parse(endpoint) + base, err := utils.BaseEndpoint(endpoint) if err != nil { return nil, err } - hadPath := u.Path != "" - u.Path, u.RawQuery, u.Fragment = "", "", "" - base := u.String() endpoint = gophercloud.NormalizeURL(endpoint) base = gophercloud.NormalizeURL(base) - if hadPath { - return &gophercloud.ProviderClient{ - IdentityBase: base, - IdentityEndpoint: endpoint, - }, nil - } + p := new(gophercloud.ProviderClient) + p.IdentityBase = base + p.IdentityEndpoint = endpoint + p.UseTokenLock() - return &gophercloud.ProviderClient{ - IdentityBase: base, - IdentityEndpoint: "", - }, nil + return p, nil } -// AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint specified by options, acquires a token, and -// returns a Client instance that's ready to operate. -// It first queries the root identity endpoint to determine which versions of the identity service are supported, then chooses -// the most recent identity service available to proceed. +/* +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 { @@ -62,11 +84,12 @@ func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.Provider return client, nil } -// Authenticate or re-authenticate against the most recent identity service supported at the provided endpoint. +// 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: v20, Priority: 20, Suffix: "/v2.0/"}, - {ID: v30, Priority: 30, Suffix: "/v3/"}, + {ID: v2, Priority: 20, Suffix: "/v2.0/"}, + {ID: v3, Priority: 30, Suffix: "/v3/"}, } chosen, endpoint, err := utils.ChooseVersion(client, versions) @@ -75,9 +98,9 @@ func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOp } switch chosen.ID { - case v20: + case v2: return v2auth(client, endpoint, options, gophercloud.EndpointOpts{}) - case v30: + case v3: return v3auth(client, endpoint, &options, gophercloud.EndpointOpts{}) default: // The switch statement must be out of date from the versions list. @@ -123,9 +146,21 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc } 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.ReauthFunc = nil + tac.TokenID = "" + tao := options + tao.AllowReauth = false client.ReauthFunc = func() error { - client.TokenID = "" - return v2auth(client, endpoint, options, eo) + err := v2auth(&tac, endpoint, tao, eo) + if err != nil { + return err + } + client.TokenID = tac.TokenID + return nil } } client.TokenID = token.ID @@ -167,9 +202,32 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au 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.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 { - client.TokenID = "" - return v3auth(client, endpoint, opts, eo) + 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) { @@ -179,7 +237,8 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au return nil } -// NewIdentityV2 creates a ServiceClient that may be used to interact with the v2 identity service. +// 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" @@ -199,7 +258,8 @@ func NewIdentityV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOp }, nil } -// NewIdentityV3 creates a ServiceClient that may be used to access the v3 identity service. +// 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" @@ -212,6 +272,19 @@ func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOp } } + // 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, @@ -232,33 +305,43 @@ func initClientOpts(client *gophercloud.ProviderClient, eo gophercloud.EndpointO return sc, nil } -// NewObjectStorageV1 creates a ServiceClient that may be used with the v1 object storage package. +// 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. +// 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. +// 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. +// 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. +// 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") @@ -270,7 +353,8 @@ func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) ( return initClientOpts(client, eo, "cdn") } -// NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 orchestration service. +// 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") } @@ -280,16 +364,64 @@ func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (* return initClientOpts(client, eo, "database") } -// NewDNSV2 creates a ServiceClient that may be used to access the v2 DNS service. +// 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. +// 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") +} diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go index 856f41bac..24c460772 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go @@ -1,3 +1,71 @@ -// Package keypairs provides information and interaction with the Keypairs -// extension for the OpenStack Compute service. +/* +Package keypairs provides the ability to manage key pairs as well as create +servers with a specified key pair. + +Example to List Key Pairs + + allPages, err := keypairs.List(computeClient).AllPages() + if err != nil { + panic(err) + } + + allKeyPairs, err := keypairs.ExtractKeyPairs(allPages) + if err != nil { + panic(err) + } + + for _, kp := range allKeyPairs { + fmt.Printf("%+v\n", kp) + } + +Example to Create a Key Pair + + createOpts := keypairs.CreateOpts{ + Name: "keypair-name", + } + + keypair, err := keypairs.Create(computeClient, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v", keypair) + +Example to Import a Key Pair + + createOpts := keypairs.CreateOpts{ + Name: "keypair-name", + PublicKey: "public-key", + } + + keypair, err := keypairs.Create(computeClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Key Pair + + err := keypairs.Delete(computeClient, "keypair-name").ExtractErr() + if err != nil { + panic(err) + } + +Example to Create a Server With a Key Pair + + serverCreateOpts := servers.CreateOpts{ + Name: "server_name", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", + } + + createOpts := keypairs.CreateOptsExt{ + CreateOptsBuilder: serverCreateOpts, + KeyName: "keypair-name", + } + + server, err := servers.Create(computeClient, createOpts).Extract() + if err != nil { + panic(err) + } +*/ package keypairs diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go index adf1e5596..4e5e499e3 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go @@ -9,11 +9,12 @@ import ( // CreateOptsExt adds a KeyPair option to the base CreateOpts. type CreateOptsExt struct { servers.CreateOptsBuilder + + // KeyName is the name of the key pair. KeyName string `json:"key_name,omitempty"` } -// ToServerCreateMap adds the key_name and, optionally, key_data options to -// the base server creation options. +// ToServerCreateMap adds the key_name to the base server creation options. func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) { base, err := opts.CreateOptsBuilder.ToServerCreateMap() if err != nil { @@ -37,18 +38,19 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { }) } -// CreateOptsBuilder describes struct types that can be accepted by the Create call. Notable, the -// CreateOpts struct in this package does. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToKeyPairCreateMap() (map[string]interface{}, error) } -// CreateOpts specifies keypair creation or import parameters. +// CreateOpts specifies KeyPair creation or import parameters. type CreateOpts struct { // Name is a friendly name to refer to this KeyPair in other services. Name string `json:"name" required:"true"` - // PublicKey [optional] is a pregenerated OpenSSH-formatted public key. If provided, this key - // will be imported and no new key will be created. + + // PublicKey [optional] is a pregenerated OpenSSH-formatted public key. + // If provided, this key will be imported and no new key will be created. PublicKey string `json:"public_key,omitempty"` } @@ -57,8 +59,8 @@ func (opts CreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "keypair") } -// Create requests the creation of a new keypair on the server, or to import a pre-existing -// keypair. +// Create requests the creation of a new KeyPair on the server, or to import a +// pre-existing keypair. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToKeyPairCreateMap() if err != nil { diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/results.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/results.go index 4c785a24c..2d71034b1 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/results.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/results.go @@ -5,29 +5,33 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// KeyPair is an SSH key known to the OpenStack Cloud that is available to be injected into -// servers. +// KeyPair is an SSH key known to the OpenStack Cloud that is available to be +// injected into servers. type KeyPair struct { - // Name is used to refer to this keypair from other services within this region. + // Name is used to refer to this keypair from other services within this + // region. Name string `json:"name"` - // Fingerprint is a short sequence of bytes that can be used to authenticate or validate a longer - // public key. + // Fingerprint is a short sequence of bytes that can be used to authenticate + // or validate a longer public key. Fingerprint string `json:"fingerprint"` - // PublicKey is the public key from this pair, in OpenSSH format. "ssh-rsa AAAAB3Nz..." + // PublicKey is the public key from this pair, in OpenSSH format. + // "ssh-rsa AAAAB3Nz..." PublicKey string `json:"public_key"` // PrivateKey is the private key from this pair, in PEM format. - // "-----BEGIN RSA PRIVATE KEY-----\nMIICXA..." It is only present if this keypair was just - // returned from a Create call + // "-----BEGIN RSA PRIVATE KEY-----\nMIICXA..." + // It is only present if this KeyPair was just returned from a Create call. PrivateKey string `json:"private_key"` - // UserID is the user who owns this keypair. + // UserID is the user who owns this KeyPair. UserID string `json:"user_id"` } -// KeyPairPage stores a single, only page of KeyPair results from a List call. +// KeyPairPage stores a single page of all KeyPair results from a List call. +// Use the ExtractKeyPairs function to convert the results to a slice of +// KeyPairs. type KeyPairPage struct { pagination.SinglePageBase } @@ -58,7 +62,8 @@ type keyPairResult struct { gophercloud.Result } -// Extract is a method that attempts to interpret any KeyPair resource response as a KeyPair struct. +// Extract is a method that attempts to interpret any KeyPair resource response +// as a KeyPair struct. func (r keyPairResult) Extract() (*KeyPair, error) { var s struct { KeyPair *KeyPair `json:"keypair"` @@ -67,20 +72,20 @@ func (r keyPairResult) Extract() (*KeyPair, error) { return s.KeyPair, err } -// CreateResult is the response from a Create operation. Call its Extract method to interpret it -// as a KeyPair. +// CreateResult is the response from a Create operation. Call its Extract method +// to interpret it as a KeyPair. type CreateResult struct { keyPairResult } -// GetResult is the response from a Get operation. Call its Extract method to interpret it -// as a KeyPair. +// GetResult is the response from a Get operation. Call its Extract method to +// interpret it as a KeyPair. type GetResult struct { keyPairResult } -// DeleteResult is the response from a Delete operation. Call its Extract method to determine if -// the call succeeded or failed. +// DeleteResult is the response from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/doc.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/doc.go index 5822e1bcf..34d8764fa 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/doc.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/doc.go @@ -1,7 +1,137 @@ -// Package flavors provides information and interaction with the flavor API -// resource in the OpenStack Compute service. -// -// A flavor is an available hardware configuration for a server. Each flavor -// has a unique combination of disk space, memory capacity and priority for CPU -// time. +/* +Package flavors provides information and interaction with the flavor API +in the OpenStack Compute service. + +A flavor is an available hardware configuration for a server. Each flavor +has a unique combination of disk space, memory capacity and priority for CPU +time. + +Example to List Flavors + + listOpts := flavors.ListOpts{ + AccessType: flavors.PublicAccess, + } + + allPages, err := flavors.ListDetail(computeClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allFlavors, err := flavors.ExtractFlavors(allPages) + if err != nil { + panic(err) + } + + for _, flavor := range allFlavors { + fmt.Printf("%+v\n", flavor) + } + +Example to Create a Flavor + + createOpts := flavors.CreateOpts{ + ID: "1", + Name: "m1.tiny", + Disk: gophercloud.IntToPointer(1), + RAM: 512, + VCPUs: 1, + RxTxFactor: 1.0, + } + + flavor, err := flavors.Create(computeClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to List Flavor Access + + flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + + allPages, err := flavors.ListAccesses(computeClient, flavorID).AllPages() + if err != nil { + panic(err) + } + + allAccesses, err := flavors.ExtractAccesses(allPages) + if err != nil { + panic(err) + } + + for _, access := range allAccesses { + fmt.Printf("%+v", access) + } + +Example to Grant Access to a Flavor + + flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + + accessOpts := flavors.AddAccessOpts{ + Tenant: "15153a0979884b59b0592248ef947921", + } + + accessList, err := flavors.AddAccess(computeClient, flavor.ID, accessOpts).Extract() + if err != nil { + panic(err) + } + +Example to Remove/Revoke Access to a Flavor + + flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + + accessOpts := flavors.RemoveAccessOpts{ + Tenant: "15153a0979884b59b0592248ef947921", + } + + accessList, err := flavors.RemoveAccess(computeClient, flavor.ID, accessOpts).Extract() + if err != nil { + panic(err) + } + +Example to Create Extra Specs for a Flavor + + flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + + createOpts := flavors.ExtraSpecsOpts{ + "hw:cpu_policy": "CPU-POLICY", + "hw:cpu_thread_policy": "CPU-THREAD-POLICY", + } + createdExtraSpecs, err := flavors.CreateExtraSpecs(computeClient, flavorID, createOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v", createdExtraSpecs) + +Example to Get Extra Specs for a Flavor + + flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + + extraSpecs, err := flavors.ListExtraSpecs(computeClient, flavorID).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v", extraSpecs) + +Example to Update Extra Specs for a Flavor + + flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + + updateOpts := flavors.ExtraSpecsOpts{ + "hw:cpu_thread_policy": "CPU-THREAD-POLICY-UPDATED", + } + updatedExtraSpec, err := flavors.UpdateExtraSpec(computeClient, flavorID, updateOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("%+v", updatedExtraSpec) + +Example to Delete an Extra Spec for a Flavor + + flavorID := "e91758d6-a54a-4778-ad72-0c73a1cb695b" + err := flavors.DeleteExtraSpec(computeClient, flavorID, "hw:cpu_thread_policy").ExtractErr() + if err != nil { + panic(err) + } +*/ package flavors diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go index 03d7e8724..539019e90 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go @@ -11,45 +11,65 @@ type ListOptsBuilder interface { ToFlavorListQuery() (string, error) } -// AccessType maps to OpenStack's Flavor.is_public field. Although the is_public field is boolean, the -// request options are ternary, which is why AccessType is a string. The following values are -// allowed: -// -// PublicAccess (the default): Returns public flavors and private flavors associated with that project. -// PrivateAccess (admin only): Returns private flavors, across all projects. -// AllAccess (admin only): Returns public and private flavors across all projects. -// -// The AccessType arguement is optional, and if it is not supplied, OpenStack returns the PublicAccess -// flavors. +/* + AccessType maps to OpenStack's Flavor.is_public field. Although the is_public + field is boolean, the request options are ternary, which is why AccessType is + a string. The following values are allowed: + + The AccessType arguement is optional, and if it is not supplied, OpenStack + returns the PublicAccess flavors. +*/ type AccessType string const ( - PublicAccess AccessType = "true" + // PublicAccess returns public flavors and private flavors associated with + // that project. + PublicAccess AccessType = "true" + + // PrivateAccess (admin only) returns private flavors, across all projects. PrivateAccess AccessType = "false" - AllAccess AccessType = "None" + + // AllAccess (admin only) returns public and private flavors across all + // projects. + AllAccess AccessType = "None" ) -// ListOpts helps control the results returned by the List() function. -// For example, a flavor with a minDisk field of 10 will not be returned if you specify MinDisk set to 20. -// Typically, software will use the last ID of the previous call to List to set the Marker for the current call. -type ListOpts struct { +/* + ListOpts filters the results returned by the List() function. + For example, a flavor with a minDisk field of 10 will not be returned if you + specify MinDisk set to 20. - // ChangesSince, if provided, instructs List to return only those things which have changed since the timestamp provided. + Typically, software will use the last ID of the previous call to List to set + the Marker for the current call. +*/ +type ListOpts struct { + // ChangesSince, if provided, instructs List to return only those things which + // have changed since the timestamp provided. ChangesSince string `q:"changes-since"` - // MinDisk and MinRAM, if provided, elides flavors which do not meet your criteria. + // MinDisk and MinRAM, if provided, elides flavors which do not meet your + // criteria. MinDisk int `q:"minDisk"` MinRAM int `q:"minRam"` + // SortDir allows to select sort direction. + // It can be "asc" or "desc" (default). + SortDir string `q:"sort_dir"` + + // SortKey allows to sort by one of the flavors attributes. + // Default is flavorid. + SortKey string `q:"sort_key"` + // Marker and Limit control paging. // Marker instructs List where to start listing from. Marker string `q:"marker"` - // Limit instructs List to refrain from sending excessively large lists of flavors. + // Limit instructs List to refrain from sending excessively large lists of + // flavors. Limit int `q:"limit"` - // AccessType, if provided, instructs List which set of flavors to return. If IsPublic not provided, - // flavors for the current project are returned. + // AccessType, if provided, instructs List which set of flavors to return. + // If IsPublic not provided, flavors for the current project are returned. AccessType AccessType `q:"is_public"` } @@ -60,8 +80,8 @@ func (opts ListOpts) ToFlavorListQuery() (string, error) { } // ListDetail instructs OpenStack to provide a list of flavors. -// You may provide criteria by which List curtails its results for easier processing. -// See ListOpts for more details. +// You may provide criteria by which List curtails its results for easier +// processing. func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(client) if opts != nil { @@ -80,31 +100,42 @@ type CreateOptsBuilder interface { ToFlavorCreateMap() (map[string]interface{}, error) } -// CreateOpts is passed to Create to create a flavor -// Source: -// https://github.com/openstack/nova/blob/stable/newton/nova/api/openstack/compute/schemas/flavor_manage.py#L20 +// CreateOpts specifies parameters used for creating a flavor. type CreateOpts struct { + // Name is the name of the flavor. Name string `json:"name" required:"true"` - // memory size, in MBs - RAM int `json:"ram" required:"true"` + + // RAM is the memory of the flavor, measured in MB. + RAM int `json:"ram" required:"true"` + + // VCPUs is the number of vcpus for the flavor. VCPUs int `json:"vcpus" required:"true"` - // disk size, in GBs - Disk *int `json:"disk" required:"true"` - ID string `json:"id,omitempty"` - // non-zero, positive - Swap *int `json:"swap,omitempty"` + + // Disk the amount of root disk space, measured in GB. + Disk *int `json:"disk" required:"true"` + + // ID is a unique ID for the flavor. + ID string `json:"id,omitempty"` + + // Swap is the amount of swap space for the flavor, measured in MB. + Swap *int `json:"swap,omitempty"` + + // RxTxFactor alters the network bandwidth of a flavor. RxTxFactor float64 `json:"rxtx_factor,omitempty"` - IsPublic *bool `json:"os-flavor-access:is_public,omitempty"` - // ephemeral disk size, in GBs, non-zero, positive + + // IsPublic flags a flavor as being available to all projects or not. + IsPublic *bool `json:"os-flavor-access:is_public,omitempty"` + + // Ephemeral is the amount of ephemeral disk space, measured in GB. Ephemeral *int `json:"OS-FLV-EXT-DATA:ephemeral,omitempty"` } -// ToFlavorCreateMap satisfies the CreateOptsBuilder interface -func (opts *CreateOpts) ToFlavorCreateMap() (map[string]interface{}, error) { +// ToFlavorCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToFlavorCreateMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "flavor") } -// Create a flavor +// Create requests the creation of a new flavor. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToFlavorCreateMap() if err != nil { @@ -117,14 +148,177 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } -// Get instructs OpenStack to provide details on a single flavor, identified by its ID. -// Use ExtractFlavor to convert its result into a Flavor. +// Get retrieves details of a single flavor. Use ExtractFlavor to convert its +// result into a Flavor. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } -// IDFromName is a convienience function that returns a flavor's ID given its name. +// Delete deletes the specified flavor ID. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} + +// ListAccesses retrieves the tenants which have access to a flavor. +func ListAccesses(client *gophercloud.ServiceClient, id string) pagination.Pager { + url := accessURL(client, id) + + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return AccessPage{pagination.SinglePageBase(r)} + }) +} + +// AddAccessOptsBuilder allows extensions to add additional parameters to the +// AddAccess requests. +type AddAccessOptsBuilder interface { + ToFlavorAddAccessMap() (map[string]interface{}, error) +} + +// AddAccessOpts represents options for adding access to a flavor. +type AddAccessOpts struct { + // Tenant is the project/tenant ID to grant access. + Tenant string `json:"tenant"` +} + +// ToFlavorAddAccessMap constructs a request body from AddAccessOpts. +func (opts AddAccessOpts) ToFlavorAddAccessMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "addTenantAccess") +} + +// AddAccess grants a tenant/project access to a flavor. +func AddAccess(client *gophercloud.ServiceClient, id string, opts AddAccessOptsBuilder) (r AddAccessResult) { + b, err := opts.ToFlavorAddAccessMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// RemoveAccessOptsBuilder allows extensions to add additional parameters to the +// RemoveAccess requests. +type RemoveAccessOptsBuilder interface { + ToFlavorRemoveAccessMap() (map[string]interface{}, error) +} + +// RemoveAccessOpts represents options for removing access to a flavor. +type RemoveAccessOpts struct { + // Tenant is the project/tenant ID to grant access. + Tenant string `json:"tenant"` +} + +// ToFlavorRemoveAccessMap constructs a request body from RemoveAccessOpts. +func (opts RemoveAccessOpts) ToFlavorRemoveAccessMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "removeTenantAccess") +} + +// RemoveAccess removes/revokes a tenant/project access to a flavor. +func RemoveAccess(client *gophercloud.ServiceClient, id string, opts RemoveAccessOptsBuilder) (r RemoveAccessResult) { + b, err := opts.ToFlavorRemoveAccessMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// ExtraSpecs requests all the extra-specs for the given flavor ID. +func ListExtraSpecs(client *gophercloud.ServiceClient, flavorID string) (r ListExtraSpecsResult) { + _, r.Err = client.Get(extraSpecsListURL(client, flavorID), &r.Body, nil) + return +} + +func GetExtraSpec(client *gophercloud.ServiceClient, flavorID string, key string) (r GetExtraSpecResult) { + _, r.Err = client.Get(extraSpecsGetURL(client, flavorID, key), &r.Body, nil) + return +} + +// CreateExtraSpecsOptsBuilder allows extensions to add additional parameters to the +// CreateExtraSpecs requests. +type CreateExtraSpecsOptsBuilder interface { + ToFlavorExtraSpecsCreateMap() (map[string]interface{}, error) +} + +// ExtraSpecsOpts is a map that contains key-value pairs. +type ExtraSpecsOpts map[string]string + +// ToFlavorExtraSpecsCreateMap assembles a body for a Create request based on +// the contents of ExtraSpecsOpts. +func (opts ExtraSpecsOpts) ToFlavorExtraSpecsCreateMap() (map[string]interface{}, error) { + return map[string]interface{}{"extra_specs": opts}, nil +} + +// CreateExtraSpecs will create or update the extra-specs key-value pairs for +// the specified Flavor. +func CreateExtraSpecs(client *gophercloud.ServiceClient, flavorID string, opts CreateExtraSpecsOptsBuilder) (r CreateExtraSpecsResult) { + b, err := opts.ToFlavorExtraSpecsCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(extraSpecsCreateURL(client, flavorID), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// UpdateExtraSpecOptsBuilder allows extensions to add additional parameters to +// the Update request. +type UpdateExtraSpecOptsBuilder interface { + ToFlavorExtraSpecUpdateMap() (map[string]string, string, error) +} + +// ToFlavorExtraSpecUpdateMap assembles a body for an Update request based on +// the contents of a ExtraSpecOpts. +func (opts ExtraSpecsOpts) ToFlavorExtraSpecUpdateMap() (map[string]string, string, error) { + if len(opts) != 1 { + err := gophercloud.ErrInvalidInput{} + err.Argument = "flavors.ExtraSpecOpts" + err.Info = "Must have 1 and only one key-value pair" + return nil, "", err + } + + var key string + for k := range opts { + key = k + } + + return opts, key, nil +} + +// UpdateExtraSpec will updates the value of the specified flavor's extra spec +// for the key in opts. +func UpdateExtraSpec(client *gophercloud.ServiceClient, flavorID string, opts UpdateExtraSpecOptsBuilder) (r UpdateExtraSpecResult) { + b, key, err := opts.ToFlavorExtraSpecUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(extraSpecUpdateURL(client, flavorID, key), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// DeleteExtraSpec will delete the key-value pair with the given key for the given +// flavor ID. +func DeleteExtraSpec(client *gophercloud.ServiceClient, flavorID, key string) (r DeleteExtraSpecResult) { + _, r.Err = client.Delete(extraSpecDeleteURL(client, flavorID, key), &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// IDFromName is a convienience function that returns a flavor's ID given its +// name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/results.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/results.go index 121abbb8d..92fe1b180 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/results.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/results.go @@ -12,16 +12,26 @@ type commonResult struct { gophercloud.Result } +// CreateResult is the response of a Get operations. Call its Extract method to +// interpret it as a Flavor. type CreateResult struct { commonResult } -// GetResult temporarily holds the response from a Get call. +// GetResult is the response of a Get operations. Call its Extract method to +// interpret it as a Flavor. type GetResult struct { commonResult } -// Extract provides access to the individual Flavor returned by the Get and Create functions. +// DeleteResult is the result from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} + +// Extract provides access to the individual Flavor returned by the Get and +// Create functions. func (r commonResult) Extract() (*Flavor, error) { var s struct { Flavor *Flavor `json:"flavor"` @@ -30,24 +40,35 @@ func (r commonResult) Extract() (*Flavor, error) { return s.Flavor, err } -// Flavor records represent (virtual) hardware configurations for server resources in a region. +// Flavor represent (virtual) hardware configurations for server resources +// in a region. type Flavor struct { - // The Id field contains the flavor's unique identifier. - // For example, this identifier will be useful when specifying which hardware configuration to use for a new server instance. + // ID is the flavor's unique ID. ID string `json:"id"` - // The Disk and RA< fields provide a measure of storage space offered by the flavor, in GB and MB, respectively. + + // Disk is the amount of root disk, measured in GB. Disk int `json:"disk"` - RAM int `json:"ram"` - // The Name field provides a human-readable moniker for the flavor. - Name string `json:"name"` + + // RAM is the amount of memory, measured in MB. + RAM int `json:"ram"` + + // Name is the name of the flavor. + Name string `json:"name"` + + // RxTxFactor describes bandwidth alterations of the flavor. RxTxFactor float64 `json:"rxtx_factor"` - // Swap indicates how much space is reserved for swap. - // If not provided, this field will be set to 0. - Swap int `json:"swap"` + + // Swap is the amount of swap space, measured in MB. + Swap int `json:"-"` + // VCPUs indicates how many (virtual) CPUs are available for this flavor. VCPUs int `json:"vcpus"` + // IsPublic indicates whether the flavor is public. - IsPublic bool `json:"is_public"` + IsPublic bool `json:"os-flavor-access:is_public"` + + // Ephemeral is the amount of ephemeral disk space, measured in GB. + Ephemeral int `json:"OS-FLV-EXT-DATA:ephemeral"` } func (r *Flavor) UnmarshalJSON(b []byte) error { @@ -82,18 +103,19 @@ func (r *Flavor) UnmarshalJSON(b []byte) error { return nil } -// FlavorPage contains a single page of the response from a List call. +// FlavorPage contains a single page of all flavors from a ListDetails call. type FlavorPage struct { pagination.LinkedPageBase } -// IsEmpty determines if a page contains any results. +// IsEmpty determines if a FlavorPage contains any results. func (page FlavorPage) IsEmpty() (bool, error) { flavors, err := ExtractFlavors(page) return len(flavors) == 0, err } -// NextPageURL uses the response's embedded link reference to navigate to the next page of results. +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. func (page FlavorPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"flavors_links"` @@ -105,7 +127,8 @@ func (page FlavorPage) NextPageURL() (string, error) { return gophercloud.ExtractNextURL(s.Links) } -// ExtractFlavors provides access to the list of flavors in a page acquired from the List operation. +// ExtractFlavors provides access to the list of flavors in a page acquired +// from the ListDetail operation. func ExtractFlavors(r pagination.Page) ([]Flavor, error) { var s struct { Flavors []Flavor `json:"flavors"` @@ -113,3 +136,117 @@ func ExtractFlavors(r pagination.Page) ([]Flavor, error) { err := (r.(FlavorPage)).ExtractInto(&s) return s.Flavors, err } + +// AccessPage contains a single page of all FlavorAccess entries for a flavor. +type AccessPage struct { + pagination.SinglePageBase +} + +// IsEmpty indicates whether an AccessPage is empty. +func (page AccessPage) IsEmpty() (bool, error) { + v, err := ExtractAccesses(page) + return len(v) == 0, err +} + +// ExtractAccesses interprets a page of results as a slice of FlavorAccess. +func ExtractAccesses(r pagination.Page) ([]FlavorAccess, error) { + var s struct { + FlavorAccesses []FlavorAccess `json:"flavor_access"` + } + err := (r.(AccessPage)).ExtractInto(&s) + return s.FlavorAccesses, err +} + +type accessResult struct { + gophercloud.Result +} + +// AddAccessResult is the response of an AddAccess operation. Call its +// Extract method to interpret it as a slice of FlavorAccess. +type AddAccessResult struct { + accessResult +} + +// RemoveAccessResult is the response of a RemoveAccess operation. Call its +// Extract method to interpret it as a slice of FlavorAccess. +type RemoveAccessResult struct { + accessResult +} + +// Extract provides access to the result of an access create or delete. +// The result will be all accesses that the flavor has. +func (r accessResult) Extract() ([]FlavorAccess, error) { + var s struct { + FlavorAccesses []FlavorAccess `json:"flavor_access"` + } + err := r.ExtractInto(&s) + return s.FlavorAccesses, err +} + +// FlavorAccess represents an ACL of tenant access to a specific Flavor. +type FlavorAccess struct { + // FlavorID is the unique ID of the flavor. + FlavorID string `json:"flavor_id"` + + // TenantID is the unique ID of the tenant. + TenantID string `json:"tenant_id"` +} + +// Extract interprets any extraSpecsResult as ExtraSpecs, if possible. +func (r extraSpecsResult) Extract() (map[string]string, error) { + var s struct { + ExtraSpecs map[string]string `json:"extra_specs"` + } + err := r.ExtractInto(&s) + return s.ExtraSpecs, err +} + +// extraSpecsResult contains the result of a call for (potentially) multiple +// key-value pairs. Call its Extract method to interpret it as a +// map[string]interface. +type extraSpecsResult struct { + gophercloud.Result +} + +// ListExtraSpecsResult contains the result of a Get operation. Call its Extract +// method to interpret it as a map[string]interface. +type ListExtraSpecsResult struct { + extraSpecsResult +} + +// CreateExtraSpecResult contains the result of a Create operation. Call its +// Extract method to interpret it as a map[string]interface. +type CreateExtraSpecsResult struct { + extraSpecsResult +} + +// extraSpecResult contains the result of a call for individual a single +// key-value pair. +type extraSpecResult struct { + gophercloud.Result +} + +// GetExtraSpecResult contains the result of a Get operation. Call its Extract +// method to interpret it as a map[string]interface. +type GetExtraSpecResult struct { + extraSpecResult +} + +// UpdateExtraSpecResult contains the result of an Update operation. Call its +// Extract method to interpret it as a map[string]interface. +type UpdateExtraSpecResult struct { + extraSpecResult +} + +// DeleteExtraSpecResult contains the result of a Delete operation. Call its +// ExtractErr method to determine if the call succeeded or failed. +type DeleteExtraSpecResult struct { + gophercloud.ErrResult +} + +// Extract interprets any extraSpecResult as an ExtraSpec, if possible. +func (r extraSpecResult) Extract() (map[string]string, error) { + var s map[string]string + err := r.ExtractInto(&s) + return s, err +} diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/urls.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/urls.go index 2fc21796f..8620dd78a 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/urls.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/urls.go @@ -15,3 +15,35 @@ func listURL(client *gophercloud.ServiceClient) string { func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("flavors") } + +func deleteURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("flavors", id) +} + +func accessURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("flavors", id, "os-flavor-access") +} + +func accessActionURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("flavors", id, "action") +} + +func extraSpecsListURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("flavors", id, "os-extra_specs") +} + +func extraSpecsGetURL(client *gophercloud.ServiceClient, id, key string) string { + return client.ServiceURL("flavors", id, "os-extra_specs", key) +} + +func extraSpecsCreateURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("flavors", id, "os-extra_specs") +} + +func extraSpecUpdateURL(client *gophercloud.ServiceClient, id, key string) string { + return client.ServiceURL("flavors", id, "os-extra_specs", key) +} + +func extraSpecDeleteURL(client *gophercloud.ServiceClient, id, key string) string { + return client.ServiceURL("flavors", id, "os-extra_specs", key) +} diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/doc.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/doc.go index 0edaa3f02..22410a79a 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/doc.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/doc.go @@ -1,7 +1,32 @@ -// Package images provides information and interaction with the image API -// resource in the OpenStack Compute service. -// -// An image is a collection of files used to create or rebuild a server. -// Operators provide a number of pre-built OS images by default. You may also -// create custom images from cloud servers you have launched. +/* +Package images provides information and interaction with the images through +the OpenStack Compute service. + +This API is deprecated and will be removed from a future version of the Nova +API service. + +An image is a collection of files used to create or rebuild a server. +Operators provide a number of pre-built OS images by default. You may also +create custom images from cloud servers you have launched. + +Example to List Images + + listOpts := images.ListOpts{ + Limit: 2, + } + + allPages, err := images.ListDetail(computeClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allImages, err := images.ExtractImages(allPages) + if err != nil { + panic(err) + } + + for _, image := range allImages { + fmt.Printf("%+v\n", image) + } +*/ package images diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/requests.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/requests.go index df9f1da8f..558b481b9 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/requests.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/requests.go @@ -6,26 +6,33 @@ import ( ) // ListOptsBuilder allows extensions to add additional parameters to the -// List request. +// ListDetail request. type ListOptsBuilder interface { ToImageListQuery() (string, error) } -// ListOpts contain options for limiting the number of Images returned from a call to ListDetail. +// ListOpts contain options filtering Images returned from a call to ListDetail. type ListOpts struct { - // When the image last changed status (in date-time format). + // ChangesSince filters Images based on the last changed status (in date-time + // format). ChangesSince string `q:"changes-since"` - // The number of Images to return. + + // Limit limits the number of Images to return. Limit int `q:"limit"` - // UUID of the Image at which to set a marker. + + // Mark is an Image UUID at which to set a marker. Marker string `q:"marker"` - // The name of the Image. + + // Name is the name of the Image. Name string `q:"name"` - // The name of the Server (in URL format). + + // Server is the name of the Server (in URL format). Server string `q:"server"` - // The current status of the Image. + + // Status is the current status of the Image. Status string `q:"status"` - // The value of the type of image (e.g. BASE, SERVER, ALL) + + // Type is the type of image (e.g. BASE, SERVER, ALL). Type string `q:"type"` } @@ -50,8 +57,7 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat }) } -// Get acquires additional detail about a specific image by ID. -// Use ExtractImage() to interpret the result as an openstack Image. +// Get returns data about a specific image by its ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return @@ -63,7 +69,8 @@ func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { return } -// IDFromName is a convienience function that returns an image's ID given its name. +// IDFromName is a convienience function that returns an image's ID given its +// name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/results.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/results.go index f9ebc69e9..70d1018c7 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/results.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/results.go @@ -5,12 +5,14 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// GetResult temporarily stores a Get response. +// GetResult is the response from a Get operation. Call its Extract method to +// interpret it as an Image. type GetResult struct { gophercloud.Result } -// DeleteResult represents the result of an image.Delete operation. +// DeleteResult is the result from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } @@ -24,44 +26,53 @@ func (r GetResult) Extract() (*Image, error) { return s.Image, err } -// Image is used for JSON (un)marshalling. -// It provides a description of an OS image. +// Image represents an Image returned by the Compute API. type Image struct { - // ID contains the image's unique identifier. + // ID is the unique ID of an image. ID string + // Created is the date when the image was created. Created string - // MinDisk and MinRAM specify the minimum resources a server must provide to be able to install the image. + // MinDisk is the minimum amount of disk a flavor must have to be able + // to create a server based on the image, measured in GB. MinDisk int - MinRAM int + + // MinRAM is the minimum amount of RAM a flavor must have to be able + // to create a server based on the image, measured in MB. + MinRAM int // Name provides a human-readable moniker for the OS image. Name string // The Progress and Status fields indicate image-creation status. - // Any usable image will have 100% progress. Progress int - Status string + // Status is the current status of the image. + Status string + + // Update is the date when the image was updated. Updated string + // Metadata provides free-form key/value pairs that further describe the + // image. Metadata map[string]interface{} } -// ImagePage contains a single page of results from a List operation. -// Use ExtractImages to convert it into a slice of usable structs. +// ImagePage contains a single page of all Images returne from a ListDetail +// operation. Use ExtractImages to convert it into a slice of usable structs. type ImagePage struct { pagination.LinkedPageBase } -// IsEmpty returns true if a page contains no Image results. +// IsEmpty returns true if an ImagePage contains no Image results. func (page ImagePage) IsEmpty() (bool, error) { images, err := ExtractImages(page) return len(images) == 0, err } -// NextPageURL uses the response's embedded link reference to navigate to the next page of results. +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. func (page ImagePage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"images_links"` @@ -73,7 +84,8 @@ func (page ImagePage) NextPageURL() (string, error) { return gophercloud.ExtractNextURL(s.Links) } -// ExtractImages converts a page of List results into a slice of usable Image structs. +// ExtractImages converts a page of List results into a slice of usable Image +// structs. func ExtractImages(r pagination.Page) ([]Image, error) { var s struct { Images []Image `json:"images"` diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/doc.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/doc.go index fe4567120..3b0ab7836 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/doc.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/doc.go @@ -1,6 +1,115 @@ -// Package servers provides information and interaction with the server API -// resource in the OpenStack Compute service. -// -// A server is a virtual machine instance in the compute system. In order for -// one to be provisioned, a valid flavor and image are required. +/* +Package servers provides information and interaction with the server API +resource in the OpenStack Compute service. + +A server is a virtual machine instance in the compute system. In order for +one to be provisioned, a valid flavor and image are required. + +Example to List Servers + + listOpts := servers.ListOpts{ + AllTenants: true, + } + + allPages, err := servers.List(computeClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allServers, err := servers.ExtractServers(allPages) + if err != nil { + panic(err) + } + + for _, server := range allServers { + fmt.Printf("%+v\n", server) + } + +Example to Create a Server + + createOpts := servers.CreateOpts{ + Name: "server_name", + ImageRef: "image-uuid", + FlavorRef: "flavor-uuid", + } + + server, err := servers.Create(computeClient, createOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete a Server + + serverID := "d9072956-1560-487c-97f2-18bdf65ec749" + err := servers.Delete(computeClient, serverID).ExtractErr() + if err != nil { + panic(err) + } + +Example to Force Delete a Server + + serverID := "d9072956-1560-487c-97f2-18bdf65ec749" + err := servers.ForceDelete(computeClient, serverID).ExtractErr() + if err != nil { + panic(err) + } + +Example to Reboot a Server + + rebootOpts := servers.RebootOpts{ + Type: servers.SoftReboot, + } + + serverID := "d9072956-1560-487c-97f2-18bdf65ec749" + + err := servers.Reboot(computeClient, serverID, rebootOpts).ExtractErr() + if err != nil { + panic(err) + } + +Example to Rebuild a Server + + rebuildOpts := servers.RebuildOpts{ + Name: "new_name", + ImageID: "image-uuid", + } + + serverID := "d9072956-1560-487c-97f2-18bdf65ec749" + + server, err := servers.Rebuilt(computeClient, serverID, rebuildOpts).Extract() + if err != nil { + panic(err) + } + +Example to Resize a Server + + resizeOpts := servers.ResizeOpts{ + FlavorRef: "flavor-uuid", + } + + serverID := "d9072956-1560-487c-97f2-18bdf65ec749" + + err := servers.Resize(computeClient, serverID, resizeOpts).ExtractErr() + if err != nil { + panic(err) + } + + err = servers.ConfirmResize(computeClient, serverID).ExtractErr() + if err != nil { + panic(err) + } + +Example to Snapshot a Server + + snapshotOpts := servers.CreateImageOpts{ + Name: "snapshot_name", + } + + serverID := "d9072956-1560-487c-97f2-18bdf65ec749" + + image, err := servers.CreateImage(computeClient, serverID, snapshotOpts).ExtractImageID() + if err != nil { + panic(err) + } +*/ package servers diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go index 1a7b3ec77..a6530f8d6 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go @@ -21,13 +21,13 @@ type ListOptsBuilder interface { // the server attributes you want to see returned. Marker and Limit are used // for pagination. type ListOpts struct { - // A time/date stamp for when the server last changed status. + // ChangesSince is a time/date stamp for when the server last changed status. ChangesSince string `q:"changes-since"` - // Name of the image in URL format. + // Image is the name of the image in URL format. Image string `q:"image"` - // Name of the flavor in URL format. + // Flavor is the name of the flavor in URL format. Flavor string `q:"flavor"` // Name of the server as a string; can be queried with regular expressions. @@ -36,22 +36,24 @@ type ListOpts struct { // underlying database server implemented for Compute. Name string `q:"name"` - // Value of the status of the server so that you can filter on "ACTIVE" for example. + // Status is the value of the status of the server so that you can filter on + // "ACTIVE" for example. Status string `q:"status"` - // Name of the host as a string. + // Host is the name of the host as a string. Host string `q:"host"` - // UUID of the server at which you want to set a marker. + // Marker is a UUID of the server at which you want to set a marker. Marker string `q:"marker"` - // Integer value for the limit of values to return. + // Limit is an integer value for the limit of values to return. Limit int `q:"limit"` - // Bool to show all tenants + // AllTenants is a bool to show all tenants. AllTenants bool `q:"all_tenants"` - // List servers for a particular tenant. Setting "AllTenants = true" is required. + // TenantID lists servers for a particular tenant. + // Setting "AllTenants = true" is required. TenantID string `q:"tenant_id"` } @@ -76,15 +78,16 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa }) } -// CreateOptsBuilder describes struct types that can be accepted by the Create call. -// The CreateOpts struct in this package does. +// CreateOptsBuilder allows extensions to add additional parameters to the +// Create request. type CreateOptsBuilder interface { ToServerCreateMap() (map[string]interface{}, error) } -// Network is used within CreateOpts to control a new server's network attachments. +// Network is used within CreateOpts to control a new server's network +// attachments. type Network struct { - // UUID of a nova-network to attach to the newly provisioned server. + // UUID of a network to attach to the newly provisioned server. // Required unless Port is provided. UUID string @@ -92,19 +95,21 @@ type Network struct { // Required unless UUID is provided. Port string - // FixedIP [optional] specifies a fixed IPv4 address to be used on this network. + // FixedIP specifies a fixed IPv4 address to be used on this network. FixedIP string } // Personality is an array of files that are injected into the server at launch. type Personality []*File -// File is used within CreateOpts and RebuildOpts to inject a file into the server at launch. -// File implements the json.Marshaler interface, so when a Create or Rebuild operation is requested, -// json.Marshal will call File's MarshalJSON method. +// File is used within CreateOpts and RebuildOpts to inject a file into the +// server at launch. +// File implements the json.Marshaler interface, so when a Create or Rebuild +// operation is requested, json.Marshal will call File's MarshalJSON method. type File struct { - // Path of the file + // Path of the file. Path string + // Contents of the file. Maximum content size is 255 bytes. Contents []byte } @@ -126,13 +131,13 @@ type CreateOpts struct { // Name is the name to assign to the newly launched server. Name string `json:"name" required:"true"` - // ImageRef [optional; required if ImageName is not provided] is the ID or full - // URL to the image that contains the server's OS and initial state. + // ImageRef [optional; required if ImageName is not provided] is the ID or + // full URL to the image that contains the server's OS and initial state. // Also optional if using the boot-from-volume extension. ImageRef string `json:"imageRef"` - // ImageName [optional; required if ImageRef is not provided] is the name of the - // image that contains the server's OS and initial state. + // ImageName [optional; required if ImageRef is not provided] is the name of + // the image that contains the server's OS and initial state. // Also optional if using the boot-from-volume extension. ImageName string `json:"-"` @@ -144,7 +149,8 @@ type CreateOpts struct { // the flavor that describes the server's specs. FlavorName string `json:"-"` - // SecurityGroups lists the names of the security groups to which this server should belong. + // SecurityGroups lists the names of the security groups to which this server + // should belong. SecurityGroups []string `json:"-"` // UserData contains configuration information or scripts to use upon launch. @@ -155,10 +161,12 @@ type CreateOpts struct { AvailabilityZone string `json:"availability_zone,omitempty"` // Networks dictates how this server will be attached to available networks. - // By default, the server will be attached to all isolated networks for the tenant. + // By default, the server will be attached to all isolated networks for the + // tenant. Networks []Network `json:"-"` - // Metadata contains key-value pairs (up to 255 bytes each) to attach to the server. + // Metadata contains key-value pairs (up to 255 bytes each) to attach to the + // server. Metadata map[string]string `json:"metadata,omitempty"` // Personality includes files to inject into the server at launch. @@ -169,7 +177,7 @@ type CreateOpts struct { ConfigDrive *bool `json:"config_drive,omitempty"` // AdminPass sets the root user password. If not set, a randomly-generated - // password will be created and returned in the rponse. + // password will be created and returned in the response. AdminPass string `json:"adminPass,omitempty"` // AccessIPv4 specifies an IPv4 address for the instance. @@ -183,7 +191,8 @@ type CreateOpts struct { ServiceClient *gophercloud.ServiceClient `json:"-"` } -// ToServerCreateMap assembles a request body based on the contents of a CreateOpts. +// ToServerCreateMap assembles a request body based on the contents of a +// CreateOpts. func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { sc := opts.ServiceClient opts.ServiceClient = nil @@ -277,13 +286,14 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } -// Delete requests that a server previously provisioned be removed from your account. +// Delete requests that a server previously provisioned be removed from your +// account. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } -// ForceDelete forces the deletion of a server +// ForceDelete forces the deletion of a server. func ForceDelete(client *gophercloud.ServiceClient, id string) (r ActionResult) { _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"forceDelete": ""}, nil, nil) return @@ -297,12 +307,14 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { return } -// UpdateOptsBuilder allows extensions to add additional attributes to the Update request. +// UpdateOptsBuilder allows extensions to add additional attributes to the +// Update request. type UpdateOptsBuilder interface { ToServerUpdateMap() (map[string]interface{}, error) } -// UpdateOpts specifies the base attributes that may be updated on an existing server. +// UpdateOpts specifies the base attributes that may be updated on an existing +// server. type UpdateOpts struct { // Name changes the displayed name of the server. // The server host name will *not* change. @@ -334,7 +346,8 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder return } -// ChangeAdminPassword alters the administrator or root password for a specified server. +// ChangeAdminPassword alters the administrator or root password for a specified +// server. func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) (r ActionResult) { b := map[string]interface{}{ "changePassword": map[string]string{ @@ -357,33 +370,38 @@ const ( PowerCycle = HardReboot ) -// RebootOptsBuilder is an interface that options must satisfy in order to be -// used when rebooting a server instance +// RebootOptsBuilder allows extensions to add additional parameters to the +// reboot request. type RebootOptsBuilder interface { ToServerRebootMap() (map[string]interface{}, error) } -// RebootOpts satisfies the RebootOptsBuilder interface +// RebootOpts provides options to the reboot request. type RebootOpts struct { + // Type is the type of reboot to perform on the server. Type RebootMethod `json:"type" required:"true"` } -// ToServerRebootMap allows RebootOpts to satisfiy the RebootOptsBuilder -// interface -func (opts *RebootOpts) ToServerRebootMap() (map[string]interface{}, error) { +// ToServerRebootMap builds a body for the reboot request. +func (opts RebootOpts) ToServerRebootMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "reboot") } -// Reboot requests that a given server reboot. -// Two methods exist for rebooting a server: -// -// HardReboot (aka PowerCycle) starts the server instance by physically cutting power to the machine, or if a VM, -// terminating it at the hypervisor level. -// It's done. Caput. Full stop. -// Then, after a brief while, power is rtored or the VM instance rtarted. -// -// SoftReboot (aka OSReboot) simply tells the OS to rtart under its own procedur. -// E.g., in Linux, asking it to enter runlevel 6, or executing "sudo shutdown -r now", or by asking Windows to rtart the machine. +/* + Reboot requests that a given server reboot. + + Two methods exist for rebooting a server: + + HardReboot (aka PowerCycle) starts the server instance by physically cutting + power to the machine, or if a VM, terminating it at the hypervisor level. + It's done. Caput. Full stop. + Then, after a brief while, power is rtored or the VM instance restarted. + + SoftReboot (aka OSReboot) simply tells the OS to restart under its own + procedure. + E.g., in Linux, asking it to enter runlevel 6, or executing + "sudo shutdown -r now", or by asking Windows to rtart the machine. +*/ func Reboot(client *gophercloud.ServiceClient, id string, opts RebootOptsBuilder) (r ActionResult) { b, err := opts.ToServerRebootMap() if err != nil { @@ -394,31 +412,43 @@ func Reboot(client *gophercloud.ServiceClient, id string, opts RebootOptsBuilder return } -// RebuildOptsBuilder is an interface that allows extensions to override the -// default behaviour of rebuild options +// RebuildOptsBuilder allows extensions to provide additional parameters to the +// rebuild request. type RebuildOptsBuilder interface { ToServerRebuildMap() (map[string]interface{}, error) } // RebuildOpts represents the configuration options used in a server rebuild -// operation +// operation. type RebuildOpts struct { - // The server's admin password + // AdminPass is the server's admin password AdminPass string `json:"adminPass,omitempty"` - // The ID of the image you want your server to be provisioned on - ImageID string `json:"imageRef"` + + // ImageID is the ID of the image you want your server to be provisioned on. + ImageID string `json:"imageRef"` + + // ImageName is readable name of an image. ImageName string `json:"-"` + // Name to set the server to Name string `json:"name,omitempty"` + // AccessIPv4 [optional] provides a new IPv4 address for the instance. AccessIPv4 string `json:"accessIPv4,omitempty"` + // AccessIPv6 [optional] provides a new IPv6 address for the instance. AccessIPv6 string `json:"accessIPv6,omitempty"` - // Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server. + + // Metadata [optional] contains key-value pairs (up to 255 bytes each) + // to attach to the server. Metadata map[string]string `json:"metadata,omitempty"` + // Personality [optional] includes files to inject into the server at launch. // Rebuild will base64-encode file contents for you. - Personality Personality `json:"personality,omitempty"` + Personality Personality `json:"personality,omitempty"` + + // ServiceClient will allow calls to be made to retrieve an image or + // flavor ID by name. ServiceClient *gophercloud.ServiceClient `json:"-"` } @@ -461,31 +491,34 @@ func Rebuild(client *gophercloud.ServiceClient, id string, opts RebuildOptsBuild return } -// ResizeOptsBuilder is an interface that allows extensions to override the default structure of -// a Resize request. +// ResizeOptsBuilder allows extensions to add additional parameters to the +// resize request. type ResizeOptsBuilder interface { ToServerResizeMap() (map[string]interface{}, error) } -// ResizeOpts represents the configuration options used to control a Resize operation. +// ResizeOpts represents the configuration options used to control a Resize +// operation. type ResizeOpts struct { // FlavorRef is the ID of the flavor you wish your server to become. FlavorRef string `json:"flavorRef" required:"true"` } -// ToServerResizeMap formats a ResizeOpts as a map that can be used as a JSON request body for the -// Resize request. +// ToServerResizeMap formats a ResizeOpts as a map that can be used as a JSON +// request body for the Resize request. func (opts ResizeOpts) ToServerResizeMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "resize") } // Resize instructs the provider to change the flavor of the server. +// // Note that this implies rebuilding it. +// // Unfortunately, one cannot pass rebuild parameters to the resize function. -// When the resize completes, the server will be in RESIZE_VERIFY state. -// While in this state, you can explore the use of the new server's configuration. -// If you like it, call ConfirmResize() to commit the resize permanently. -// Otherwise, call RevertResize() to restore the old configuration. +// When the resize completes, the server will be in VERIFY_RESIZE state. +// While in this state, you can explore the use of the new server's +// configuration. If you like it, call ConfirmResize() to commit the resize +// permanently. Otherwise, call RevertResize() to restore the old configuration. func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ActionResult) { b, err := opts.ToServerResizeMap() if err != nil { @@ -512,41 +545,8 @@ func RevertResize(client *gophercloud.ServiceClient, id string) (r ActionResult) return } -// RescueOptsBuilder is an interface that allows extensions to override the -// default structure of a Rescue request. -type RescueOptsBuilder interface { - ToServerRescueMap() (map[string]interface{}, error) -} - -// RescueOpts represents the configuration options used to control a Rescue -// option. -type RescueOpts struct { - // AdminPass is the desired administrative password for the instance in - // RESCUE mode. If it's left blank, the server will generate a password. - AdminPass string `json:"adminPass,omitempty"` -} - -// ToServerRescueMap formats a RescueOpts as a map that can be used as a JSON -// request body for the Rescue request. -func (opts RescueOpts) ToServerRescueMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "rescue") -} - -// Rescue instructs the provider to place the server into RESCUE mode. -func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder) (r RescueResult) { - b, err := opts.ToServerRescueMap() - if err != nil { - r.Err = err - return - } - _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - return -} - -// ResetMetadataOptsBuilder allows extensions to add additional parameters to the -// Reset request. +// ResetMetadataOptsBuilder allows extensions to add additional parameters to +// the Reset request. type ResetMetadataOptsBuilder interface { ToMetadataResetMap() (map[string]interface{}, error) } @@ -554,20 +554,23 @@ type ResetMetadataOptsBuilder interface { // MetadataOpts is a map that contains key-value pairs. type MetadataOpts map[string]string -// ToMetadataResetMap assembles a body for a Reset request based on the contents of a MetadataOpts. +// ToMetadataResetMap assembles a body for a Reset request based on the contents +// of a MetadataOpts. func (opts MetadataOpts) ToMetadataResetMap() (map[string]interface{}, error) { return map[string]interface{}{"metadata": opts}, nil } -// ToMetadataUpdateMap assembles a body for an Update request based on the contents of a MetadataOpts. +// ToMetadataUpdateMap assembles a body for an Update request based on the +// contents of a MetadataOpts. func (opts MetadataOpts) ToMetadataUpdateMap() (map[string]interface{}, error) { return map[string]interface{}{"metadata": opts}, nil } -// ResetMetadata will create multiple new key-value pairs for the given server ID. -// Note: Using this operation will erase any already-existing metadata and create -// the new metadata provided. To keep any already-existing metadata, use the -// UpdateMetadatas or UpdateMetadata function. +// ResetMetadata will create multiple new key-value pairs for the given server +// ID. +// Note: Using this operation will erase any already-existing metadata and +// create the new metadata provided. To keep any already-existing metadata, +// use the UpdateMetadatas or UpdateMetadata function. func ResetMetadata(client *gophercloud.ServiceClient, id string, opts ResetMetadataOptsBuilder) (r ResetMetadataResult) { b, err := opts.ToMetadataResetMap() if err != nil { @@ -586,15 +589,15 @@ func Metadata(client *gophercloud.ServiceClient, id string) (r GetMetadataResult return } -// UpdateMetadataOptsBuilder allows extensions to add additional parameters to the -// Create request. +// UpdateMetadataOptsBuilder allows extensions to add additional parameters to +// the Create request. type UpdateMetadataOptsBuilder interface { ToMetadataUpdateMap() (map[string]interface{}, error) } -// UpdateMetadata updates (or creates) all the metadata specified by opts for the given server ID. -// This operation does not affect already-existing metadata that is not specified -// by opts. +// UpdateMetadata updates (or creates) all the metadata specified by opts for +// the given server ID. This operation does not affect already-existing metadata +// that is not specified by opts. func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { b, err := opts.ToMetadataUpdateMap() if err != nil { @@ -616,7 +619,8 @@ type MetadatumOptsBuilder interface { // MetadatumOpts is a map of length one that contains a key-value pair. type MetadatumOpts map[string]string -// ToMetadatumCreateMap assembles a body for a Create request based on the contents of a MetadataumOpts. +// ToMetadatumCreateMap assembles a body for a Create request based on the +// contents of a MetadataumOpts. func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]interface{}, string, error) { if len(opts) != 1 { err := gophercloud.ErrInvalidInput{} @@ -632,7 +636,8 @@ func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]interface{}, string return metadatum, key, nil } -// CreateMetadatum will create or update the key-value pair with the given key for the given server ID. +// CreateMetadatum will create or update the key-value pair with the given key +// for the given server ID. func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts MetadatumOptsBuilder) (r CreateMetadatumResult) { b, key, err := opts.ToMetadatumCreateMap() if err != nil { @@ -645,53 +650,60 @@ func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts Metadatu return } -// Metadatum requests the key-value pair with the given key for the given server ID. +// Metadatum requests the key-value pair with the given key for the given +// server ID. func Metadatum(client *gophercloud.ServiceClient, id, key string) (r GetMetadatumResult) { _, r.Err = client.Get(metadatumURL(client, id, key), &r.Body, nil) return } -// DeleteMetadatum will delete the key-value pair with the given key for the given server ID. +// DeleteMetadatum will delete the key-value pair with the given key for the +// given server ID. func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) (r DeleteMetadatumResult) { _, r.Err = client.Delete(metadatumURL(client, id, key), nil) return } -// ListAddresses makes a request against the API to list the servers IP addresses. +// ListAddresses makes a request against the API to list the servers IP +// addresses. func ListAddresses(client *gophercloud.ServiceClient, id string) pagination.Pager { return pagination.NewPager(client, listAddressesURL(client, id), func(r pagination.PageResult) pagination.Page { return AddressPage{pagination.SinglePageBase(r)} }) } -// ListAddressesByNetwork makes a request against the API to list the servers IP addresses -// for the given network. +// ListAddressesByNetwork makes a request against the API to list the servers IP +// addresses for the given network. func ListAddressesByNetwork(client *gophercloud.ServiceClient, id, network string) pagination.Pager { return pagination.NewPager(client, listAddressesByNetworkURL(client, id, network), func(r pagination.PageResult) pagination.Page { return NetworkAddressPage{pagination.SinglePageBase(r)} }) } -// CreateImageOptsBuilder is the interface types must satisfy in order to be -// used as CreateImage options +// CreateImageOptsBuilder allows extensions to add additional parameters to the +// CreateImage request. type CreateImageOptsBuilder interface { ToServerCreateImageMap() (map[string]interface{}, error) } -// CreateImageOpts satisfies the CreateImageOptsBuilder +// CreateImageOpts provides options to pass to the CreateImage request. type CreateImageOpts struct { - // Name of the image/snapshot + // Name of the image/snapshot. Name string `json:"name" required:"true"` - // Metadata contains key-value pairs (up to 255 bytes each) to attach to the created image. + + // Metadata contains key-value pairs (up to 255 bytes each) to attach to + // the created image. Metadata map[string]string `json:"metadata,omitempty"` } -// ToServerCreateImageMap formats a CreateImageOpts structure into a request body. +// ToServerCreateImageMap formats a CreateImageOpts structure into a request +// body. func (opts CreateImageOpts) ToServerCreateImageMap() (map[string]interface{}, error) { return gophercloud.BuildRequestBody(opts, "createImage") } -// CreateImage makes a request against the nova API to schedule an image to be created of the server +// CreateImage makes a request against the nova API to schedule an image to be +// created of the server func CreateImage(client *gophercloud.ServiceClient, id string, opts CreateImageOptsBuilder) (r CreateImageResult) { b, err := opts.ToServerCreateImageMap() if err != nil { @@ -706,11 +718,17 @@ func CreateImage(client *gophercloud.ServiceClient, id string, opts CreateImageO return } -// IDFromName is a convienience function that returns a server's ID given its name. +// IDFromName is a convienience function that returns a server's ID given its +// name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { count := 0 id := "" - allPages, err := List(client, nil).AllPages() + + listOpts := ListOpts{ + Name: name, + } + + allPages, err := List(client, listOpts).AllPages() if err != nil { return "", err } @@ -737,8 +755,40 @@ func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) } } -// GetPassword makes a request against the nova API to get the encrypted administrative password. +// GetPassword makes a request against the nova API to get the encrypted +// administrative password. func GetPassword(client *gophercloud.ServiceClient, serverId string) (r GetPasswordResult) { _, r.Err = client.Get(passwordURL(client, serverId), &r.Body, nil) return } + +// ShowConsoleOutputOptsBuilder is the interface types must satisfy in order to be +// used as ShowConsoleOutput options +type ShowConsoleOutputOptsBuilder interface { + ToServerShowConsoleOutputMap() (map[string]interface{}, error) +} + +// ShowConsoleOutputOpts satisfies the ShowConsoleOutputOptsBuilder +type ShowConsoleOutputOpts struct { + // The number of lines to fetch from the end of console log. + // All lines will be returned if this is not specified. + Length int `json:"length,omitempty"` +} + +// ToServerShowConsoleOutputMap formats a ShowConsoleOutputOpts structure into a request body. +func (opts ShowConsoleOutputOpts) ToServerShowConsoleOutputMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "os-getConsoleOutput") +} + +// ShowConsoleOutput makes a request against the nova API to get console log from the server +func ShowConsoleOutput(client *gophercloud.ServiceClient, id string, opts ShowConsoleOutputOptsBuilder) (r ShowConsoleOutputResult) { + b, err := opts.ToServerShowConsoleOutputMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go index 1ae1e91c7..f973d1ea0 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go @@ -32,54 +32,73 @@ func ExtractServersInto(r pagination.Page, v interface{}) error { return r.(ServerPage).Result.ExtractIntoSlicePtr(v, "servers") } -// CreateResult temporarily contains the response from a Create call. +// CreateResult is the response from a Create operation. Call its Extract +// method to interpret it as a Server. type CreateResult struct { serverResult } -// GetResult temporarily contains the response from a Get call. +// GetResult is the response from a Get operation. Call its Extract +// method to interpret it as a Server. type GetResult struct { serverResult } -// UpdateResult temporarily contains the response from an Update call. +// UpdateResult is the response from an Update operation. Call its Extract +// method to interpret it as a Server. type UpdateResult struct { serverResult } -// DeleteResult temporarily contains the response from a Delete call. +// DeleteResult is the response from a Delete operation. Call its ExtractErr +// method to determine if the call succeeded or failed. type DeleteResult struct { gophercloud.ErrResult } -// RebuildResult temporarily contains the response from a Rebuild call. +// RebuildResult is the response from a Rebuild operation. Call its Extract +// method to interpret it as a Server. type RebuildResult struct { serverResult } -// ActionResult represents the result of server action operations, like reboot +// ActionResult represents the result of server action operations, like reboot. +// Call its ExtractErr method to determine if the action succeeded or failed. type ActionResult struct { gophercloud.ErrResult } -// RescueResult represents the result of a server rescue operation -type RescueResult struct { - ActionResult -} - -// CreateImageResult represents the result of an image creation operation +// CreateImageResult is the response from a CreateImage operation. Call its +// ExtractImageID method to retrieve the ID of the newly created image. type CreateImageResult struct { gophercloud.Result } +// ShowConsoleOutputResult represents the result of console output from a server +type ShowConsoleOutputResult struct { + gophercloud.Result +} + +// Extract will return the console output from a ShowConsoleOutput request. +func (r ShowConsoleOutputResult) Extract() (string, error) { + var s struct { + Output string `json:"output"` + } + + err := r.ExtractInto(&s) + return s.Output, err +} + // GetPasswordResult represent the result of a get os-server-password operation. +// Call its ExtractPassword method to retrieve the password. type GetPasswordResult struct { gophercloud.Result } // ExtractPassword gets the encrypted password. // If privateKey != nil the password is decrypted with the private key. -// If privateKey == nil the encrypted password is returned and can be decrypted with: +// If privateKey == nil the encrypted password is returned and can be decrypted +// with: // echo '' | base64 -D | openssl rsautl -decrypt -inkey func (r GetPasswordResult) ExtractPassword(privateKey *rsa.PrivateKey) (string, error) { var s struct { @@ -107,7 +126,7 @@ func decryptPassword(encryptedPassword string, privateKey *rsa.PrivateKey) (stri return string(password), nil } -// ExtractImageID gets the ID of the newly created server image from the header +// ExtractImageID gets the ID of the newly created server image from the header. func (r CreateImageResult) ExtractImageID() (string, error) { if r.Err != nil { return "", r.Err @@ -124,54 +143,84 @@ func (r CreateImageResult) ExtractImageID() (string, error) { return imageID, nil } -// Extract interprets any RescueResult as an AdminPass, if possible. -func (r RescueResult) Extract() (string, error) { - var s struct { - AdminPass string `json:"adminPass"` - } - err := r.ExtractInto(&s) - return s.AdminPass, err -} - -// Server exposes only the standard OpenStack fields corresponding to a given server on the user's account. +// Server represents a server/instance in the OpenStack cloud. type Server struct { - // ID uniquely identifies this server amongst all other servers, including those not accessible to the current tenant. + // ID uniquely identifies this server amongst all other servers, + // including those not accessible to the current tenant. ID string `json:"id"` + // TenantID identifies the tenant owning this server resource. TenantID string `json:"tenant_id"` + // UserID uniquely identifies the user account owning the tenant. UserID string `json:"user_id"` + // Name contains the human-readable name for the server. Name string `json:"name"` - // Updated and Created contain ISO-8601 timestamps of when the state of the server last changed, and when it was created. + + // Updated and Created contain ISO-8601 timestamps of when the state of the + // server last changed, and when it was created. Updated time.Time `json:"updated"` Created time.Time `json:"created"` - HostID string `json:"hostid"` - // Status contains the current operational status of the server, such as IN_PROGRESS or ACTIVE. + + // HostID is the host where the server is located in the cloud. + HostID string `json:"hostid"` + + // Status contains the current operational status of the server, + // such as IN_PROGRESS or ACTIVE. Status string `json:"status"` + // Progress ranges from 0..100. // A request made against the server completes only once Progress reaches 100. Progress int `json:"progress"` - // AccessIPv4 and AccessIPv6 contain the IP addresses of the server, suitable for remote access for administration. + + // AccessIPv4 and AccessIPv6 contain the IP addresses of the server, + // suitable for remote access for administration. AccessIPv4 string `json:"accessIPv4"` AccessIPv6 string `json:"accessIPv6"` - // Image refers to a JSON object, which itself indicates the OS image used to deploy the server. + + // Image refers to a JSON object, which itself indicates the OS image used to + // deploy the server. Image map[string]interface{} `json:"-"` - // Flavor refers to a JSON object, which itself indicates the hardware configuration of the deployed server. + + // Flavor refers to a JSON object, which itself indicates the hardware + // configuration of the deployed server. Flavor map[string]interface{} `json:"flavor"` - // Addresses includes a list of all IP addresses assigned to the server, keyed by pool. + + // Addresses includes a list of all IP addresses assigned to the server, + // keyed by pool. Addresses map[string]interface{} `json:"addresses"` - // Metadata includes a list of all user-specified key-value pairs attached to the server. + + // Metadata includes a list of all user-specified key-value pairs attached + // to the server. Metadata map[string]string `json:"metadata"` - // Links includes HTTP references to the itself, useful for passing along to other APIs that might want a server reference. + + // Links includes HTTP references to the itself, useful for passing along to + // other APIs that might want a server reference. Links []interface{} `json:"links"` + // KeyName indicates which public key was injected into the server on launch. KeyName string `json:"key_name"` - // AdminPass will generally be empty (""). However, it will contain the administrative password chosen when provisioning a new server without a set AdminPass setting in the first place. + + // AdminPass will generally be empty (""). However, it will contain the + // administrative password chosen when provisioning a new server without a + // set AdminPass setting in the first place. // Note that this is the ONLY time this field will be valid. AdminPass string `json:"adminPass"` - // SecurityGroups includes the security groups that this instance has applied to it + + // SecurityGroups includes the security groups that this instance has applied + // to it. SecurityGroups []map[string]interface{} `json:"security_groups"` + + // Fault contains failure information about a server. + Fault Fault `json:"fault"` +} + +type Fault struct { + Code int `json:"code"` + Created time.Time `json:"created"` + Details string `json:"details"` + Message string `json:"message"` } func (r *Server) UnmarshalJSON(b []byte) error { @@ -200,9 +249,10 @@ func (r *Server) UnmarshalJSON(b []byte) error { return err } -// ServerPage abstracts the raw results of making a List() request against the API. -// As OpenStack extensions may freely alter the response bodies of structures returned to the client, you may only safely access the -// data provided through the ExtractServers call. +// ServerPage abstracts the raw results of making a List() request against +// the API. As OpenStack extensions may freely alter the response bodies of +// structures returned to the client, you may only safely access the data +// provided through the ExtractServers call. type ServerPage struct { pagination.LinkedPageBase } @@ -213,7 +263,8 @@ func (r ServerPage) IsEmpty() (bool, error) { return len(s) == 0, err } -// NextPageURL uses the response's embedded link reference to navigate to the next page of results. +// NextPageURL uses the response's embedded link reference to navigate to the +// next page of results. func (r ServerPage) NextPageURL() (string, error) { var s struct { Links []gophercloud.Link `json:"servers_links"` @@ -225,49 +276,59 @@ func (r ServerPage) NextPageURL() (string, error) { return gophercloud.ExtractNextURL(s.Links) } -// ExtractServers interprets the results of a single page from a List() call, producing a slice of Server entities. +// ExtractServers interprets the results of a single page from a List() call, +// producing a slice of Server entities. func ExtractServers(r pagination.Page) ([]Server, error) { var s []Server err := ExtractServersInto(r, &s) return s, err } -// MetadataResult contains the result of a call for (potentially) multiple key-value pairs. +// MetadataResult contains the result of a call for (potentially) multiple +// key-value pairs. Call its Extract method to interpret it as a +// map[string]interface. type MetadataResult struct { gophercloud.Result } -// GetMetadataResult temporarily contains the response from a metadata Get call. +// GetMetadataResult contains the result of a Get operation. Call its Extract +// method to interpret it as a map[string]interface. type GetMetadataResult struct { MetadataResult } -// ResetMetadataResult temporarily contains the response from a metadata Reset call. +// ResetMetadataResult contains the result of a Reset operation. Call its +// Extract method to interpret it as a map[string]interface. type ResetMetadataResult struct { MetadataResult } -// UpdateMetadataResult temporarily contains the response from a metadata Update call. +// UpdateMetadataResult contains the result of an Update operation. Call its +// Extract method to interpret it as a map[string]interface. type UpdateMetadataResult struct { MetadataResult } -// MetadatumResult contains the result of a call for individual a single key-value pair. +// MetadatumResult contains the result of a call for individual a single +// key-value pair. type MetadatumResult struct { gophercloud.Result } -// GetMetadatumResult temporarily contains the response from a metadatum Get call. +// GetMetadatumResult contains the result of a Get operation. Call its Extract +// method to interpret it as a map[string]interface. type GetMetadatumResult struct { MetadatumResult } -// CreateMetadatumResult temporarily contains the response from a metadatum Create call. +// CreateMetadatumResult contains the result of a Create operation. Call its +// Extract method to interpret it as a map[string]interface. type CreateMetadatumResult struct { MetadatumResult } -// DeleteMetadatumResult temporarily contains the response from a metadatum Delete call. +// DeleteMetadatumResult contains the result of a Delete operation. Call its +// ExtractErr method to determine if the call succeeded or failed. type DeleteMetadatumResult struct { gophercloud.ErrResult } @@ -296,9 +357,10 @@ type Address struct { Address string `json:"addr"` } -// AddressPage abstracts the raw results of making a ListAddresses() request against the API. -// As OpenStack extensions may freely alter the response bodies of structures returned -// to the client, you may only safely access the data provided through the ExtractAddresses call. +// AddressPage abstracts the raw results of making a ListAddresses() request +// against the API. As OpenStack extensions may freely alter the response bodies +// of structures returned to the client, you may only safely access the data +// provided through the ExtractAddresses call. type AddressPage struct { pagination.SinglePageBase } @@ -309,8 +371,8 @@ func (r AddressPage) IsEmpty() (bool, error) { return len(addresses) == 0, err } -// ExtractAddresses interprets the results of a single page from a ListAddresses() call, -// producing a map of addresses. +// ExtractAddresses interprets the results of a single page from a +// ListAddresses() call, producing a map of addresses. func ExtractAddresses(r pagination.Page) (map[string][]Address, error) { var s struct { Addresses map[string][]Address `json:"addresses"` @@ -319,9 +381,11 @@ func ExtractAddresses(r pagination.Page) (map[string][]Address, error) { return s.Addresses, err } -// NetworkAddressPage abstracts the raw results of making a ListAddressesByNetwork() request against the API. -// As OpenStack extensions may freely alter the response bodies of structures returned -// to the client, you may only safely access the data provided through the ExtractAddresses call. +// NetworkAddressPage abstracts the raw results of making a +// ListAddressesByNetwork() request against the API. +// As OpenStack extensions may freely alter the response bodies of structures +// returned to the client, you may only safely access the data provided through +// the ExtractAddresses call. type NetworkAddressPage struct { pagination.SinglePageBase } @@ -332,8 +396,8 @@ func (r NetworkAddressPage) IsEmpty() (bool, error) { return len(addresses) == 0, err } -// ExtractNetworkAddresses interprets the results of a single page from a ListAddressesByNetwork() call, -// producing a slice of addresses. +// ExtractNetworkAddresses interprets the results of a single page from a +// ListAddressesByNetwork() call, producing a slice of addresses. func ExtractNetworkAddresses(r pagination.Page) ([]Address, error) { var s map[string][]Address err := (r.(NetworkAddressPage)).ExtractInto(&s) diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/util.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/util.go index 494a0e4dc..cadef0545 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/util.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/util.go @@ -2,8 +2,9 @@ package servers import "github.com/gophercloud/gophercloud" -// WaitForStatus will continually poll a server until it successfully transitions to a specified -// status. It will do this for at most the number of seconds specified. +// WaitForStatus will continually poll a server until it successfully +// transitions to a specified status. It will do this for at most the number +// of seconds specified. func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { return gophercloud.WaitFor(secs, func() (bool, error) { current, err := Get(c, id).Extract() diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/doc.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/doc.go new file mode 100644 index 000000000..cedf1f4d3 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/doc.go @@ -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 diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go index ea37f5b27..12c8aebcf 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go @@ -6,12 +6,16 @@ import ( 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. +/* +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) @@ -54,12 +58,16 @@ func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts gophercloud.EndpointOpt 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. +/* +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. @@ -76,7 +84,7 @@ func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts gophercloud.EndpointOpt return "", err } if (opts.Availability == gophercloud.Availability(endpoint.Interface)) && - (opts.Region == "" || endpoint.Region == opts.Region) { + (opts.Region == "" || endpoint.Region == opts.Region || endpoint.RegionID == opts.Region) { endpoints = append(endpoints, endpoint) } } diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/doc.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/doc.go index 0c2d49d56..45623369e 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/doc.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/doc.go @@ -1,7 +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. +/* +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 diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go index b6550ce60..f21a58f10 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go @@ -9,6 +9,7 @@ import ( 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"` } @@ -32,18 +33,22 @@ func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager { 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 describes struct types that can be accepted by the Create call. +// 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. +// 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") } @@ -67,17 +72,21 @@ func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { return } -// UpdateOptsBuilder allows extensions to add additional attributes to the Update request. +// 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 server. +// 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"` + Description *string `json:"description,omitempty"` + // Enabled sets the tenant status to enabled or disabled. Enabled *bool `json:"enabled,omitempty"` } @@ -100,7 +109,7 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder return } -// Delete is the operation responsible for permanently deleting an API tenant. +// 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 diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go index 5a319de5c..bb6c2c6b0 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go @@ -43,7 +43,8 @@ func (r TenantPage) NextPageURL() (string, error) { return gophercloud.ExtractNextURL(s.Links) } -// ExtractTenants returns a slice of Tenants contained in a single page of results. +// 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"` @@ -56,7 +57,7 @@ type tenantResult struct { gophercloud.Result } -// Extract interprets any tenantResults as a tenant. +// Extract interprets any tenantResults as a Tenant. func (r tenantResult) Extract() (*Tenant, error) { var s struct { Tenant *Tenant `json:"tenant"` @@ -65,22 +66,26 @@ func (r tenantResult) Extract() (*Tenant, error) { return s.Tenant, err } -// GetResult temporarily contains the response from the Get call. +// GetResult is the response from a Get request. Call its Extract method to +// interpret it as a Tenant. type GetResult struct { tenantResult } -// CreateResult temporarily contains the reponse from the Create call. +// CreateResult is the response from a Create request. Call its Extract method +// to interpret it as a Tenant. type CreateResult struct { tenantResult } -// DeleteResult temporarily contains the response from the Delete call. +// 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 temporarily contains the response from the Update call. +// UpdateResult is the response from a Update request. Call its Extract method +// to interpret it as a Tenant. type UpdateResult struct { tenantResult } diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/doc.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/doc.go index 31cacc5e1..5375eea87 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/doc.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/doc.go @@ -1,5 +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 +/* +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 diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go index 4983031e7..ab32368cc 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go @@ -2,17 +2,21 @@ 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. +// AuthOptionsV2 wraps a gophercloud AuthOptions in order to adhere to the +// AuthOptionsBuilder interface. type AuthOptionsV2 struct { PasswordCredentials *PasswordCredentialsV2 `json:"passwordCredentials,omitempty" xor:"TokenCredentials"` @@ -23,15 +27,16 @@ type AuthOptionsV2 struct { 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 allows users to authenticate (possibly as another user) + // with an authentication token ID. TokenCredentials *TokenCredentialsV2 `json:"token,omitempty" xor:"PasswordCredentials"` } -// AuthOptionsBuilder describes any argument that may be passed to the Create call. +// 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. + // ToTokenCreateMap assembles the Create request body, returning an error + // if parameters are missing or inconsistent. ToTokenV2CreateMap() (map[string]interface{}, error) } @@ -47,8 +52,7 @@ type AuthOptions struct { TokenID string } -// ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder -// interface in the v2 tokens package +// ToTokenV2CreateMap builds a token request body from the given AuthOptions. func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { v2Opts := AuthOptionsV2{ TenantID: opts.TenantID, @@ -74,9 +78,9 @@ func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { } // Create authenticates to the identity service and attempts to acquire a Token. -// If successful, the CreateResult -// 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. +// 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 { diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go index 6b3649370..b11326772 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go @@ -7,20 +7,24 @@ import ( "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" ) -// Token provides only the most basic information related to an authentication token. +// 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. + // 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. + // 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 provides information about the tenant to which this token grants + // access. Tenant tenants.Tenant } @@ -38,13 +42,17 @@ type User struct { } // 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. +// 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 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 (""). +// 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"` @@ -56,38 +64,44 @@ type Endpoint struct { 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. +// CatalogEntry provides a type-safe interface to an Identity API V2 service +// catalog listing. // -// 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. +// 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 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 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. +// ServiceCatalog provides a view into the service catalog from a previous, +// successful authentication. type ServiceCatalog struct { Entries []CatalogEntry } -// CreateResult defers the interpretation of a created token. -// Use ExtractToken() to interpret it as a Token, or ExtractServiceCatalog() to interpret it as a service catalog. +// 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. +// 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 } @@ -121,7 +135,8 @@ func (r CreateResult) ExtractToken() (*Token, error) { }, nil } -// ExtractServiceCatalog returns the ServiceCatalog that was generated along with the user's Token. +// ExtractServiceCatalog returns the ServiceCatalog that was generated along +// with the user's Token. func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) { var s struct { Access struct { diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/doc.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/doc.go index 76ff5f473..966e128f1 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/doc.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/doc.go @@ -1,6 +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 +/* +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 diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go index 39c19aee3..2d20fa6f4 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go @@ -10,20 +10,22 @@ type Scope struct { DomainName string } -// AuthOptionsBuilder describes any argument that may be passed to the Create call. +// 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 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. + // 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 @@ -39,96 +41,55 @@ type AuthOptions struct { 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 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, + 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) { - 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, gophercloud.ErrScopeDomainIDOrDomainName{} - } - if opts.Scope.ProjectID != "" { - return nil, gophercloud.ErrScopeProjectIDOrProjectName{} - } + scope := gophercloud.AuthScope(opts.Scope) - 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, gophercloud.ErrScopeProjectIDAlone{} - } - if opts.Scope.DomainName != "" { - return nil, gophercloud.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, gophercloud.ErrScopeDomainIDOrDomainName{} - } - - // DomainID - return map[string]interface{}{ - "domain": map[string]interface{}{ - "id": &opts.Scope.DomainID, - }, - }, nil - } else if opts.Scope.DomainName != "" { - return nil, gophercloud.ErrScopeDomainName{} + gophercloudAuthOpts := gophercloud.AuthOptions{ + Scope: &scope, + DomainID: opts.DomainID, + DomainName: opts.DomainName, } - return nil, nil + return gophercloudAuthOpts.ToTokenV3ScopeMap() } func (opts *AuthOptions) CanReauth() bool { @@ -141,7 +102,8 @@ func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[ } } -// Create authenticates and either generates a new token, or changes the Scope of an existing token. +// 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 { @@ -180,7 +142,7 @@ func Get(c *gophercloud.ServiceClient, token string) (r GetResult) { // Validate determines if a specified token is valid or not. func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { - resp, err := c.Request("HEAD", tokenURL(c), &gophercloud.RequestOpts{ + resp, err := c.Head(tokenURL(c), &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(c, token), OkCodes: []int{200, 204, 404}, }) diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go index 7c306e83f..ebdca58f6 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go @@ -13,41 +13,50 @@ import ( 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). +// 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. +// 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 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 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. +// 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. +// 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 on the API. +// User represents a user resource that exists in the Identity Service. type User struct { Domain Domain `json:"domain"` ID string `json:"id"` @@ -67,7 +76,8 @@ type Project struct { Name string `json:"name"` } -// commonResult is the deferred result of a Create or a Get call. +// 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 } @@ -92,7 +102,8 @@ func (r commonResult) ExtractToken() (*Token, error) { return &s, err } -// ExtractServiceCatalog returns the ServiceCatalog that was generated along with the user's Token. +// 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) @@ -126,27 +137,31 @@ func (r commonResult) ExtractProject() (*Project, error) { return s.Project, err } -// CreateResult defers the interpretation of a created token. -// Use ExtractToken() to interpret it as a Token, or ExtractServiceCatalog() to interpret it as a service catalog. +// 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 deferred response from a Get call. +// 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 the deferred response from a Revoke call. +// 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. +// 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"` } diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/doc.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/doc.go new file mode 100644 index 000000000..0c12bf2e0 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/doc.go @@ -0,0 +1,48 @@ +/* +Package imagedata enables management of image data. + +Example to Upload Image Data + + imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea" + + imageData, err := os.Open("/path/to/image/file") + if err != nil { + panic(err) + } + defer imageData.Close() + + err = imagedata.Upload(imageClient, imageID, imageData).ExtractErr() + if err != nil { + panic(err) + } + +Example to Stage Image Data + + imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea" + + imageData, err := os.Open("/path/to/image/file") + if err != nil { + panic(err) + } + defer imageData.Close() + + err = imagedata.Stage(imageClient, imageID, imageData).ExtractErr() + if err != nil { + panic(err) + } + +Example to Download Image Data + + imageID := "da3b75d9-3f4a-40e7-8a2c-bfab23927dea" + + image, err := imagedata.Download(imageClient, imageID).Extract() + if err != nil { + panic(err) + } + + imageData, err := ioutil.ReadAll(image) + if err != nil { + panic(err) + } +*/ +package imagedata diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/requests.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/requests.go index 7fd6951d3..545c561f3 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/requests.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/requests.go @@ -7,7 +7,7 @@ import ( "github.com/gophercloud/gophercloud" ) -// Upload uploads image file +// Upload uploads an image file. func Upload(client *gophercloud.ServiceClient, id string, data io.Reader) (r UploadResult) { _, r.Err = client.Put(uploadURL(client, id), data, nil, &gophercloud.RequestOpts{ MoreHeaders: map[string]string{"Content-Type": "application/octet-stream"}, @@ -16,7 +16,18 @@ func Upload(client *gophercloud.ServiceClient, id string, data io.Reader) (r Upl return } -// Download retrieves file +// Stage performs PUT call on the existing image object in the Imageservice with +// the provided file. +// Existing image object must be in the "queued" status. +func Stage(client *gophercloud.ServiceClient, id string, data io.Reader) (r StageResult) { + _, r.Err = client.Put(stageURL(client, id), data, nil, &gophercloud.RequestOpts{ + MoreHeaders: map[string]string{"Content-Type": "application/octet-stream"}, + OkCodes: []int{204}, + }) + return +} + +// Download retrieves an image. func Download(client *gophercloud.ServiceClient, id string) (r DownloadResult) { var resp *http.Response resp, r.Err = client.Get(downloadURL(client, id), nil, nil) diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/results.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/results.go index 970b226f2..17a3f0e0f 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/results.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/results.go @@ -7,12 +7,20 @@ import ( "github.com/gophercloud/gophercloud" ) -// UploadResult is the result of an upload image operation +// UploadResult is the result of an upload image operation. Call its ExtractErr +// method to determine if the request succeeded or failed. type UploadResult struct { gophercloud.ErrResult } -// DownloadResult is the result of a download image operation +// StageResult is the result of a stage image operation. Call its ExtractErr +// method to determine if the request succeeded or failed. +type StageResult struct { + gophercloud.ErrResult +} + +// DownloadResult is the result of a download image operation. Call its Extract +// method to gain access to the image data. type DownloadResult struct { gophercloud.Result } diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/urls.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/urls.go index ccd6416e5..d9615ba7f 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/urls.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/imagedata/urls.go @@ -2,10 +2,20 @@ package imagedata import "github.com/gophercloud/gophercloud" +const ( + rootPath = "images" + uploadPath = "file" + stagePath = "stage" +) + // `imageDataURL(c,i)` is the URL for the binary image data for the // image identified by ID `i` in the service `c`. func uploadURL(c *gophercloud.ServiceClient, imageID string) string { - return c.ServiceURL("images", imageID, "file") + return c.ServiceURL(rootPath, imageID, uploadPath) +} + +func stageURL(c *gophercloud.ServiceClient, imageID string) string { + return c.ServiceURL(rootPath, imageID, stagePath) } func downloadURL(c *gophercloud.ServiceClient, imageID string) string { diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/doc.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/doc.go new file mode 100644 index 000000000..14da9ac90 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/doc.go @@ -0,0 +1,60 @@ +/* +Package images enables management and retrieval of images from the OpenStack +Image Service. + +Example to List Images + + images.ListOpts{ + Owner: "a7509e1ae65945fda83f3e52c6296017", + } + + allPages, err := images.List(imagesClient, listOpts).AllPages() + if err != nil { + panic(err) + } + + allImages, err := images.ExtractImages(allPages) + if err != nil { + panic(err) + } + + for _, image := range allImages { + fmt.Printf("%+v\n", image) + } + +Example to Create an Image + + createOpts := images.CreateOpts{ + Name: "image_name", + Visibility: images.ImageVisibilityPrivate, + } + + image, err := images.Create(imageClient, createOpts) + if err != nil { + panic(err) + } + +Example to Update an Image + + imageID := "1bea47ed-f6a9-463b-b423-14b9cca9ad27" + + updateOpts := images.UpdateOpts{ + images.ReplaceImageName{ + NewName: "new_name", + }, + } + + image, err := images.Update(imageClient, imageID, updateOpts).Extract() + if err != nil { + panic(err) + } + +Example to Delete an Image + + imageID := "1bea47ed-f6a9-463b-b423-14b9cca9ad27" + err := images.Delete(imageClient, imageID).ExtractErr() + if err != nil { + panic(err) + } +*/ +package images diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/requests.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/requests.go index 044b5cb95..16290d395 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/requests.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/requests.go @@ -1,6 +1,10 @@ package images import ( + "fmt" + "net/url" + "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -15,33 +19,106 @@ type ListOptsBuilder interface { // 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. -//http://developer.openstack.org/api-ref-image-v2.html +// +// http://developer.openstack.org/api-ref-image-v2.html type ListOpts struct { + // ID is the ID of the image. + // Multiple IDs can be specified by constructing a string + // such as "in:uuid1,uuid2,uuid3". + ID string `q:"id"` + // Integer value for the limit of values to return. Limit int `q:"limit"` // UUID of the server at which you want to set a marker. Marker string `q:"marker"` - Name string `q:"name"` - Visibility ImageVisibility `q:"visibility"` + // Name filters on the name of the image. + // Multiple names can be specified by constructing a string + // such as "in:name1,name2,name3". + Name string `q:"name"` + + // Visibility filters on the visibility of the image. + Visibility ImageVisibility `q:"visibility"` + + // MemberStatus filters on the member status of the image. MemberStatus ImageMemberStatus `q:"member_status"` - Owner string `q:"owner"` - Status ImageStatus `q:"status"` - SizeMin int64 `q:"size_min"` - SizeMax int64 `q:"size_max"` - SortKey string `q:"sort_key"` - SortDir string `q:"sort_dir"` - Tag string `q:"tag"` + + // Owner filters on the project ID of the image. + Owner string `q:"owner"` + + // Status filters on the status of the image. + // Multiple statuses can be specified by constructing a string + // such as "in:saving,queued". + Status ImageStatus `q:"status"` + + // SizeMin filters on the size_min image property. + SizeMin int64 `q:"size_min"` + + // SizeMax filters on the size_max image property. + SizeMax int64 `q:"size_max"` + + // Sort sorts the results using the new style of sorting. See the OpenStack + // Image API reference for the exact syntax. + // + // Sort cannot be used with the classic sort options (sort_key and sort_dir). + Sort string `q:"sort"` + + // SortKey will sort the results based on a specified image property. + SortKey string `q:"sort_key"` + + // SortDir will sort the list results either ascending or decending. + SortDir string `q:"sort_dir"` + + // Tags filters on specific image tags. + Tags []string `q:"tag"` + + // CreatedAtQuery filters images based on their creation date. + CreatedAtQuery *ImageDateQuery + + // UpdatedAtQuery filters images based on their updated date. + UpdatedAtQuery *ImageDateQuery + + // ContainerFormat filters images based on the container_format. + // Multiple container formats can be specified by constructing a + // string such as "in:bare,ami". + ContainerFormat string `q:"container_format"` + + // DiskFormat filters images based on the disk_format. + // Multiple disk formats can be specified by constructing a string + // such as "in:qcow2,iso". + DiskFormat string `q:"disk_format"` } // ToImageListQuery formats a ListOpts into a query string. func (opts ListOpts) ToImageListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) + params := q.Query() + + if opts.CreatedAtQuery != nil { + createdAt := opts.CreatedAtQuery.Date.Format(time.RFC3339) + if v := opts.CreatedAtQuery.Filter; v != "" { + createdAt = fmt.Sprintf("%s:%s", v, createdAt) + } + + params.Add("created_at", createdAt) + } + + if opts.UpdatedAtQuery != nil { + updatedAt := opts.UpdatedAtQuery.Date.Format(time.RFC3339) + if v := opts.UpdatedAtQuery.Filter; v != "" { + updatedAt = fmt.Sprintf("%s:%s", v, updatedAt) + } + + params.Add("updated_at", updatedAt) + } + + q = &url.URL{RawQuery: params.Encode()} + return q.String(), err } -// List implements image list request +// List implements image list request. func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listURL(c) if opts != nil { @@ -52,18 +129,22 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url += query } return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { - return ImagePage{pagination.LinkedPageBase{PageResult: r}} + imagePage := ImagePage{ + serviceURL: c.ServiceURL(), + LinkedPageBase: pagination.LinkedPageBase{PageResult: r}, + } + + return imagePage }) } -// CreateOptsBuilder describes struct types that can be accepted by the Create call. -// The CreateOpts struct in this package does. +// CreateOptsBuilder allows extensions to add parameters to the Create request. type CreateOptsBuilder interface { // Returns value that can be passed to json.Marshal ToImageCreateMap() (map[string]interface{}, error) } -// CreateOpts implements CreateOptsBuilder +// CreateOpts represents options used to create an image. type CreateOpts struct { // Name is the name of the new image. Name string `json:"name" required:"true"` @@ -118,7 +199,7 @@ func (opts CreateOpts) ToImageCreateMap() (map[string]interface{}, error) { return b, nil } -// Create implements create image request +// Create implements create image request. func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { b, err := opts.ToImageCreateMap() if err != nil { @@ -129,19 +210,19 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create return } -// Delete implements image delete request +// Delete implements image delete request. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { _, r.Err = client.Delete(deleteURL(client, id), nil) return } -// Get implements image get request +// Get implements image get request. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { _, r.Err = client.Get(getURL(client, id), &r.Body, nil) return } -// Update implements image updated request +// Update implements image updated request. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { b, err := opts.ToImageUpdateMap() if err != nil { @@ -155,9 +236,11 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder return } -// UpdateOptsBuilder implements UpdateOptsBuilder +// UpdateOptsBuilder allows extensions to add additional parameters to the +// Update request. type UpdateOptsBuilder interface { - // returns value implementing json.Marshaler which when marshaled matches the patch schema: + // returns value implementing json.Marshaler which when marshaled matches + // the patch schema: // http://specs.openstack.org/openstack/glance-specs/specs/api/v2/http-patch-image-api-v2.html ToImageUpdateMap() ([]interface{}, error) } @@ -165,7 +248,8 @@ type UpdateOptsBuilder interface { // UpdateOpts implements UpdateOpts type UpdateOpts []Patch -// ToImageUpdateMap builder +// ToImageUpdateMap assembles a request body based on the contents of +// UpdateOpts. func (opts UpdateOpts) ToImageUpdateMap() ([]interface{}, error) { m := make([]interface{}, len(opts)) for i, patch := range opts { @@ -175,32 +259,32 @@ func (opts UpdateOpts) ToImageUpdateMap() ([]interface{}, error) { return m, nil } -// Patch represents a single update to an existing image. Multiple updates to an image can be -// submitted at the same time. +// Patch represents a single update to an existing image. Multiple updates +// to an image can be submitted at the same time. type Patch interface { ToImagePatchMap() map[string]interface{} } -// UpdateVisibility updated visibility +// UpdateVisibility represents an updated visibility property request. type UpdateVisibility struct { Visibility ImageVisibility } -// ToImagePatchMap builder -func (u UpdateVisibility) ToImagePatchMap() map[string]interface{} { +// ToImagePatchMap assembles a request body based on UpdateVisibility. +func (r UpdateVisibility) ToImagePatchMap() map[string]interface{} { return map[string]interface{}{ "op": "replace", "path": "/visibility", - "value": u.Visibility, + "value": r.Visibility, } } -// ReplaceImageName implements Patch +// ReplaceImageName represents an updated image_name property request. type ReplaceImageName struct { NewName string } -// ToImagePatchMap builder +// ToImagePatchMap assembles a request body based on ReplaceImageName. func (r ReplaceImageName) ToImagePatchMap() map[string]interface{} { return map[string]interface{}{ "op": "replace", @@ -209,26 +293,26 @@ func (r ReplaceImageName) ToImagePatchMap() map[string]interface{} { } } -// ReplaceImageChecksum implements Patch +// ReplaceImageChecksum represents an updated checksum property request. type ReplaceImageChecksum struct { Checksum string } -// ReplaceImageChecksum builder -func (rc ReplaceImageChecksum) ToImagePatchMap() map[string]interface{} { +// ReplaceImageChecksum assembles a request body based on ReplaceImageChecksum. +func (r ReplaceImageChecksum) ToImagePatchMap() map[string]interface{} { return map[string]interface{}{ "op": "replace", "path": "/checksum", - "value": rc.Checksum, + "value": r.Checksum, } } -// ReplaceImageTags implements Patch +// ReplaceImageTags represents an updated tags property request. type ReplaceImageTags struct { NewTags []string } -// ToImagePatchMap builder +// ToImagePatchMap assembles a request body based on ReplaceImageTags. func (r ReplaceImageTags) ToImagePatchMap() map[string]interface{} { return map[string]interface{}{ "op": "replace", @@ -236,3 +320,33 @@ func (r ReplaceImageTags) ToImagePatchMap() map[string]interface{} { "value": r.NewTags, } } + +// UpdateOp represents a valid update operation. +type UpdateOp string + +const ( + AddOp UpdateOp = "add" + ReplaceOp UpdateOp = "replace" + RemoveOp UpdateOp = "remove" +) + +// UpdateImageProperty represents an update property request. +type UpdateImageProperty struct { + Op UpdateOp + Name string + Value string +} + +// ToImagePatchMap assembles a request body based on UpdateImageProperty. +func (r UpdateImageProperty) ToImagePatchMap() map[string]interface{} { + updateMap := map[string]interface{}{ + "op": r.Op, + "path": fmt.Sprintf("/%s", r.Name), + } + + if r.Value != "" { + updateMap["value"] = r.Value + } + + return updateMap +} diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/results.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/results.go index 632186b72..676181e1f 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/results.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/results.go @@ -11,11 +11,9 @@ import ( "github.com/gophercloud/gophercloud/pagination" ) -// Image model -// Does not include the literal image data; just metadata. -// returned by listing images, and by fetching a specific image. +// Image represents an image found in the OpenStack Image service. type Image struct { - // ID is the image UUID + // ID is the image UUID. ID string `json:"id"` // Name is the human-readable display name for the image. @@ -34,16 +32,19 @@ type Image struct { ContainerFormat string `json:"container_format"` // DiskFormat is the format of the disk. - // If set, valid values are ami, ari, aki, vhd, vmdk, raw, qcow2, vdi, and iso. + // If set, valid values are ami, ari, aki, vhd, vmdk, raw, qcow2, vdi, + // and iso. DiskFormat string `json:"disk_format"` - // MinDiskGigabytes is the amount of disk space in GB that is required to boot the image. + // MinDiskGigabytes is the amount of disk space in GB that is required to + // boot the image. MinDiskGigabytes int `json:"min_disk"` - // MinRAMMegabytes [optional] is the amount of RAM in MB that is required to boot the image. + // MinRAMMegabytes [optional] is the amount of RAM in MB that is required to + // boot the image. MinRAMMegabytes int `json:"min_ram"` - // Owner is the tenant the image belongs to. + // Owner is the tenant ID the image belongs to. Owner string `json:"owner"` // Protected is whether the image is deletable or not. @@ -52,31 +53,35 @@ type Image struct { // Visibility defines who can see/use the image. Visibility ImageVisibility `json:"visibility"` - // Checksum is the checksum of the data that's associated with the image + // Checksum is the checksum of the data that's associated with the image. Checksum string `json:"checksum"` // SizeBytes is the size of the data that's associated with the image. - SizeBytes int64 `json:"size"` + SizeBytes int64 `json:"-"` // Metadata is a set of metadata associated with the image. // Image metadata allow for meaningfully define the image properties - // and tags. See http://docs.openstack.org/developer/glance/metadefs-concepts.html. + // and tags. + // See http://docs.openstack.org/developer/glance/metadefs-concepts.html. Metadata map[string]string `json:"metadata"` - // Properties is a set of key-value pairs, if any, that are associated with the image. - Properties map[string]interface{} `json:"-"` + // Properties is a set of key-value pairs, if any, that are associated with + // the image. + Properties map[string]interface{} // CreatedAt is the date when the image has been created. CreatedAt time.Time `json:"created_at"` - // UpdatedAt is the date when the last change has been made to the image or it's properties. + // UpdatedAt is the date when the last change has been made to the image or + // it's properties. UpdatedAt time.Time `json:"updated_at"` - // File is the trailing path after the glance endpoint that represent the location - // of the image or the path to retrieve it. + // File is the trailing path after the glance endpoint that represent the + // location of the image or the path to retrieve it. File string `json:"file"` - // Schema is the path to the JSON-schema that represent the image or image entity. + // Schema is the path to the JSON-schema that represent the image or image + // entity. Schema string `json:"schema"` // VirtualSize is the virtual size of the image @@ -97,7 +102,7 @@ func (r *Image) UnmarshalJSON(b []byte) error { switch t := s.SizeBytes.(type) { case nil: - return nil + r.SizeBytes = 0 case float32: r.SizeBytes = int64(t) case float64: @@ -114,6 +119,7 @@ func (r *Image) UnmarshalJSON(b []byte) error { } if resultMap, ok := result.(map[string]interface{}); ok { delete(resultMap, "self") + delete(resultMap, "size") r.Properties = internal.RemainingKeys(Image{}, resultMap) } @@ -131,38 +137,44 @@ func (r commonResult) Extract() (*Image, error) { return s, err } -// CreateResult represents the result of a Create operation +// CreateResult represents the result of a Create operation. Call its Extract +// method to interpret it as an Image. type CreateResult struct { commonResult } -// UpdateResult represents the result of an Update operation +// UpdateResult represents the result of an Update operation. Call its Extract +// method to interpret it as an Image. type UpdateResult struct { commonResult } -// GetResult represents the result of a Get operation +// GetResult represents the result of a Get operation. Call its Extract +// method to interpret it as an Image. type GetResult struct { commonResult } -//DeleteResult model +// DeleteResult represents the result of a Delete operation. Call its +// ExtractErr method to interpret it as an Image. type DeleteResult struct { gophercloud.ErrResult } -// ImagePage represents page +// ImagePage represents the results of a List request. type ImagePage struct { + serviceURL string pagination.LinkedPageBase } -// IsEmpty returns true if a page contains no Images results. +// IsEmpty returns true if an ImagePage contains no Images results. func (r ImagePage) IsEmpty() (bool, error) { images, err := ExtractImages(r) return len(images) == 0, err } -// NextPageURL uses the response's embedded link reference to navigate to the next page of results. +// NextPageURL uses the response's embedded link reference to navigate to +// the next page of results. func (r ImagePage) NextPageURL() (string, error) { var s struct { Next string `json:"next"` @@ -176,10 +188,11 @@ func (r ImagePage) NextPageURL() (string, error) { return "", nil } - return nextPageURL(r.URL.String(), s.Next) + return nextPageURL(r.serviceURL, s.Next) } -// ExtractImages interprets the results of a single page from a List() call, producing a slice of Image entities. +// ExtractImages interprets the results of a single page from a List() call, +// producing a slice of Image entities. func ExtractImages(r pagination.Page) ([]Image, error) { var s struct { Images []Image `json:"images"` diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/types.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/types.go index 086e7e5d5..d2f9cbd3b 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/types.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/types.go @@ -1,5 +1,9 @@ package images +import ( + "time" +) + // ImageStatus image statuses // http://docs.openstack.org/developer/glance/statuses.html type ImageStatus string @@ -9,7 +13,8 @@ const ( // been reserved for an image in the image registry. ImageStatusQueued ImageStatus = "queued" - // ImageStatusSaving denotes that an image’s raw data is currently being uploaded to Glance + // ImageStatusSaving denotes that an image’s raw data is currently being + // uploaded to Glance ImageStatusSaving ImageStatus = "saving" // ImageStatusActive denotes an image that is fully available in Glance. @@ -23,16 +28,18 @@ const ( // The image information is retained in the image registry. ImageStatusDeleted ImageStatus = "deleted" - // ImageStatusPendingDelete is similar to Delete, but the image is not yet deleted. + // ImageStatusPendingDelete is similar to Delete, but the image is not yet + // deleted. ImageStatusPendingDelete ImageStatus = "pending_delete" - // ImageStatusDeactivated denotes that access to image data is not allowed to any non-admin user. + // ImageStatusDeactivated denotes that access to image data is not allowed to + // any non-admin user. ImageStatusDeactivated ImageStatus = "deactivated" ) // ImageVisibility denotes an image that is fully available in Glance. -// This occurs when the image data is uploaded, or the image size -// is explicitly set to zero on creation. +// This occurs when the image data is uploaded, or the image size is explicitly +// set to zero on creation. // According to design // https://wiki.openstack.org/wiki/Glance-v2-community-image-visibility-design type ImageVisibility string @@ -52,12 +59,13 @@ const ( // ImageVisibilityCommunity images: // - all users can see and boot it - // - users with tenantId in the member-list of the image with member_status == 'accepted' - // have this image in their default image-list + // - users with tenantId in the member-list of the image with + // member_status == 'accepted' have this image in their default image-list. ImageVisibilityCommunity ImageVisibility = "community" ) -// MemberStatus is a status for adding a new member (tenant) to an image member list. +// MemberStatus is a status for adding a new member (tenant) to an image +// member list. type ImageMemberStatus string const ( @@ -73,3 +81,24 @@ const ( // ImageMemberStatusAll ImageMemberStatusAll ImageMemberStatus = "all" ) + +// ImageDateFilter represents a valid filter to use for filtering +// images by their date during a List. +type ImageDateFilter string + +const ( + FilterGT ImageDateFilter = "gt" + FilterGTE ImageDateFilter = "gte" + FilterLT ImageDateFilter = "lt" + FilterLTE ImageDateFilter = "lte" + FilterNEQ ImageDateFilter = "neq" + FilterEQ ImageDateFilter = "eq" +) + +// ImageDateQuery represents a date field to be used for listing images. +// If no filter is specified, the query will act as though FilterEQ was +// set. +type ImageDateQuery struct { + Date time.Time + Filter ImageDateFilter +} diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/urls.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/urls.go index bf7cea1ef..1780c3c6c 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/urls.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/urls.go @@ -2,8 +2,10 @@ package images import ( "net/url" + "strings" "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/utils" ) // `listURL` is a pure function. `listURL(c)` is a URL for which a GET @@ -38,14 +40,26 @@ func deleteURL(c *gophercloud.ServiceClient, imageID string) string { } // builds next page full url based on current url -func nextPageURL(currentURL string, next string) (string, error) { - base, err := url.Parse(currentURL) +func nextPageURL(serviceURL, requestedNext string) (string, error) { + base, err := utils.BaseEndpoint(serviceURL) if err != nil { return "", err } - rel, err := url.Parse(next) + + requestedNextURL, err := url.Parse(requestedNext) if err != nil { return "", err } - return base.ResolveReference(rel).String(), nil + + base = gophercloud.NormalizeURL(base) + nextPath := base + strings.TrimPrefix(requestedNextURL.Path, "/") + + nextURL, err := url.Parse(nextPath) + if err != nil { + return "", err + } + + nextURL.RawQuery = requestedNextURL.RawQuery + + return nextURL.String(), nil } diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/utils/base_endpoint.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/utils/base_endpoint.go new file mode 100644 index 000000000..40080f7af --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/utils/base_endpoint.go @@ -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 +} diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/utils/choose_version.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/utils/choose_version.go index c605d0844..27da19f91 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/utils/choose_version.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/utils/choose_version.go @@ -68,11 +68,6 @@ func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (* return nil, "", err } - byID := make(map[string]*Version) - for _, version := range recognized { - byID[version.ID] = version - } - var highest *Version var endpoint string @@ -84,20 +79,22 @@ func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (* } } - if matching, ok := byID[value.ID]; ok { - // 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) + 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 } - return matching, href, nil - } - // Otherwise, find the highest-priority version with a whitelisted status. - if goodStatus[strings.ToLower(value.Status)] { - if highest == nil || matching.Priority > highest.Priority { - highest = matching - endpoint = href + // 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 + } } } } diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/pagination/pager.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/pagination/pager.go index 6f1609ef2..42c0b2dbe 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/pagination/pager.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/pagination/pager.go @@ -22,7 +22,6 @@ var ( // 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) @@ -42,6 +41,8 @@ type Pager struct { createPage func(r PageResult) Page + firstPage Page + Err error // Headers supplies additional HTTP headers to populate on each paged request. @@ -90,9 +91,18 @@ func (p Pager) EachPage(handler func(Page) (bool, error)) error { } currentURL := p.initialURL for { - currentPage, err := p.fetchNextPage(currentURL) - if err != nil { - return err + 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() @@ -129,23 +139,26 @@ func (p Pager) AllPages() (Page, error) { // body will contain the final concatenated Page body. var body reflect.Value - // Grab a test page to ascertain the page body type. - testPage, err := p.fetchNextPage(p.initialURL) + // 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(testPage) + pageType := reflect.TypeOf(firstPage) - // if it's a single page, just return the testPage (first page) + // if it's a single page, just return the firstPage (first page) if _, found := pageType.FieldByName("SinglePageBase"); found { - return testPage, nil + 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 := testPage.GetBody().(type) { + 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 diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/params.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/params.go index e484fe1c1..b9986660c 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/params.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/params.go @@ -10,10 +10,28 @@ import ( "time" ) -// BuildRequestBody builds a map[string]interface from the given `struct`. If -// parent is not the empty string, the final map[string]interface returned will -// encapsulate the built one -// +/* +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 { @@ -97,10 +115,31 @@ func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, } } + 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 := f.Tag.Get("json"); jsonTag != "" { + if jsonTag != "" { jsonTagPieces := strings.Split(jsonTag, ",") if len(jsonTagPieces) > 1 && jsonTagPieces[1] == "omitempty" { if v.CanSet() { @@ -329,12 +368,20 @@ func BuildQueryString(opts interface{}) (*url.URL, error) { 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 { - // Otherwise, the field is not set. - if len(tags) == 2 && tags[1] == "required" { - // And the field is required. Return an error. - return nil, fmt.Errorf("Required query parameter [%s] not set.", f.Name) + // 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) } } } @@ -407,10 +454,9 @@ func BuildHeaders(opts interface{}) (map[string]string, error) { optsMap[tags[0]] = strconv.FormatBool(v.Bool()) } } else { - // Otherwise, the field is not set. - if len(tags) == 2 && tags[1] == "required" { - // And the field is required. Return an error. - return optsMap, fmt.Errorf("Required header not set.") + // 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) } } } diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/provider_client.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/provider_client.go index f88682381..7455751df 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/provider_client.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/provider_client.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "net/http" "strings" + "sync" ) // DefaultUserAgent is the default User-Agent string set in the request header. @@ -51,6 +52,8 @@ type ProviderClient struct { 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 @@ -68,16 +71,89 @@ type ProviderClient struct { // authentication functions for different Identity service versions. ReauthFunc func() error - Debug bool + mut *sync.RWMutex + + reauthmut *reauthlock +} + +type reauthlock struct { + sync.RWMutex + reauthing bool } // AuthenticatedHeaders returns a map of HTTP headers that are common for all // authenticated service requests. -func (client *ProviderClient) AuthenticatedHeaders() map[string]string { - if client.TokenID == "" { - return map[string]string{} +func (client *ProviderClient) AuthenticatedHeaders() (m map[string]string) { + if client.reauthmut != nil { + client.reauthmut.RLock() + if client.reauthmut.reauthing { + client.reauthmut.RUnlock() + return + } + client.reauthmut.RUnlock() } - return map[string]string{"X-Auth-Token": client.TokenID} + 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 +} + +// 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.mut.Lock() + defer client.mut.Unlock() + + client.reauthmut.Lock() + client.reauthmut.reauthing = true + client.reauthmut.Unlock() + + if previousToken == "" || client.TokenID == previousToken { + err = client.ReauthFunc() + } + + client.reauthmut.Lock() + client.reauthmut.reauthing = false + client.reauthmut.Unlock() + return } // RequestOpts customizes the behavior of the provider.Request() method. @@ -145,10 +221,6 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) } req.Header.Set("Accept", applicationJSON) - for k, v := range client.AuthenticatedHeaders() { - req.Header.Add(k, v) - } - // Set the User-Agent header req.Header.Set("User-Agent", client.UserAgent.Join()) @@ -162,9 +234,16 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) } } + // 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 { @@ -188,9 +267,6 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) if !ok { body, _ := ioutil.ReadAll(resp.Body) resp.Body.Close() - //pc := make([]uintptr, 1) - //runtime.Callers(2, pc) - //f := runtime.FuncForPC(pc[0]) respErr := ErrUnexpectedResponseCode{ URL: url, Method: method, @@ -198,7 +274,6 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) Actual: resp.StatusCode, Body: body, } - //respErr.Function = "gophercloud.ProviderClient.Request" errType := options.ErrorContext switch resp.StatusCode { @@ -209,7 +284,7 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) } case http.StatusUnauthorized: if client.ReauthFunc != nil { - err = client.ReauthFunc() + err = client.Reauthenticate(prereqtok) if err != nil { e := &ErrUnableToReauthenticate{} e.ErrOriginal = respErr @@ -239,6 +314,11 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) 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 { @@ -298,7 +378,7 @@ func defaultOkCodes(method string) []int { case method == "PUT": return []int{201, 202} case method == "PATCH": - return []int{200, 204} + return []int{200, 202, 204} case method == "DELETE": return []int{202, 204} } diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/results.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/results.go index 76c16ef8f..94a16bff0 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/results.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/results.go @@ -78,6 +78,77 @@ func (r Result) extractIntoPtr(to interface{}, label string) error { 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 } @@ -177,9 +248,8 @@ type HeaderResult struct { Result } -// ExtractHeader will return the http.Header and error from the HeaderResult. -// -// header, err := objects.Create(client, "my_container", objects.CreateOpts{}).ExtractHeader() +// 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 @@ -299,6 +369,48 @@ func (jt *JSONRFC3339NoZ) UnmarshalJSON(data []byte) error { 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. diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/service_client.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/service_client.go index 1160fefa7..2734510e1 100644 --- a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/service_client.go +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/service_client.go @@ -28,6 +28,10 @@ type ServiceClient struct { // 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 /. @@ -108,15 +112,39 @@ func (client *ServiceClient) Delete(url string, opts *RequestOpts) (*http.Respon 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) +} diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/utils/LICENSE b/src/cmd/linuxkit/vendor/github.com/gophercloud/utils/LICENSE new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/utils/LICENSE @@ -0,0 +1,201 @@ + 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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/utils/openstack/clientconfig/doc.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/utils/openstack/clientconfig/doc.go new file mode 100644 index 000000000..1f3be2128 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/utils/openstack/clientconfig/doc.go @@ -0,0 +1,49 @@ +/* +Package clientconfig provides convienent functions for creating OpenStack +clients. It is based on the Python os-client-config library. + +See https://docs.openstack.org/os-client-config/latest for details. + +Example to Create a Provider Client From clouds.yaml + + opts := &clientconfig.ClientOpts{ + Name: "hawaii", + } + + pClient, err := clientconfig.AuthenticatedClient(opts) + if err != nil { + panic(err) + } + + +Example to Manually Create a Provider Client + + opts := &clientconfig.ClientOpts{ + AuthInfo: &clientconfig.AuthInfo{ + AuthURL: "https://hi.example.com:5000/v3", + Username: "jdoe", + Password: "password", + ProjectName: "Some Project", + DomainName: "default", + }, + } + + pClient, err := clientconfig.AuthenticatedClient(opts) + if err != nil { + panic(err) + } + + +Example to Create a Service Client from clouds.yaml + + opts := &clientconfig.ClientOpts{ + Name: "hawaii", + } + + computeClient, err := clientconfig.NewServiceClient("compute", opts) + if err != nil { + panic(err) + } + +*/ +package clientconfig diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/utils/openstack/clientconfig/requests.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/utils/openstack/clientconfig/requests.go new file mode 100644 index 000000000..4b38c7a81 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/utils/openstack/clientconfig/requests.go @@ -0,0 +1,763 @@ +package clientconfig + +import ( + "fmt" + "os" + "reflect" + "strings" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack" + + "gopkg.in/yaml.v2" +) + +// AuthType respresents a valid method of authentication. +type AuthType string + +const ( + // AuthPassword defines an unknown version of the password + AuthPassword AuthType = "password" + // AuthToken defined an unknown version of the token + AuthToken AuthType = "token" + + // AuthV2Password defines version 2 of the password + AuthV2Password AuthType = "v2password" + // AuthV2Token defines version 2 of the token + AuthV2Token AuthType = "v2token" + + // AuthV3Password defines version 3 of the password + AuthV3Password AuthType = "v3password" + // AuthV3Token defines version 3 of the token + AuthV3Token AuthType = "v3token" +) + +// ClientOpts represents options to customize the way a client is +// configured. +type ClientOpts struct { + // Cloud is the cloud entry in clouds.yaml to use. + Cloud string + + // EnvPrefix allows a custom environment variable prefix to be used. + EnvPrefix string + + // AuthType specifies the type of authentication to use. + // By default, this is "password". + AuthType AuthType + + // AuthInfo defines the authentication information needed to + // authenticate to a cloud when clouds.yaml isn't used. + AuthInfo *AuthInfo + + // RegionName is the region to create a Service Client in. + // This will override a region in clouds.yaml or can be used + // when authenticating directly with AuthInfo. + RegionName string +} + +// LoadCloudsYAML will load a clouds.yaml file and return the full config. +func LoadCloudsYAML() (map[string]Cloud, error) { + content, err := findAndReadCloudsYAML() + if err != nil { + return nil, err + } + + var clouds Clouds + err = yaml.Unmarshal(content, &clouds) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal yaml: %v", err) + } + + return clouds.Clouds, nil +} + +// LoadSecureCloudsYAML will load a secure.yaml file and return the full config. +func LoadSecureCloudsYAML() (map[string]Cloud, error) { + var secureClouds Clouds + + content, err := findAndReadSecureCloudsYAML() + if err != nil { + if err.Error() == "no secure.yaml file found" { + // secure.yaml is optional so just ignore read error + return secureClouds.Clouds, nil + } + return nil, err + } + + err = yaml.Unmarshal(content, &secureClouds) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal yaml: %v", err) + } + + return secureClouds.Clouds, nil +} + +// LoadPublicCloudsYAML will load a public-clouds.yaml file and return the full config. +func LoadPublicCloudsYAML() (map[string]Cloud, error) { + var publicClouds PublicClouds + + content, err := findAndReadPublicCloudsYAML() + if err != nil { + if err.Error() == "no clouds-public.yaml file found" { + // clouds-public.yaml is optional so just ignore read error + return publicClouds.Clouds, nil + } + + return nil, err + } + + err = yaml.Unmarshal(content, &publicClouds) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal yaml: %v", err) + } + + return publicClouds.Clouds, nil +} + +// GetCloudFromYAML will return a cloud entry from a clouds.yaml file. +func GetCloudFromYAML(opts *ClientOpts) (*Cloud, error) { + clouds, err := LoadCloudsYAML() + if err != nil { + return nil, fmt.Errorf("unable to load clouds.yaml: %s", err) + } + + // Determine which cloud to use. + // First see if a cloud name was explicitly set in opts. + var cloudName string + if opts != nil && opts.Cloud != "" { + cloudName = opts.Cloud + } + + // Next see if a cloud name was specified as an environment variable. + // This is supposed to override an explicit opts setting. + envPrefix := "OS_" + if opts.EnvPrefix != "" { + envPrefix = opts.EnvPrefix + } + + if v := os.Getenv(envPrefix + "CLOUD"); v != "" { + cloudName = v + } + + var cloud *Cloud + if cloudName != "" { + v, ok := clouds[cloudName] + if !ok { + return nil, fmt.Errorf("cloud %s does not exist in clouds.yaml", cloudName) + } + cloud = &v + } + + // If a cloud was not specified, and clouds only contains + // a single entry, use that entry. + if cloudName == "" && len(clouds) == 1 { + for _, v := range clouds { + cloud = &v + } + } + + var cloudIsInCloudsYaml bool + if cloud == nil { + // not an immediate error as it might still be defined in secure.yaml + cloudIsInCloudsYaml = false + } else { + cloudIsInCloudsYaml = true + } + + publicClouds, err := LoadPublicCloudsYAML() + if err != nil { + return nil, fmt.Errorf("unable to load clouds-public.yaml: %s", err) + } + + var profileName = defaultIfEmpty(cloud.Profile, cloud.Cloud) + if profileName != "" { + publicCloud, ok := publicClouds[profileName] + if !ok { + return nil, fmt.Errorf("cloud %s does not exist in clouds-public.yaml", profileName) + } + cloud, err = mergeClouds(cloud, publicCloud) + if err != nil { + return nil, fmt.Errorf("Could not merge information from clouds.yaml and clouds-public.yaml for cloud %s", profileName) + } + } + + secureClouds, err := LoadSecureCloudsYAML() + if err != nil { + return nil, fmt.Errorf("unable to load secure.yaml: %s", err) + } + + if secureClouds != nil { + // If no entry was found in clouds.yaml, no cloud name was specified, + // and only one secureCloud entry exists, use that as the cloud entry. + if !cloudIsInCloudsYaml && cloudName == "" && len(secureClouds) == 1 { + for _, v := range secureClouds { + cloud = &v + } + } + + secureCloud, ok := secureClouds[cloudName] + if !ok && cloud == nil { + // cloud == nil serves two purposes here: + // if no entry in clouds.yaml was found and + // if a single-entry secureCloud wasn't used. + // At this point, no entry could be determined at all. + return nil, fmt.Errorf("Could not find cloud %s", cloudName) + } + + // If secureCloud has content and it differs from the cloud entry, + // merge the two together. + if !reflect.DeepEqual((Cloud{}), secureCloud) && !reflect.DeepEqual(cloud, secureCloud) { + cloud, err = mergeClouds(secureCloud, cloud) + if err != nil { + return nil, fmt.Errorf("unable to merge information from clouds.yaml and secure.yaml") + } + } + } + + // Default is to verify SSL API requests + if cloud.Verify == nil { + iTrue := true + cloud.Verify = &iTrue + } + + // TODO: this is where reading vendor files should go be considered when not found in + // clouds-public.yml + // https://github.com/openstack/openstacksdk/tree/master/openstack/config/vendors + + return cloud, nil +} + +// AuthOptions creates a gophercloud.AuthOptions structure with the +// settings found in a specific cloud entry of a clouds.yaml file or +// based on authentication settings given in ClientOpts. +// +// This attempts to be a single point of entry for all OpenStack authentication. +// +// See http://docs.openstack.org/developer/os-client-config and +// https://github.com/openstack/os-client-config/blob/master/os_client_config/config.py. +func AuthOptions(opts *ClientOpts) (*gophercloud.AuthOptions, error) { + cloud := new(Cloud) + + // If no opts were passed in, create an empty ClientOpts. + if opts == nil { + opts = new(ClientOpts) + } + + // Determine if a clouds.yaml entry should be retrieved. + // Start by figuring out the cloud name. + // First check if one was explicitly specified in opts. + var cloudName string + if opts.Cloud != "" { + cloudName = opts.Cloud + } + + // Next see if a cloud name was specified as an environment variable. + envPrefix := "OS_" + if opts.EnvPrefix != "" { + envPrefix = opts.EnvPrefix + } + + if v := os.Getenv(envPrefix + "CLOUD"); v != "" { + cloudName = v + } + + // If a cloud name was determined, try to look it up in clouds.yaml. + if cloudName != "" { + // Get the requested cloud. + var err error + cloud, err = GetCloudFromYAML(opts) + if err != nil { + return nil, err + } + } + + // If cloud.AuthInfo is nil, then no cloud was specified. + if cloud.AuthInfo == nil { + // If opts.Auth is not nil, then try using the auth settings from it. + if opts.AuthInfo != nil { + cloud.AuthInfo = opts.AuthInfo + } + + // If cloud.AuthInfo is still nil, then set it to an empty Auth struct + // and rely on environment variables to do the authentication. + if cloud.AuthInfo == nil { + cloud.AuthInfo = new(AuthInfo) + } + } + + identityAPI := determineIdentityAPI(cloud, opts) + switch identityAPI { + case "2.0", "2": + return v2auth(cloud, opts) + case "3": + return v3auth(cloud, opts) + } + + return nil, fmt.Errorf("Unable to build AuthOptions") +} + +func determineIdentityAPI(cloud *Cloud, opts *ClientOpts) string { + var identityAPI string + if cloud.IdentityAPIVersion != "" { + identityAPI = cloud.IdentityAPIVersion + } + + envPrefix := "OS_" + if opts != nil && opts.EnvPrefix != "" { + envPrefix = opts.EnvPrefix + } + + if v := os.Getenv(envPrefix + "IDENTITY_API_VERSION"); v != "" { + identityAPI = v + } + + if identityAPI == "" { + if cloud.AuthInfo != nil { + if strings.Contains(cloud.AuthInfo.AuthURL, "v2.0") { + identityAPI = "2.0" + } + + if strings.Contains(cloud.AuthInfo.AuthURL, "v3") { + identityAPI = "3" + } + } + } + + if identityAPI == "" { + switch cloud.AuthType { + case AuthV2Password: + identityAPI = "2.0" + case AuthV2Token: + identityAPI = "2.0" + case AuthV3Password: + identityAPI = "3" + case AuthV3Token: + identityAPI = "3" + } + } + + // If an Identity API version could not be determined, + // default to v3. + if identityAPI == "" { + identityAPI = "3" + } + + return identityAPI +} + +// v2auth creates a v2-compatible gophercloud.AuthOptions struct. +func v2auth(cloud *Cloud, opts *ClientOpts) (*gophercloud.AuthOptions, error) { + // Environment variable overrides. + envPrefix := "OS_" + if opts != nil && opts.EnvPrefix != "" { + envPrefix = opts.EnvPrefix + } + + if cloud.AuthInfo.AuthURL == "" { + if v := os.Getenv(envPrefix + "AUTH_URL"); v != "" { + cloud.AuthInfo.AuthURL = v + } + } + + if cloud.AuthInfo.Token == "" { + if v := os.Getenv(envPrefix + "TOKEN"); v != "" { + cloud.AuthInfo.Token = v + } + + if v := os.Getenv(envPrefix + "AUTH_TOKEN"); v != "" { + cloud.AuthInfo.Token = v + } + } + + if cloud.AuthInfo.Username == "" { + if v := os.Getenv(envPrefix + "USERNAME"); v != "" { + cloud.AuthInfo.Username = v + } + } + + if cloud.AuthInfo.Password == "" { + if v := os.Getenv(envPrefix + "PASSWORD"); v != "" { + cloud.AuthInfo.Password = v + } + } + + if cloud.AuthInfo.ProjectID == "" { + if v := os.Getenv(envPrefix + "TENANT_ID"); v != "" { + cloud.AuthInfo.ProjectID = v + } + + if v := os.Getenv(envPrefix + "PROJECT_ID"); v != "" { + cloud.AuthInfo.ProjectID = v + } + } + + if cloud.AuthInfo.ProjectName == "" { + if v := os.Getenv(envPrefix + "TENANT_NAME"); v != "" { + cloud.AuthInfo.ProjectName = v + } + + if v := os.Getenv(envPrefix + "PROJECT_NAME"); v != "" { + cloud.AuthInfo.ProjectName = v + } + } + + ao := &gophercloud.AuthOptions{ + IdentityEndpoint: cloud.AuthInfo.AuthURL, + TokenID: cloud.AuthInfo.Token, + Username: cloud.AuthInfo.Username, + Password: cloud.AuthInfo.Password, + TenantID: cloud.AuthInfo.ProjectID, + TenantName: cloud.AuthInfo.ProjectName, + } + + return ao, nil +} + +// v3auth creates a v3-compatible gophercloud.AuthOptions struct. +func v3auth(cloud *Cloud, opts *ClientOpts) (*gophercloud.AuthOptions, error) { + // Environment variable overrides. + envPrefix := "OS_" + if opts != nil && opts.EnvPrefix != "" { + envPrefix = opts.EnvPrefix + } + + if cloud.AuthInfo.AuthURL == "" { + if v := os.Getenv(envPrefix + "AUTH_URL"); v != "" { + cloud.AuthInfo.AuthURL = v + } + } + + if cloud.AuthInfo.Token == "" { + if v := os.Getenv(envPrefix + "TOKEN"); v != "" { + cloud.AuthInfo.Token = v + } + + if v := os.Getenv(envPrefix + "AUTH_TOKEN"); v != "" { + cloud.AuthInfo.Token = v + } + } + + if cloud.AuthInfo.Username == "" { + if v := os.Getenv(envPrefix + "USERNAME"); v != "" { + cloud.AuthInfo.Username = v + } + } + + if cloud.AuthInfo.UserID == "" { + if v := os.Getenv(envPrefix + "USER_ID"); v != "" { + cloud.AuthInfo.UserID = v + } + } + + if cloud.AuthInfo.Password == "" { + if v := os.Getenv(envPrefix + "PASSWORD"); v != "" { + cloud.AuthInfo.Password = v + } + } + + if cloud.AuthInfo.ProjectID == "" { + if v := os.Getenv(envPrefix + "TENANT_ID"); v != "" { + cloud.AuthInfo.ProjectID = v + } + + if v := os.Getenv(envPrefix + "PROJECT_ID"); v != "" { + cloud.AuthInfo.ProjectID = v + } + } + + if cloud.AuthInfo.ProjectName == "" { + if v := os.Getenv(envPrefix + "TENANT_NAME"); v != "" { + cloud.AuthInfo.ProjectName = v + } + + if v := os.Getenv(envPrefix + "PROJECT_NAME"); v != "" { + cloud.AuthInfo.ProjectName = v + } + } + + if cloud.AuthInfo.DomainID == "" { + if v := os.Getenv(envPrefix + "DOMAIN_ID"); v != "" { + cloud.AuthInfo.DomainID = v + } + } + + if cloud.AuthInfo.DomainName == "" { + if v := os.Getenv(envPrefix + "DOMAIN_NAME"); v != "" { + cloud.AuthInfo.DomainName = v + } + } + + if cloud.AuthInfo.DefaultDomain == "" { + if v := os.Getenv(envPrefix + "DEFAULT_DOMAIN"); v != "" { + cloud.AuthInfo.DefaultDomain = v + } + } + + if cloud.AuthInfo.ProjectDomainID == "" { + if v := os.Getenv(envPrefix + "PROJECT_DOMAIN_ID"); v != "" { + cloud.AuthInfo.ProjectDomainID = v + } + } + + if cloud.AuthInfo.ProjectDomainName == "" { + if v := os.Getenv(envPrefix + "PROJECT_DOMAIN_NAME"); v != "" { + cloud.AuthInfo.ProjectDomainName = v + } + } + + if cloud.AuthInfo.UserDomainID == "" { + if v := os.Getenv(envPrefix + "USER_DOMAIN_ID"); v != "" { + cloud.AuthInfo.UserDomainID = v + } + } + + if cloud.AuthInfo.UserDomainName == "" { + if v := os.Getenv(envPrefix + "USER_DOMAIN_NAME"); v != "" { + cloud.AuthInfo.UserDomainName = v + } + } + + // Build a scope and try to do it correctly. + // https://github.com/openstack/os-client-config/blob/master/os_client_config/config.py#L595 + scope := new(gophercloud.AuthScope) + + if !isProjectScoped(cloud.AuthInfo) { + if cloud.AuthInfo.DomainID != "" { + scope.DomainID = cloud.AuthInfo.DomainID + } else if cloud.AuthInfo.DomainName != "" { + scope.DomainName = cloud.AuthInfo.DomainName + } + } else { + // If Domain* is set, but UserDomain* or ProjectDomain* aren't, + // then use Domain* as the default setting. + cloud = setDomainIfNeeded(cloud) + + if cloud.AuthInfo.ProjectID != "" { + scope.ProjectID = cloud.AuthInfo.ProjectID + } else { + scope.ProjectName = cloud.AuthInfo.ProjectName + scope.DomainID = cloud.AuthInfo.ProjectDomainID + scope.DomainName = cloud.AuthInfo.ProjectDomainName + } + } + + ao := &gophercloud.AuthOptions{ + Scope: scope, + IdentityEndpoint: cloud.AuthInfo.AuthURL, + TokenID: cloud.AuthInfo.Token, + Username: cloud.AuthInfo.Username, + UserID: cloud.AuthInfo.UserID, + Password: cloud.AuthInfo.Password, + TenantID: cloud.AuthInfo.ProjectID, + TenantName: cloud.AuthInfo.ProjectName, + DomainID: cloud.AuthInfo.UserDomainID, + DomainName: cloud.AuthInfo.UserDomainName, + } + + // If an auth_type of "token" was specified, then make sure + // Gophercloud properly authenticates with a token. This involves + // unsetting a few other auth options. The reason this is done + // here is to wait until all auth settings (both in clouds.yaml + // and via environment variables) are set and then unset them. + if strings.Contains(string(cloud.AuthType), "token") || ao.TokenID != "" { + ao.Username = "" + ao.Password = "" + ao.UserID = "" + ao.DomainID = "" + ao.DomainName = "" + } + + // Check for absolute minimum requirements. + if ao.IdentityEndpoint == "" { + err := gophercloud.ErrMissingInput{Argument: "auth_url"} + return nil, err + } + + return ao, nil +} + +// AuthenticatedClient is a convenience function to get a new provider client +// based on a clouds.yaml entry. +func AuthenticatedClient(opts *ClientOpts) (*gophercloud.ProviderClient, error) { + ao, err := AuthOptions(opts) + if err != nil { + return nil, err + } + + return openstack.AuthenticatedClient(*ao) +} + +// NewServiceClient is a convenience function to get a new service client. +func NewServiceClient(service string, opts *ClientOpts) (*gophercloud.ServiceClient, error) { + cloud := new(Cloud) + + // If no opts were passed in, create an empty ClientOpts. + if opts == nil { + opts = new(ClientOpts) + } + + // Determine if a clouds.yaml entry should be retrieved. + // Start by figuring out the cloud name. + // First check if one was explicitly specified in opts. + var cloudName string + if opts.Cloud != "" { + cloudName = opts.Cloud + } + + // Next see if a cloud name was specified as an environment variable. + envPrefix := "OS_" + if opts.EnvPrefix != "" { + envPrefix = opts.EnvPrefix + } + + if v := os.Getenv(envPrefix + "CLOUD"); v != "" { + cloudName = v + } + + // If a cloud name was determined, try to look it up in clouds.yaml. + if cloudName != "" { + // Get the requested cloud. + var err error + cloud, err = GetCloudFromYAML(opts) + if err != nil { + return nil, err + } + } + + // Get a Provider Client + pClient, err := AuthenticatedClient(opts) + if err != nil { + return nil, err + } + + // Determine the region to use. + // First, check if the REGION_NAME environment variable is set. + var region string + if v := os.Getenv(envPrefix + "REGION_NAME"); v != "" { + region = v + } + + // Next, check if the cloud entry sets a region. + if v := cloud.RegionName; v != "" { + region = v + } + + // Finally, see if one was specified in the ClientOpts. + // If so, this takes precedence. + if v := opts.RegionName; v != "" { + region = v + } + + eo := gophercloud.EndpointOpts{ + Region: region, + } + + switch service { + case "clustering": + return openstack.NewClusteringV1(pClient, eo) + case "compute": + return openstack.NewComputeV2(pClient, eo) + case "container": + return openstack.NewContainerV1(pClient, eo) + case "database": + return openstack.NewDBV1(pClient, eo) + case "dns": + return openstack.NewDNSV2(pClient, eo) + case "identity": + identityVersion := "3" + if v := cloud.IdentityAPIVersion; v != "" { + identityVersion = v + } + + switch identityVersion { + case "v2", "2", "2.0": + return openstack.NewIdentityV2(pClient, eo) + case "v3", "3": + return openstack.NewIdentityV3(pClient, eo) + default: + return nil, fmt.Errorf("invalid identity API version") + } + case "image": + return openstack.NewImageServiceV2(pClient, eo) + case "load-balancer": + return openstack.NewLoadBalancerV2(pClient, eo) + case "network": + return openstack.NewNetworkV2(pClient, eo) + case "object-store": + return openstack.NewObjectStorageV1(pClient, eo) + case "orchestration": + return openstack.NewOrchestrationV1(pClient, eo) + case "sharev2": + return openstack.NewSharedFileSystemV2(pClient, eo) + case "volume": + volumeVersion := "2" + if v := cloud.VolumeAPIVersion; v != "" { + volumeVersion = v + } + + switch volumeVersion { + case "v1", "1": + return openstack.NewBlockStorageV1(pClient, eo) + case "v2", "2": + return openstack.NewBlockStorageV2(pClient, eo) + case "v3", "3": + return openstack.NewBlockStorageV3(pClient, eo) + default: + return nil, fmt.Errorf("invalid volume API version") + } + } + + return nil, fmt.Errorf("unable to create a service client for %s", service) +} + +// isProjectScoped determines if an auth struct is project scoped. +func isProjectScoped(authInfo *AuthInfo) bool { + if authInfo.ProjectID == "" && authInfo.ProjectName == "" { + return false + } + + return true +} + +// setDomainIfNeeded will set a DomainID and DomainName +// to ProjectDomain* and UserDomain* if not already set. +func setDomainIfNeeded(cloud *Cloud) *Cloud { + if cloud.AuthInfo.DomainID != "" { + if cloud.AuthInfo.UserDomainID == "" { + cloud.AuthInfo.UserDomainID = cloud.AuthInfo.DomainID + } + + if cloud.AuthInfo.ProjectDomainID == "" { + cloud.AuthInfo.ProjectDomainID = cloud.AuthInfo.DomainID + } + + cloud.AuthInfo.DomainID = "" + } + + if cloud.AuthInfo.DomainName != "" { + if cloud.AuthInfo.UserDomainName == "" { + cloud.AuthInfo.UserDomainName = cloud.AuthInfo.DomainName + } + + if cloud.AuthInfo.ProjectDomainName == "" { + cloud.AuthInfo.ProjectDomainName = cloud.AuthInfo.DomainName + } + + cloud.AuthInfo.DomainName = "" + } + + // If Domain fields are still not set, and if DefaultDomain has a value, + // set UserDomainID and ProjectDomainID to DefaultDomain. + // https://github.com/openstack/osc-lib/blob/86129e6f88289ef14bfaa3f7c9cdfbea8d9fc944/osc_lib/cli/client_config.py#L117-L146 + if cloud.AuthInfo.DefaultDomain != "" { + if cloud.AuthInfo.UserDomainName == "" && cloud.AuthInfo.UserDomainID == "" { + cloud.AuthInfo.UserDomainID = cloud.AuthInfo.DefaultDomain + } + + if cloud.AuthInfo.ProjectDomainName == "" && cloud.AuthInfo.ProjectDomainID == "" { + cloud.AuthInfo.ProjectDomainID = cloud.AuthInfo.DefaultDomain + } + } + + return cloud +} diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/utils/openstack/clientconfig/results.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/utils/openstack/clientconfig/results.go new file mode 100644 index 000000000..b49367c3c --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/utils/openstack/clientconfig/results.go @@ -0,0 +1,112 @@ +package clientconfig + +// PublicClouds represents a collection of PublicCloud entries in clouds-public.yaml file. +// The format of the clouds-public.yml is documented at +// https://docs.openstack.org/python-openstackclient/latest/configuration/ +type PublicClouds struct { + Clouds map[string]Cloud `yaml:"public-clouds"` +} + +// Clouds represents a collection of Cloud entries in a clouds.yaml file. +// The format of clouds.yaml is documented at +// https://docs.openstack.org/os-client-config/latest/user/configuration.html. +type Clouds struct { + Clouds map[string]Cloud `yaml:"clouds"` +} + +// Cloud represents an entry in a clouds.yaml/public-clouds.yaml/secure.yaml file. +type Cloud struct { + Cloud string `yaml:"cloud"` + Profile string `yaml:"profile"` + AuthInfo *AuthInfo `yaml:"auth"` + AuthType AuthType `yaml:"auth_type"` + RegionName string `yaml:"region_name"` + Regions []interface{} `yaml:"regions"` + + // API Version overrides. + IdentityAPIVersion string `yaml:"identity_api_version"` + VolumeAPIVersion string `yaml:"volume_api_version"` + + // Verify whether or not SSL API requests should be verified. + Verify *bool `yaml:"verify"` + + // CACertFile a path to a CA Cert bundle that can be used as part of + // verifying SSL API requests. + CACertFile string `yaml:"cacert"` + + // ClientCertFile a path to a client certificate to use as part of the SSL + // transaction. + ClientCertFile string `yaml:"cert"` + + // ClientKeyFile a path to a client key to use as part of the SSL + // transaction. + ClientKeyFile string `yaml:"key"` +} + +// AuthInfo represents the auth section of a cloud entry or +// auth options entered explicitly in ClientOpts. +type AuthInfo struct { + // AuthURL is the keystone/identity endpoint URL. + AuthURL string `yaml:"auth_url"` + + // Token is a pre-generated authentication token. + Token string `yaml:"token"` + + // Username is the username of the user. + Username string `yaml:"username"` + + // UserID is the unique ID of a user. + UserID string `yaml:"user_id"` + + // Password is the password of the user. + Password string `yaml:"password"` + + // ProjectName is the common/human-readable name of a project. + // Users can be scoped to a project. + // ProjectName on its own is not enough to ensure a unique scope. It must + // also be combined with either a ProjectDomainName or ProjectDomainID. + // ProjectName cannot be combined with ProjectID in a scope. + ProjectName string `yaml:"project_name"` + + // ProjectID is the unique ID of a project. + // It can be used to scope a user to a specific project. + ProjectID string `yaml:"project_id"` + + // UserDomainName is the name of the domain where a user resides. + // It is used to identify the source domain of a user. + UserDomainName string `yaml:"user_domain_name"` + + // UserDomainID is the unique ID of the domain where a user resides. + // It is used to identify the source domain of a user. + UserDomainID string `yaml:"user_domain_id"` + + // ProjectDomainName is the name of the domain where a project resides. + // It is used to identify the source domain of a project. + // ProjectDomainName can be used in addition to a ProjectName when scoping + // a user to a specific project. + ProjectDomainName string `yaml:"project_domain_name"` + + // ProjectDomainID is the name of the domain where a project resides. + // It is used to identify the source domain of a project. + // ProjectDomainID can be used in addition to a ProjectName when scoping + // a user to a specific project. + ProjectDomainID string `yaml:"project_domain_id"` + + // DomainName is the name of a domain which can be used to identify the + // source domain of either a user or a project. + // If UserDomainName and ProjectDomainName are not specified, then DomainName + // is used as a default choice. + // It can also be used be used to specify a domain-only scope. + DomainName string `yaml:"domain_name"` + + // DomainID is the unique ID of a domain which can be used to identify the + // source domain of eitehr a user or a project. + // If UserDomainID and ProjectDomainID are not specified, then DomainID is + // used as a default choice. + // It can also be used be used to specify a domain-only scope. + DomainID string `yaml:"domain_id"` + + // DefaultDomain is the domain ID to fall back on if no other domain has + // been specified and a domain is required for scope. + DefaultDomain string `yaml:"default_domain"` +} diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/utils/openstack/clientconfig/utils.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/utils/openstack/clientconfig/utils.go new file mode 100644 index 000000000..884e5644e --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/utils/openstack/clientconfig/utils.go @@ -0,0 +1,155 @@ +package clientconfig + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "os/user" + "path/filepath" + "reflect" +) + +// defaultIfEmpty is a helper function to make it cleaner to set default value +// for strings. +func defaultIfEmpty(value string, defaultValue string) string { + if value == "" { + return defaultValue + } + return value +} + +// mergeCLouds merges two Clouds recursively (the AuthInfo also gets merged). +// In case both Clouds define a value, the value in the 'override' cloud takes precedence +func mergeClouds(override, cloud interface{}) (*Cloud, error) { + overrideJson, err := json.Marshal(override) + if err != nil { + return nil, err + } + cloudJson, err := json.Marshal(cloud) + if err != nil { + return nil, err + } + var overrideInterface interface{} + err = json.Unmarshal(overrideJson, &overrideInterface) + if err != nil { + return nil, err + } + var cloudInterface interface{} + err = json.Unmarshal(cloudJson, &cloudInterface) + if err != nil { + return nil, err + } + var mergedCloud Cloud + mergedInterface := mergeInterfaces(overrideInterface, cloudInterface) + mergedJson, err := json.Marshal(mergedInterface) + json.Unmarshal(mergedJson, &mergedCloud) + return &mergedCloud, nil +} + +// merges two interfaces. In cases where a value is defined for both 'overridingInterface' and +// 'inferiorInterface' the value in 'overridingInterface' will take precedence. +func mergeInterfaces(overridingInterface, inferiorInterface interface{}) interface{} { + switch overriding := overridingInterface.(type) { + case map[string]interface{}: + interfaceMap, ok := inferiorInterface.(map[string]interface{}) + if !ok { + return overriding + } + for k, v := range interfaceMap { + if overridingValue, ok := overriding[k]; ok { + overriding[k] = mergeInterfaces(overridingValue, v) + } else { + overriding[k] = v + } + } + case []interface{}: + list, ok := inferiorInterface.([]interface{}) + if !ok { + return overriding + } + for i := range list { + overriding = append(overriding, list[i]) + } + return overriding + case nil: + // mergeClouds(nil, map[string]interface{...}) -> map[string]interface{...} + v, ok := inferiorInterface.(map[string]interface{}) + if ok { + return v + } + } + // We don't want to override with empty values + if reflect.DeepEqual(overridingInterface, nil) || reflect.DeepEqual(reflect.Zero(reflect.TypeOf(overridingInterface)).Interface(), overridingInterface) { + return inferiorInterface + } else { + return overridingInterface + } +} + +// findAndReadCloudsYAML attempts to locate a clouds.yaml file in the following +// locations: +// +// 1. OS_CLIENT_CONFIG_FILE +// 2. Current directory. +// 3. unix-specific user_config_dir (~/.config/openstack/clouds.yaml) +// 4. unix-specific site_config_dir (/etc/openstack/clouds.yaml) +// +// If found, the contents of the file is returned. +func findAndReadCloudsYAML() ([]byte, error) { + // OS_CLIENT_CONFIG_FILE + if v := os.Getenv("OS_CLIENT_CONFIG_FILE"); v != "" { + if ok := fileExists(v); ok { + return ioutil.ReadFile(v) + } + } + + return findAndReadYAML("clouds.yaml") +} + +func findAndReadPublicCloudsYAML() ([]byte, error) { + return findAndReadYAML("clouds-public.yaml") +} + +func findAndReadSecureCloudsYAML() ([]byte, error) { + return findAndReadYAML("secure.yaml") +} + +func findAndReadYAML(yamlFile string) ([]byte, error) { + // current directory + cwd, err := os.Getwd() + if err != nil { + return nil, fmt.Errorf("unable to determine working directory: %s", err) + } + + filename := filepath.Join(cwd, yamlFile) + if ok := fileExists(filename); ok { + return ioutil.ReadFile(filename) + } + + // unix user config directory: ~/.config/openstack. + if currentUser, err := user.Current(); err == nil { + homeDir := currentUser.HomeDir + if homeDir != "" { + filename := filepath.Join(homeDir, ".config/openstack/"+yamlFile) + if ok := fileExists(filename); ok { + return ioutil.ReadFile(filename) + } + } + } + + // unix-specific site config directory: /etc/openstack. + if ok := fileExists("/etc/openstack/" + yamlFile); ok { + return ioutil.ReadFile("/etc/openstack/" + yamlFile) + } + + return nil, fmt.Errorf("no " + yamlFile + " file found") +} + +// fileExists checks for the existence of a file at a given location. +func fileExists(filename string) bool { + if _, err := os.Stat(filename); err == nil { + return true + } + return false +}