Files
client-go/vendor/github.com/gophercloud/gophercloud/FAQ.md
Kubernetes Publisher 1f91834be7 Merge pull request #39587 from zhouhaibing089/openstack-auth-provider
Automatic merge from submit-queue (batch tested with PRs 50087, 39587, 50042, 50241, 49914)

plugin/pkg/client/auth: add openstack auth provider

This is an implementation of auth provider for OpenStack world, just like python-openstackclient, we read the environment variables of a list `OS_*`, and client will cache a token to interact with each components, we can do the same here, the client side can cache a token locally at the first time, and rotate automatically when it expires.

This requires an implementation of token authenticator at server side, refer:

1.  [made by me] https://github.com/kubernetes/kubernetes/pull/25536, I can carry this on when it is fine to go.
2.  [made by @kfox1111] https://github.com/kubernetes/kubernetes/pull/25391

The reason why I want to add this is due to the `client-side` nature, it will be confusing to implement it downstream, we would like to add this support here, and customers can get `kubectl` like they usually do(`brew install kubernetes-cli`), and it will just work.

When this is done, we can deprecate the password keystone authenticator as the following reasons:

1.  as mentioned at some other places, the `domain` is another parameters which should be provided.
2.  in case the user supplies `apikey` and `secrets`, we might want to fill the `UserInfo` with the real name which is not implemented for now.

cc @erictune @liggitt

```
add openstack auth provider
```

Kubernetes-commit: 59b8fa32f129be29f146bfd4888a5d1ab7e71ca5
2017-08-29 12:51:23 +00:00

3.3 KiB

Tips

Implementing default logging and re-authentication attempts

You can implement custom logging and/or limit re-auth attempts by creating a custom HTTP client like the following and setting it as the provider client's HTTP Client (via the gophercloud.ProviderClient.HTTPClient field):

//...

// LogRoundTripper satisfies the http.RoundTripper interface and is used to
// customize the default Gophercloud RoundTripper to allow for logging.
type LogRoundTripper struct {
	rt                http.RoundTripper
	numReauthAttempts int
}

// newHTTPClient return a custom HTTP client that allows for logging relevant
// information before and after the HTTP request.
func newHTTPClient() http.Client {
	return http.Client{
		Transport: &LogRoundTripper{
			rt: http.DefaultTransport,
		},
	}
}

// RoundTrip performs a round-trip HTTP request and logs relevant information about it.
func (lrt *LogRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {
	glog.Infof("Request URL: %s\n", request.URL)

	response, err := lrt.rt.RoundTrip(request)
	if response == nil {
		return nil, err
	}

	if response.StatusCode == http.StatusUnauthorized {
		if lrt.numReauthAttempts == 3 {
			return response, fmt.Errorf("Tried to re-authenticate 3 times with no success.")
		}
		lrt.numReauthAttempts++
	}

	glog.Debugf("Response Status: %s\n", response.Status)

	return response, nil
}

endpoint := "https://127.0.0.1/auth"
pc := openstack.NewClient(endpoint)
pc.HTTPClient = newHTTPClient()

//...

Implementing custom objects

OpenStack request/response objects may differ among variable names or types.

Custom request objects

To pass custom options to a request, implement the desired <ACTION>OptsBuilder interface. For example, to pass in

type MyCreateServerOpts struct {
	Name string
	Size int
}

to servers.Create, simply implement the servers.CreateOptsBuilder interface:

func (o MyCreateServeropts) ToServerCreateMap() (map[string]interface{}, error) {
	return map[string]interface{}{
		"name": o.Name,
		"size": o.Size,
	}, nil
}

create an instance of your custom options object, and pass it to servers.Create:

// ...
myOpts := MyCreateServerOpts{
	Name: "s1",
	Size: "100",
}
server, err := servers.Create(computeClient, myOpts).Extract()
// ...

Custom response objects

Some OpenStack services have extensions. Extensions that are supported in Gophercloud can be combined to create a custom object:

// ...
type MyVolume struct {
  volumes.Volume
  tenantattr.VolumeExt
}

var v struct {
  MyVolume `json:"volume"`
}

err := volumes.Get(client, volID).ExtractInto(&v)
// ...

Overriding default UnmarshalJSON method

For some response objects, a field may be a custom type or may be allowed to take on different types. In these cases, overriding the default UnmarshalJSON method may be necessary. To do this, declare the JSON struct field tag as "-" and create an UnmarshalJSON method on the type:

// ...
type MyVolume struct {
	ID string `json: "id"`
	TimeCreated time.Time `json: "-"`
}

func (r *MyVolume) UnmarshalJSON(b []byte) error {
	type tmp MyVolume
	var s struct {
		tmp
		TimeCreated gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
	}
	err := json.Unmarshal(b, &s)
	if err != nil {
		return err
	}
	*r = Volume(s.tmp)

	r.TimeCreated = time.Time(s.CreatedAt)

	return err
}
// ...