rebase: bump github.com/IBM/keyprotect-go-client from 0.7.0 to 0.8.0

Bumps [github.com/IBM/keyprotect-go-client](https://github.com/IBM/keyprotect-go-client) from 0.7.0 to 0.8.0.
- [Release notes](https://github.com/IBM/keyprotect-go-client/releases)
- [Commits](https://github.com/IBM/keyprotect-go-client/compare/v0.7.0...v0.8.0)

---
updated-dependencies:
- dependency-name: github.com/IBM/keyprotect-go-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot] 2022-06-27 20:15:13 +00:00 committed by mergify[bot]
parent d6719c4d62
commit 4b709310e2
17 changed files with 721 additions and 168 deletions

6
go.mod
View File

@ -3,7 +3,7 @@ module github.com/ceph/ceph-csi
go 1.17 go 1.17
require ( require (
github.com/IBM/keyprotect-go-client v0.7.0 github.com/IBM/keyprotect-go-client v0.8.0
github.com/aws/aws-sdk-go v1.44.28 github.com/aws/aws-sdk-go v1.44.28
github.com/aws/aws-sdk-go-v2/service/sts v1.16.7 github.com/aws/aws-sdk-go-v2/service/sts v1.16.7
github.com/ceph/ceph-csi/api v0.0.0-00010101000000-000000000000 github.com/ceph/ceph-csi/api v0.0.0-00010101000000-000000000000
@ -75,7 +75,7 @@ require (
github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.5.8 // indirect github.com/google/go-cmp v0.5.8 // indirect
github.com/google/gofuzz v1.1.0 // indirect github.com/google/gofuzz v1.1.0 // indirect
github.com/google/uuid v1.1.2 // indirect github.com/google/uuid v1.3.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
@ -83,7 +83,7 @@ require (
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-plugin v1.4.3 // indirect github.com/hashicorp/go-plugin v1.4.3 // indirect
github.com/hashicorp/go-retryablehttp v0.6.6 // indirect github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect

13
go.sum
View File

@ -79,8 +79,8 @@ github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3
github.com/DataDog/zstd v1.4.4/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.4.4/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/GoogleCloudPlatform/k8s-cloud-provider v1.16.1-0.20210702024009-ea6160c1d0e3/go.mod h1:8XasY4ymP2V/tn2OOV9ZadmiTE1FIB/h3W+yNlPttKw= github.com/GoogleCloudPlatform/k8s-cloud-provider v1.16.1-0.20210702024009-ea6160c1d0e3/go.mod h1:8XasY4ymP2V/tn2OOV9ZadmiTE1FIB/h3W+yNlPttKw=
github.com/IBM/keyprotect-go-client v0.5.1/go.mod h1:5TwDM/4FRJq1ZOlwQL1xFahLWQ3TveR88VmL1u3njyI= github.com/IBM/keyprotect-go-client v0.5.1/go.mod h1:5TwDM/4FRJq1ZOlwQL1xFahLWQ3TveR88VmL1u3njyI=
github.com/IBM/keyprotect-go-client v0.7.0 h1:JstSHD14Lp6ihwQseyPuGcs1AjOBjAmcisP0dTBA6A0= github.com/IBM/keyprotect-go-client v0.8.0 h1:IgLKSigHRpCCl5cZjBkOYziUZ9zxn6w9iRh+KA8Siww=
github.com/IBM/keyprotect-go-client v0.7.0/go.mod h1:SVr2ylV/fhSQPDiUjWirN9fsyWFCNNbt8GIT8hPJVjE= github.com/IBM/keyprotect-go-client v0.8.0/go.mod h1:yr8h2noNgU8vcbs+vhqoXp3Lmv73PI0zAc6VMgFvWwM=
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA= github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA=
github.com/Jeffail/gabs v1.1.1 h1:V0uzR08Hj22EX8+8QMhyI9sX2hwRu+/RJhJUmnwda/E= github.com/Jeffail/gabs v1.1.1 h1:V0uzR08Hj22EX8+8QMhyI9sX2hwRu+/RJhJUmnwda/E=
github.com/Jeffail/gabs v1.1.1/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc= github.com/Jeffail/gabs v1.1.1/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
@ -494,8 +494,9 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
@ -582,8 +583,9 @@ github.com/hashicorp/go-raftchunking v0.6.3-0.20191002164813-7e9e8525653a/go.mod
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY= github.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY=
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4=
github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
@ -1695,8 +1697,9 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0=
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=

View File

@ -119,18 +119,18 @@ crkID := key.ID
myDEK := []byte{"thisisadataencryptionkey"} myDEK := []byte{"thisisadataencryptionkey"}
// Do some encryption with myDEK // Do some encryption with myDEK
// Wrap the DEK so we can safely store it // Wrap the DEK so we can safely store it
wrappedDEK, err := client.Wrap(ctx, crkID, myDEK, nil) wrappedDEK, err := client.Wrap(ctx, crkIDOrAlias, myDEK, nil)
// Unwrap the DEK // Unwrap the DEK
dek, err := client.Unwrap(ctx, crkID, wrappedDEK, nil) dek, err := client.Unwrap(ctx, crkIDOrAlias, wrappedDEK, nil)
// Do some encryption/decryption using the DEK // Do some encryption/decryption using the DEK
// Discard the DEK // Discard the DEK
dek = nil dek = nil
``` ```
Note you can also pass additional authentication data (AAD) to wrap and unwrap calls Note you can also pass additional authentication data (AAD) to wrap and unwrap calls
to provide another level of protection for your DEK. The AAD is a string array with to provide another level of protection for your DEK. The AAD is a string array with
each element up to 255 chars. For example: each element up to 255 chars. For example:
```go ```go
@ -138,11 +138,11 @@ myAAD := []string{"First aad string", "second aad string", "third aad string"}
myDEK := []byte{"thisisadataencryptionkey"} myDEK := []byte{"thisisadataencryptionkey"}
// Do some encryption with myDEK // Do some encryption with myDEK
// Wrap the DEK so we can safely store it // Wrap the DEK so we can safely store it
wrappedDEK, err := client.Wrap(ctx, crkID, myDEK, &myAAD) wrappedDEK, err := client.Wrap(ctx, crkIDOrAlias, myDEK, &myAAD)
// Unwrap the DEK // Unwrap the DEK
dek, err := client.Unwrap(ctx, crkID, wrappedDEK, &myAAD) dek, err := client.Unwrap(ctx, crkIDOrAlias, wrappedDEK, &myAAD)
// Do some encryption/decryption using the DEK // Do some encryption/decryption using the DEK
// Discard the DEK // Discard the DEK
dek = nil dek = nil
@ -151,7 +151,7 @@ dek = nil
Have key protect create a DEK for you: Have key protect create a DEK for you:
```go ```go
dek, wrappedDek, err := client.WrapCreateDEK(ctx, crkID, nil) dek, wrappedDek, err := client.WrapCreateDEK(ctx, crkIDOrAlias, nil)
// Do some encrypt/decrypt with the dek // Do some encrypt/decrypt with the dek
// Discard the DEK // Discard the DEK
dek = nil dek = nil
@ -163,7 +163,7 @@ Can also specify AAD:
```go ```go
myAAD := []string{"First aad string", "second aad string", "third aad string"} myAAD := []string{"First aad string", "second aad string", "third aad string"}
dek, wrappedDek, err := client.WrapCreateDEK(ctx, crkID, &myAAD) dek, wrappedDek, err := client.WrapCreateDEK(ctx, crkIDOrAlias, &myAAD)
// Do some encrypt/decrypt with the dek // Do some encrypt/decrypt with the dek
// Discard the DEK // Discard the DEK
dek = nil dek = nil
@ -171,3 +171,46 @@ dek = nil
// Save the wrapped DEK for later. Call Unwrap to use it, make // Save the wrapped DEK for later. Call Unwrap to use it, make
// sure to specify the same AAD. // sure to specify the same AAD.
``` ```
### Fetching List Key Versions With Parameters.
```go
limit := uint32(2)
offset := uint32(0)
totalCount := true
listkeyVersionsOptions := &kp.ListKeyVersionsOptions{
Limit : &limit,
Offset : &offset,
TotalCount : &totalCount,
}
keyVersions, err := client.ListKeyVersions(ctx, "key_id_or_alias", listkeyVersionsOptions)
if err != nil {
fmt.Println(err)
}
fmt.Println(keyVersions)
```
### Fetching List Key With Parameters.
```go
limit := uint32(5)
offset := uint32(0)
extractable := false
keyStates := []kp.KeyState{kp.KeyState(kp.Active), kp.KeyState(kp.Suspended)}
listKeysOptions := &kp.ListKeysOptions{
Limit : &limit,
Offset : &offset,
Extractable : &extractable,
State : keyStates,
}
keys, err := client.ListKeys(ctx, listKeysOptions)
if err != nil {
fmt.Println(err)
}
fmt.Println(keys)
```

View File

@ -32,7 +32,13 @@ import (
"time" "time"
) )
const importTokenEncAlgo = "RSAES_OAEP_SHA_256" // currently the only one supported // EncryptionAlgorithm represents the encryption algorithm used for key creation
const (
// AlgorithmRSAOAEP256 denotes RSA OAEP SHA 256 encryption, supported by KP
AlgorithmRSAOAEP256 string = "RSAES_OAEP_SHA_256"
// AlgorithmRSAOAEP1 denotes RSA OAEP SHA 1 encryption, supported by HPCS
AlgorithmRSAOAEP1 string = "RSAES_OAEP_SHA_1"
)
// ImportTokenCreateRequest represents request parameters for creating a // ImportTokenCreateRequest represents request parameters for creating a
// ImportToken. // ImportToken.

View File

@ -33,9 +33,9 @@ type KeyAliases struct {
// An alias name acts as an identifier just like key ID // An alias name acts as an identifier just like key ID
// For more information please refer to the link below: // For more information please refer to the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-create-key-alias#create-key-alias-api // https://cloud.ibm.com/docs/key-protect?topic=key-protect-create-key-alias#create-key-alias-api
func (c *Client) CreateKeyAlias(ctx context.Context, aliasName, keyID string) (*KeyAlias, error) { func (c *Client) CreateKeyAlias(ctx context.Context, aliasName, idOrAlias string) (*KeyAlias, error) {
req, err := c.newRequest("POST", fmt.Sprintf(requestPath, keyID, aliasName), nil) req, err := c.newRequest("POST", fmt.Sprintf(requestPath, idOrAlias, aliasName), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -56,9 +56,9 @@ func (c *Client) CreateKeyAlias(ctx context.Context, aliasName, keyID string) (*
// DeleteKeyAlias deletes an alias name associated with a key // DeleteKeyAlias deletes an alias name associated with a key
// For more information please refer to the link below: // For more information please refer to the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-create-key-alias#delete-key-alias // https://cloud.ibm.com/docs/key-protect?topic=key-protect-create-key-alias#delete-key-alias
func (c *Client) DeleteKeyAlias(ctx context.Context, aliasName, keyID string) error { func (c *Client) DeleteKeyAlias(ctx context.Context, aliasName, idOrAlias string) error {
req, err := c.newRequest("DELETE", fmt.Sprintf(requestPath, keyID, aliasName), nil) req, err := c.newRequest("DELETE", fmt.Sprintf(requestPath, idOrAlias, aliasName), nil)
if err != nil { if err != nil {
return err return err
} }

View File

@ -21,6 +21,7 @@ import (
"log" "log"
"net/url" "net/url"
"strconv" "strconv"
"strings"
"time" "time"
) )
@ -38,6 +39,17 @@ var (
// PreferReturn designates the value for the "Prefer" header. // PreferReturn designates the value for the "Prefer" header.
type PreferReturn int type PreferReturn int
type KeyState uint32
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-key-states
const (
Active KeyState = iota + 1
Suspended
Deactivated
_
Destroyed
)
// Key represents a key as returned by the KP API. // Key represents a key as returned by the KP API.
type Key struct { type Key struct {
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
@ -65,6 +77,9 @@ type Key struct {
Deleted *bool `json:"deleted,omitempty"` Deleted *bool `json:"deleted,omitempty"`
DeletedBy *string `json:"deletedBy,omitempty"` DeletedBy *string `json:"deletedBy,omitempty"`
DeletionDate *time.Time `json:"deletionDate,omitempty"` DeletionDate *time.Time `json:"deletionDate,omitempty"`
PurgeAllowed *bool `json:"purgeAllowed,omitempty"`
PurgeAllowedFrom *time.Time `json:"purgeAllowedFrom,omitempty"`
PurgeScheduledOn *time.Time `json:"purgeScheduledOn,omitempty"`
DualAuthDelete *DualAuth `json:"dualAuthDelete,omitempty"` DualAuthDelete *DualAuth `json:"dualAuthDelete,omitempty"`
} }
@ -80,13 +95,27 @@ type Keys struct {
Keys []Key `json:"resources"` Keys []Key `json:"resources"`
} }
type KeyVersionsMetadata struct {
CollectionType string `json:"collectionType"`
CollectionTotal *uint32 `json:"collectionTotal"`
TotalCount *uint32 `json:"totalCount,omitempty"`
}
type KeyVersions struct {
Metadata KeyVersionsMetadata `json:"metadata"`
KeyVersion []KeyVersion `json:"resources"`
}
// KeysActionRequest represents request parameters for a key action // KeysActionRequest represents request parameters for a key action
// API call. // API call.
type KeysActionRequest struct { type KeysActionRequest struct {
PlainText string `json:"plaintext,omitempty"` PlainText string `json:"plaintext,omitempty"`
AAD []string `json:"aad,omitempty"` AAD []string `json:"aad,omitempty"`
CipherText string `json:"ciphertext,omitempty"` CipherText string `json:"ciphertext,omitempty"`
Payload string `json:"payload,omitempty"` Payload string `json:"payload,omitempty"`
EncryptedNonce string `json:"encryptedNonce,omitempty"`
IV string `json:"iv,omitempty"`
EncryptionAlgorithm string `json:"encryptionAlgorithm,omitempty"`
} }
type KeyVersion struct { type KeyVersion struct {
@ -101,23 +130,14 @@ func (c *Client) CreateKey(ctx context.Context, name string, expiration *time.Ti
// CreateImportedKey creates a new KP key from the given key material. // CreateImportedKey creates a new KP key from the given key material.
func (c *Client) CreateImportedKey(ctx context.Context, name string, expiration *time.Time, payload, encryptedNonce, iv string, extractable bool) (*Key, error) { func (c *Client) CreateImportedKey(ctx context.Context, name string, expiration *time.Time, payload, encryptedNonce, iv string, extractable bool) (*Key, error) {
key := Key{ key := c.createKeyTemplate(ctx, name, expiration, payload, encryptedNonce, iv, extractable, nil, AlgorithmRSAOAEP256)
Name: name, return c.createKey(ctx, key)
Type: keyType, }
Extractable: extractable,
Payload: payload,
}
if payload != "" && encryptedNonce != "" && iv != "" {
key.EncryptedNonce = encryptedNonce
key.IV = iv
key.EncryptionAlgorithm = importTokenEncAlgo
}
if expiration != nil {
key.Expiration = expiration
}
// CreateImportedKeyWithSHA1 creates a new KP key from the given key material
// using RSAES OAEP SHA 1 as encryption algorithm.
func (c *Client) CreateImportedKeyWithSHA1(ctx context.Context, name string, expiration *time.Time, payload, encryptedNonce, iv string, extractable bool, aliases []string) (*Key, error) {
key := c.createKeyTemplate(ctx, name, expiration, payload, encryptedNonce, iv, extractable, aliases, AlgorithmRSAOAEP1)
return c.createKey(ctx, key) return c.createKey(ctx, key)
} }
@ -160,25 +180,33 @@ func (c *Client) CreateKeyWithAliases(ctx context.Context, name string, expirati
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-import-root-keys#import-root-key-api // https://cloud.ibm.com/docs/key-protect?topic=key-protect-import-root-keys#import-root-key-api
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-import-standard-keys#import-standard-key-gui // https://cloud.ibm.com/docs/key-protect?topic=key-protect-import-standard-keys#import-standard-key-gui
func (c *Client) CreateImportedKeyWithAliases(ctx context.Context, name string, expiration *time.Time, payload, encryptedNonce, iv string, extractable bool, aliases []string) (*Key, error) { func (c *Client) CreateImportedKeyWithAliases(ctx context.Context, name string, expiration *time.Time, payload, encryptedNonce, iv string, extractable bool, aliases []string) (*Key, error) {
key := c.createKeyTemplate(ctx, name, expiration, payload, encryptedNonce, iv, extractable, aliases, AlgorithmRSAOAEP256)
return c.createKey(ctx, key)
}
func (c *Client) createKeyTemplate(ctx context.Context, name string, expiration *time.Time, payload, encryptedNonce, iv string, extractable bool, aliases []string, encryptionAlgorithm string) Key {
key := Key{ key := Key{
Name: name, Name: name,
Type: keyType, Type: keyType,
Extractable: extractable, Extractable: extractable,
Payload: payload, Payload: payload,
Aliases: aliases, }
if aliases != nil {
key.Aliases = aliases
} }
if !extractable && payload != "" && encryptedNonce != "" && iv != "" { if !extractable && payload != "" && encryptedNonce != "" && iv != "" {
key.EncryptedNonce = encryptedNonce key.EncryptedNonce = encryptedNonce
key.IV = iv key.IV = iv
key.EncryptionAlgorithm = importTokenEncAlgo key.EncryptionAlgorithm = encryptionAlgorithm
} }
if expiration != nil { if expiration != nil {
key.Expiration = expiration key.Expiration = expiration
} }
return c.createKey(ctx, key) return key
} }
func (c *Client) createKey(ctx context.Context, key Key) (*Key, error) { func (c *Client) createKey(ctx context.Context, key Key) (*Key, error) {
@ -203,6 +231,36 @@ func (c *Client) createKey(ctx context.Context, key Key) (*Key, error) {
return &keysResponse.Keys[0], nil return &keysResponse.Keys[0], nil
} }
// SetKeyRing method transfers a key associated with one key ring to another key ring
// For more information please refer to the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-grouping-keys#transfer-key-key-ring
func (c *Client) SetKeyRing(ctx context.Context, idOrAlias, newKeyRingID string) (*Key, error) {
if idOrAlias == "" {
return nil, fmt.Errorf("Please provide a valid key ID or alias")
}
if newKeyRingID == "" {
return nil, fmt.Errorf("Please provide a valid key ring id")
}
keyRingRequestBody := struct {
KeyRingID string
}{
KeyRingID: newKeyRingID,
}
req, err := c.newRequest("PATCH", fmt.Sprintf("keys/%s", idOrAlias), keyRingRequestBody)
if err != nil {
return nil, err
}
response := Keys{}
if _, err := c.do(ctx, req, &response); err != nil {
return nil, err
}
return &response.Keys[0], nil
}
// GetKeys retrieves a collection of keys that can be paged through. // GetKeys retrieves a collection of keys that can be paged through.
func (c *Client) GetKeys(ctx context.Context, limit int, offset int) (*Keys, error) { func (c *Client) GetKeys(ctx context.Context, limit int, offset int) (*Keys, error) {
if limit == 0 { if limit == 0 {
@ -228,6 +286,55 @@ func (c *Client) GetKeys(ctx context.Context, limit int, offset int) (*Keys, err
return &keys, nil return &keys, nil
} }
//ListKeysOptions struct to add the query parameters for the List Keys function
type ListKeysOptions struct {
Extractable *bool
Limit *uint32
Offset *uint32
State []KeyState
}
// ListKeys retrieves a list of keys that are stored in your Key Protect service instance.
// https://cloud.ibm.com/apidocs/key-protect#getkeys
func (c *Client) ListKeys(ctx context.Context, listKeysOptions *ListKeysOptions) (*Keys, error) {
req, err := c.newRequest("GET", "keys", nil)
if err != nil {
return nil, err
}
// extracting the query parameters and encoding the same in the request url
if listKeysOptions != nil {
values := req.URL.Query()
if listKeysOptions.Limit != nil {
values.Set("limit", fmt.Sprint(*listKeysOptions.Limit))
}
if listKeysOptions.Offset != nil {
values.Set("offset", fmt.Sprint(*listKeysOptions.Offset))
}
if listKeysOptions.State != nil {
var states []string
for _, i := range listKeysOptions.State {
states = append(states, strconv.Itoa(int(i)))
}
values.Set("state", strings.Join(states, ","))
}
if listKeysOptions.Extractable != nil {
values.Set("extractable", fmt.Sprint(*listKeysOptions.Extractable))
}
req.URL.RawQuery = values.Encode()
}
keys := Keys{}
_, err = c.do(ctx, req, &keys)
if err != nil {
return nil, err
}
return &keys, nil
}
// GetKey retrieves a key by ID or alias name. // GetKey retrieves a key by ID or alias name.
// For more information on Key Alias please refer to the link below // For more information on Key Alias please refer to the link below
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-retrieve-key // https://cloud.ibm.com/docs/key-protect?topic=key-protect-retrieve-key
@ -245,10 +352,10 @@ func (c *Client) GetKeyMetadata(ctx context.Context, idOrAlias string) (*Key, er
return c.getKey(ctx, idOrAlias, "keys/%s/metadata") return c.getKey(ctx, idOrAlias, "keys/%s/metadata")
} }
func (c *Client) getKey(ctx context.Context, id string, path string) (*Key, error) { func (c *Client) getKey(ctx context.Context, idOrAlias string, path string) (*Key, error) {
keys := Keys{} keys := Keys{}
req, err := c.newRequest("GET", fmt.Sprintf(path, id), nil) req, err := c.newRequest("GET", fmt.Sprintf(path, idOrAlias), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -267,10 +374,51 @@ type ForceOpt struct {
Force bool Force bool
} }
// DeleteKey deletes a key resource by specifying the ID of the key. // ListKeyVersionsOptions struct to add the query parameters for the ListKeyVersions function
func (c *Client) DeleteKey(ctx context.Context, id string, prefer PreferReturn, callOpts ...CallOpt) (*Key, error) { type ListKeyVersionsOptions struct {
Limit *uint32
Offset *uint32
TotalCount *bool
}
req, err := c.newRequest("DELETE", fmt.Sprintf("keys/%s", id), nil) // ListKeyVersions gets all the versions of the key resource by specifying ID of the key and/or optional parameters
// https://cloud.ibm.com/apidocs/key-protect#getkeyversions
func (c *Client) ListKeyVersions(ctx context.Context, idOrAlias string, listKeyVersionsOptions *ListKeyVersionsOptions) (*KeyVersions, error) {
keyVersion := KeyVersions{}
// forming the request
req, err := c.newRequest("GET", fmt.Sprintf("keys/%s/versions", idOrAlias), nil)
if err != nil {
return nil, err
}
// extracting the query parameters and encoding the same in the request url
if listKeyVersionsOptions != nil {
values := req.URL.Query()
if listKeyVersionsOptions.Limit != nil {
values.Set("limit", fmt.Sprint(*listKeyVersionsOptions.Limit))
}
if listKeyVersionsOptions.Offset != nil {
values.Set("offset", fmt.Sprint(*listKeyVersionsOptions.Offset))
}
if listKeyVersionsOptions.TotalCount != nil {
values.Set("totalCount", fmt.Sprint(*listKeyVersionsOptions.TotalCount))
}
req.URL.RawQuery = values.Encode()
}
//making a request
_, err = c.do(ctx, req, &keyVersion)
if err != nil {
return nil, err
}
return &keyVersion, nil
}
// DeleteKey deletes a key resource by specifying the ID of the key.
func (c *Client) DeleteKey(ctx context.Context, idOrAlias string, prefer PreferReturn, callOpts ...CallOpt) (*Key, error) {
req, err := c.newRequest("DELETE", fmt.Sprintf("keys/%s", idOrAlias), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -301,12 +449,37 @@ func (c *Client) DeleteKey(ctx context.Context, id string, prefer PreferReturn,
return nil, nil return nil, nil
} }
// Purge key method shreds all the metadata and registrations associated with a key that has been
// deleted. The purge operation is allowed to be performed on a key from 4 hours after its deletion
// and its action is irreversible.
// For more information please refer to the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-delete-keys#delete-keys-key-purge
func (c *Client) PurgeKey(ctx context.Context, idOrAlias string, prefer PreferReturn) (*Key, error) {
req, err := c.newRequest("DELETE", fmt.Sprintf("keys/%s/purge", idOrAlias), nil)
if err != nil {
return nil, err
}
req.Header.Set("Prefer", preferHeaders[prefer])
keys := Keys{}
_, err = c.do(ctx, req, &keys)
if err != nil {
return nil, err
}
if len(keys.Keys) > 0 {
return &keys.Keys[0], nil
}
return nil, nil
}
// RestoreKey method reverts a delete key status to active key // RestoreKey method reverts a delete key status to active key
// This method performs restore of any key from deleted state to active state. // This method performs restore of any key from deleted state to active state.
// For more information please refer to the link below: // For more information please refer to the link below:
// https://cloud.ibm.com/dowcs/key-protect?topic=key-protect-restore-keys // https://cloud.ibm.com/docs/key-protect?topic=key-protect-restore-keys
func (c *Client) RestoreKey(ctx context.Context, id string) (*Key, error) { func (c *Client) RestoreKey(ctx context.Context, idOrAlias string) (*Key, error) {
req, err := c.newRequest("POST", fmt.Sprintf("keys/%s/restore", id), nil) req, err := c.newRequest("POST", fmt.Sprintf("keys/%s/restore", idOrAlias), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -322,17 +495,17 @@ func (c *Client) RestoreKey(ctx context.Context, id string) (*Key, error) {
} }
// Wrap calls the wrap action with the given plain text. // Wrap calls the wrap action with the given plain text.
func (c *Client) Wrap(ctx context.Context, id string, plainText []byte, additionalAuthData *[]string) ([]byte, error) { func (c *Client) Wrap(ctx context.Context, idOrAlias string, plainText []byte, additionalAuthData *[]string) ([]byte, error) {
_, ct, err := c.wrap(ctx, id, plainText, additionalAuthData) _, ct, err := c.wrap(ctx, idOrAlias, plainText, additionalAuthData)
return ct, err return ct, err
} }
// WrapCreateDEK calls the wrap action without plain text. // WrapCreateDEK calls the wrap action without plain text.
func (c *Client) WrapCreateDEK(ctx context.Context, id string, additionalAuthData *[]string) ([]byte, []byte, error) { func (c *Client) WrapCreateDEK(ctx context.Context, idOrAlias string, additionalAuthData *[]string) ([]byte, []byte, error) {
return c.wrap(ctx, id, nil, additionalAuthData) return c.wrap(ctx, idOrAlias, nil, additionalAuthData)
} }
func (c *Client) wrap(ctx context.Context, id string, plainText []byte, additionalAuthData *[]string) ([]byte, []byte, error) { func (c *Client) wrap(ctx context.Context, idOrAlias string, plainText []byte, additionalAuthData *[]string) ([]byte, []byte, error) {
keysActionReq := &KeysActionRequest{} keysActionReq := &KeysActionRequest{}
if plainText != nil { if plainText != nil {
@ -347,7 +520,7 @@ func (c *Client) wrap(ctx context.Context, id string, plainText []byte, addition
keysActionReq.AAD = *additionalAuthData keysActionReq.AAD = *additionalAuthData
} }
keysAction, err := c.doKeysAction(ctx, id, "wrap", keysActionReq) keysAction, err := c.doKeysAction(ctx, idOrAlias, "wrap", keysActionReq)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -359,8 +532,8 @@ func (c *Client) wrap(ctx context.Context, id string, plainText []byte, addition
} }
// Unwrap is deprecated since it returns only plaintext and doesn't know how to handle rotation. // Unwrap is deprecated since it returns only plaintext and doesn't know how to handle rotation.
func (c *Client) Unwrap(ctx context.Context, id string, cipherText []byte, additionalAuthData *[]string) ([]byte, error) { func (c *Client) Unwrap(ctx context.Context, idOrAlias string, cipherText []byte, additionalAuthData *[]string) ([]byte, error) {
plainText, _, err := c.UnwrapV2(ctx, id, cipherText, additionalAuthData) plainText, _, err := c.UnwrapV2(ctx, idOrAlias, cipherText, additionalAuthData)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -368,7 +541,7 @@ func (c *Client) Unwrap(ctx context.Context, id string, cipherText []byte, addit
} }
// Unwrap with rotation support. // Unwrap with rotation support.
func (c *Client) UnwrapV2(ctx context.Context, id string, cipherText []byte, additionalAuthData *[]string) ([]byte, []byte, error) { func (c *Client) UnwrapV2(ctx context.Context, idOrAlias string, cipherText []byte, additionalAuthData *[]string) ([]byte, []byte, error) {
keysAction := &KeysActionRequest{ keysAction := &KeysActionRequest{
CipherText: string(cipherText), CipherText: string(cipherText),
@ -378,7 +551,7 @@ func (c *Client) UnwrapV2(ctx context.Context, id string, cipherText []byte, add
keysAction.AAD = *additionalAuthData keysAction.AAD = *additionalAuthData
} }
respAction, err := c.doKeysAction(ctx, id, "unwrap", keysAction) respAction, err := c.doKeysAction(ctx, idOrAlias, "unwrap", keysAction)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -390,13 +563,13 @@ func (c *Client) UnwrapV2(ctx context.Context, id string, cipherText []byte, add
} }
// Rotate rotates a CRK. // Rotate rotates a CRK.
func (c *Client) Rotate(ctx context.Context, id, payload string) error { func (c *Client) Rotate(ctx context.Context, idOrAlias, payload string) error {
actionReq := &KeysActionRequest{ actionReq := &KeysActionRequest{
Payload: payload, Payload: payload,
} }
_, err := c.doKeysAction(ctx, id, "rotate", actionReq) _, err := c.doKeysAction(ctx, idOrAlias, "rotate", actionReq)
if err != nil { if err != nil {
return err return err
} }
@ -404,12 +577,78 @@ func (c *Client) Rotate(ctx context.Context, id, payload string) error {
return nil return nil
} }
type KeyPayload struct {
payload string
encryptedNonce string
iv string
encryptionAlgorithm string
}
func NewKeyPayload(payload, encryptedNonce, iv string) KeyPayload {
kp := KeyPayload{
payload: payload,
encryptedNonce: encryptedNonce,
iv: iv,
}
return kp
}
// EncryptWithRSA256 sets the encryption algorithm for key create to RSAES_OAEP_SHA_256
// This is the default algorithm for key creation under Key Protect service
func (kp KeyPayload) WithRSA256() KeyPayload {
kp.encryptionAlgorithm = "RSAES_OAEP_SHA_256"
return kp
}
// EncryptWithRSA1 sets the encryption algorithm for key create to RSAES_OAEP_SHA_1
// This algorithm is only supported by the Hyper Protect(HPCS) service
func (kp KeyPayload) WithRSA1() KeyPayload {
kp.encryptionAlgorithm = "RSAES_OAEP_SHA_1"
return kp
}
// RotateV2 methods supports rotation of a root key with or without payload and also rotate a
// securely imported root key.
func (c *Client) RotateV2(ctx context.Context, idOrAlias string, new_key *KeyPayload) error {
var actionReq *KeysActionRequest
if new_key != nil {
actionReq = &KeysActionRequest{
Payload: new_key.payload,
EncryptedNonce: new_key.encryptedNonce,
IV: new_key.iv,
EncryptionAlgorithm: new_key.encryptionAlgorithm,
}
}
_, err := c.doKeysAction(ctx, idOrAlias, "rotate", actionReq)
if err != nil {
return err
}
return nil
}
// SyncAssociatedResources method executes the sync request which verifies and updates
// the resources associated with the key.
// For more information please refer to the link below
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-sync-associated-resources
func (c *Client) SyncAssociatedResources(ctx context.Context, idOrAlias string) error {
req, err := c.newRequest("POST", fmt.Sprintf("keys/%s/actions/sync", idOrAlias), nil)
if err != nil {
return err
}
_, err = c.do(ctx, req, nil)
return err
}
// Disable a key. The key will not be deleted but it will not be active // Disable a key. The key will not be deleted but it will not be active
// and key operations cannot be performed on a disabled key. // and key operations cannot be performed on a disabled key.
// For more information can refer to the Key Protect docs in the link below: // For more information can refer to the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-disable-keys // https://cloud.ibm.com/docs/key-protect?topic=key-protect-disable-keys
func (c *Client) DisableKey(ctx context.Context, id string) error { func (c *Client) DisableKey(ctx context.Context, idOrAlias string) error {
_, err := c.doKeysAction(ctx, id, "disable", nil) _, err := c.doKeysAction(ctx, idOrAlias, "disable", nil)
return err return err
} }
@ -418,8 +657,8 @@ func (c *Client) DisableKey(ctx context.Context, id string) error {
// Note: This does not recover Deleted keys. // Note: This does not recover Deleted keys.
// For more information can refer to the Key Protect docs in the link below: // For more information can refer to the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-disable-keys#enable-api // https://cloud.ibm.com/docs/key-protect?topic=key-protect-disable-keys#enable-api
func (c *Client) EnableKey(ctx context.Context, id string) error { func (c *Client) EnableKey(ctx context.Context, idOrAlias string) error {
_, err := c.doKeysAction(ctx, id, "enable", nil) _, err := c.doKeysAction(ctx, idOrAlias, "enable", nil)
return err return err
} }
@ -427,8 +666,8 @@ func (c *Client) EnableKey(ctx context.Context, id string) error {
// After the key is set to deletion it can be deleted by another user who has Manager access. // After the key is set to deletion it can be deleted by another user who has Manager access.
// For more information refer to the Key Protect docs in the link below: // For more information refer to the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-delete-dual-auth-keys#set-key-deletion-api // https://cloud.ibm.com/docs/key-protect?topic=key-protect-delete-dual-auth-keys#set-key-deletion-api
func (c *Client) InitiateDualAuthDelete(ctx context.Context, id string) error { func (c *Client) InitiateDualAuthDelete(ctx context.Context, idOrAlias string) error {
_, err := c.doKeysAction(ctx, id, "setKeyForDeletion", nil) _, err := c.doKeysAction(ctx, idOrAlias, "setKeyForDeletion", nil)
return err return err
} }
@ -436,25 +675,20 @@ func (c *Client) InitiateDualAuthDelete(ctx context.Context, id string) error {
// be prevented from getting deleted by unsetting the key for deletion. // be prevented from getting deleted by unsetting the key for deletion.
// For more information refer to the Key Protect docs in the link below: // For more information refer to the Key Protect docs in the link below:
//https://cloud.ibm.com/docs/key-protect?topic=key-protect-delete-dual-auth-keys#unset-key-deletion-api //https://cloud.ibm.com/docs/key-protect?topic=key-protect-delete-dual-auth-keys#unset-key-deletion-api
func (c *Client) CancelDualAuthDelete(ctx context.Context, id string) error { func (c *Client) CancelDualAuthDelete(ctx context.Context, idOrAlias string) error {
_, err := c.doKeysAction(ctx, id, "unsetKeyForDeletion", nil) _, err := c.doKeysAction(ctx, idOrAlias, "unsetKeyForDeletion", nil)
return err return err
} }
// doKeysAction calls the KP Client to perform an action on a key. // doKeysAction calls the KP Client to perform an action on a key.
func (c *Client) doKeysAction(ctx context.Context, id string, action string, keysActionReq *KeysActionRequest) (*KeysActionRequest, error) { func (c *Client) doKeysAction(ctx context.Context, idOrAlias string, action string, keysActionReq *KeysActionRequest) (*KeysActionRequest, error) {
keyActionRsp := KeysActionRequest{} keyActionRsp := KeysActionRequest{}
v := url.Values{} req, err := c.newRequest("POST", fmt.Sprintf("keys/%s/actions/%s", idOrAlias, action), keysActionReq)
v.Set("action", action)
req, err := c.newRequest("POST", fmt.Sprintf("keys/%s", id), keysActionReq)
if err != nil { if err != nil {
return nil, err return nil, err
} }
req.URL.RawQuery = v.Encode()
_, err = c.do(ctx, req, &keyActionRsp) _, err = c.do(ctx, req, &keyActionRsp)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -59,15 +59,15 @@ type Policies struct {
Policies []Policy `json:"resources"` Policies []Policy `json:"resources"`
} }
// GetPolicy retrieves a policy by Key ID. This function is // GetPolicy retrieves a policy by Key ID or alias. This function is
// deprecated, as it only returns one policy and does not let you // deprecated, as it only returns one policy and does not let you
// select which policy set it will return. It is kept for backward // select which policy set it will return. It is kept for backward
// compatibility on keys with only one rotation policy. Please update // compatibility on keys with only one rotation policy. Please update
// to use the new GetPolicies or Get<type>Policy functions. // to use the new GetPolicies or Get<type>Policy functions.
func (c *Client) GetPolicy(ctx context.Context, id string) (*Policy, error) { func (c *Client) GetPolicy(ctx context.Context, idOrAlias string) (*Policy, error) {
policyresponse := Policies{} policyresponse := Policies{}
req, err := c.newRequest("GET", fmt.Sprintf("keys/%s/policies", id), nil) req, err := c.newRequest("GET", fmt.Sprintf("keys/%s/policies", idOrAlias), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -84,7 +84,7 @@ func (c *Client) GetPolicy(ctx context.Context, id string) (*Policy, error) {
// the rotation interval needed. This function is deprecated as it will only // the rotation interval needed. This function is deprecated as it will only
// let you set key rotation policies. To set dual auth and other newer policies // let you set key rotation policies. To set dual auth and other newer policies
// on a key, please use the new SetPolicies of Set<type>Policy functions. // on a key, please use the new SetPolicies of Set<type>Policy functions.
func (c *Client) SetPolicy(ctx context.Context, id string, prefer PreferReturn, rotationInterval int) (*Policy, error) { func (c *Client) SetPolicy(ctx context.Context, idOrAlias string, prefer PreferReturn, rotationInterval int) (*Policy, error) {
policy := Policy{ policy := Policy{
Type: policyType, Type: policyType,
@ -103,7 +103,7 @@ func (c *Client) SetPolicy(ctx context.Context, id string, prefer PreferReturn,
policyresponse := Policies{} policyresponse := Policies{}
req, err := c.newRequest("PUT", fmt.Sprintf("keys/%s/policies", id), &policyRequest) req, err := c.newRequest("PUT", fmt.Sprintf("keys/%s/policies", idOrAlias), &policyRequest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -118,11 +118,11 @@ func (c *Client) SetPolicy(ctx context.Context, id string, prefer PreferReturn,
return &policyresponse.Policies[0], nil return &policyresponse.Policies[0], nil
} }
// GetPolicies retrieves all policies details associated with a Key ID. // GetPolicies retrieves all policies details associated with a Key ID or alias.
func (c *Client) GetPolicies(ctx context.Context, id string) ([]Policy, error) { func (c *Client) GetPolicies(ctx context.Context, idOrAlias string) ([]Policy, error) {
policyresponse := Policies{} policyresponse := Policies{}
req, err := c.newRequest("GET", fmt.Sprintf("keys/%s/policies", id), nil) req, err := c.newRequest("GET", fmt.Sprintf("keys/%s/policies", idOrAlias), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -155,10 +155,10 @@ func (c *Client) getPolicy(ctx context.Context, id, policyType string, policyres
// GetRotationPolivy method retrieves rotation policy details of a key // GetRotationPolivy method retrieves rotation policy details of a key
// For more information can refet the Key Protect docs in the link below: // For more information can refet the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-set-rotation-policy#view-rotation-policy-api // https://cloud.ibm.com/docs/key-protect?topic=key-protect-set-rotation-policy#view-rotation-policy-api
func (c *Client) GetRotationPolicy(ctx context.Context, id string) (*Policy, error) { func (c *Client) GetRotationPolicy(ctx context.Context, idOrAlias string) (*Policy, error) {
policyresponse := Policies{} policyresponse := Policies{}
err := c.getPolicy(ctx, id, RotationPolicy, &policyresponse) err := c.getPolicy(ctx, idOrAlias, RotationPolicy, &policyresponse)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -173,10 +173,10 @@ func (c *Client) GetRotationPolicy(ctx context.Context, id string) (*Policy, err
// GetDualAuthDeletePolicy method retrieves dual auth delete policy details of a key // GetDualAuthDeletePolicy method retrieves dual auth delete policy details of a key
// For more information can refer the Key Protect docs in the link below: // For more information can refer the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-set-dual-auth-key-policy#view-dual-auth-key-policy-api // https://cloud.ibm.com/docs/key-protect?topic=key-protect-set-dual-auth-key-policy#view-dual-auth-key-policy-api
func (c *Client) GetDualAuthDeletePolicy(ctx context.Context, id string) (*Policy, error) { func (c *Client) GetDualAuthDeletePolicy(ctx context.Context, idOrAlias string) (*Policy, error) {
policyresponse := Policies{} policyresponse := Policies{}
err := c.getPolicy(ctx, id, DualAuthDelete, &policyresponse) err := c.getPolicy(ctx, idOrAlias, DualAuthDelete, &policyresponse)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -188,10 +188,10 @@ func (c *Client) GetDualAuthDeletePolicy(ctx context.Context, id string) (*Polic
return &policyresponse.Policies[0], nil return &policyresponse.Policies[0], nil
} }
func (c *Client) setPolicy(ctx context.Context, id, policyType string, policyRequest Policies) (*Policies, error) { func (c *Client) setPolicy(ctx context.Context, idOrAlias, policyType string, policyRequest Policies) (*Policies, error) {
policyresponse := Policies{} policyresponse := Policies{}
req, err := c.newRequest("PUT", fmt.Sprintf("keys/%s/policies", id), &policyRequest) req, err := c.newRequest("PUT", fmt.Sprintf("keys/%s/policies", idOrAlias), &policyRequest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -207,10 +207,10 @@ func (c *Client) setPolicy(ctx context.Context, id, policyType string, policyReq
return &policyresponse, nil return &policyresponse, nil
} }
// SetRotationPolicy updates the rotation policy associated with a key by specifying key ID and rotation interval. // SetRotationPolicy updates the rotation policy associated with a key by specifying key ID or alias and rotation interval.
// For more information can refer the Key Protect docs in the link below: // For more information can refer the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-set-rotation-policy#update-rotation-policy-api // https://cloud.ibm.com/docs/key-protect?topic=key-protect-set-rotation-policy#update-rotation-policy-api
func (c *Client) SetRotationPolicy(ctx context.Context, id string, rotationInterval int) (*Policy, error) { func (c *Client) SetRotationPolicy(ctx context.Context, idOrAlias string, rotationInterval int) (*Policy, error) {
policy := Policy{ policy := Policy{
Type: policyType, Type: policyType,
Rotation: &Rotation{ Rotation: &Rotation{
@ -226,7 +226,7 @@ func (c *Client) SetRotationPolicy(ctx context.Context, id string, rotationInter
Policies: []Policy{policy}, Policies: []Policy{policy},
} }
policyresponse, err := c.setPolicy(ctx, id, RotationPolicy, policyRequest) policyresponse, err := c.setPolicy(ctx, idOrAlias, RotationPolicy, policyRequest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -238,10 +238,10 @@ func (c *Client) SetRotationPolicy(ctx context.Context, id string, rotationInter
return &policyresponse.Policies[0], nil return &policyresponse.Policies[0], nil
} }
// SetDualAuthDeletePolicy updates the dual auth delete policy by passing the key ID and enable detail // SetDualAuthDeletePolicy updates the dual auth delete policy by passing the key ID or alias and enable detail
// For more information can refer the Key Protect docs in the link below: // For more information can refer the Key Protect docs in the link below:
// https://cloud.ibm.com/docs/key-protect?topic=key-protect-set-dual-auth-key-policy#create-dual-auth-key-policy-api // https://cloud.ibm.com/docs/key-protect?topic=key-protect-set-dual-auth-key-policy#create-dual-auth-key-policy-api
func (c *Client) SetDualAuthDeletePolicy(ctx context.Context, id string, enabled bool) (*Policy, error) { func (c *Client) SetDualAuthDeletePolicy(ctx context.Context, idOrAlias string, enabled bool) (*Policy, error) {
policy := Policy{ policy := Policy{
Type: policyType, Type: policyType,
DualAuth: &DualAuth{ DualAuth: &DualAuth{
@ -257,7 +257,7 @@ func (c *Client) SetDualAuthDeletePolicy(ctx context.Context, id string, enabled
Policies: []Policy{policy}, Policies: []Policy{policy},
} }
policyresponse, err := c.setPolicy(ctx, id, DualAuthDelete, policyRequest) policyresponse, err := c.setPolicy(ctx, idOrAlias, DualAuthDelete, policyRequest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -273,7 +273,7 @@ func (c *Client) SetDualAuthDeletePolicy(ctx context.Context, id string, enabled
// To set rotation policy for the key pass the setRotationPolicy parameter as true and set the rotationInterval detail. // To set rotation policy for the key pass the setRotationPolicy parameter as true and set the rotationInterval detail.
// To set dual auth delete policy for the key pass the setDualAuthDeletePolicy parameter as true and set the dualAuthEnable detail. // To set dual auth delete policy for the key pass the setDualAuthDeletePolicy parameter as true and set the dualAuthEnable detail.
// Both the policies can be set or either of the policies can be set. // Both the policies can be set or either of the policies can be set.
func (c *Client) SetPolicies(ctx context.Context, id string, setRotationPolicy bool, rotationInterval int, setDualAuthDeletePolicy, dualAuthEnable bool) ([]Policy, error) { func (c *Client) SetPolicies(ctx context.Context, idOrAlias string, setRotationPolicy bool, rotationInterval int, setDualAuthDeletePolicy, dualAuthEnable bool) ([]Policy, error) {
policies := []Policy{} policies := []Policy{}
if setRotationPolicy { if setRotationPolicy {
rotationPolicy := Policy{ rotationPolicy := Policy{
@ -304,7 +304,7 @@ func (c *Client) SetPolicies(ctx context.Context, id string, setRotationPolicy b
policyresponse := Policies{} policyresponse := Policies{}
req, err := c.newRequest("PUT", fmt.Sprintf("keys/%s/policies", id), &policyRequest) req, err := c.newRequest("PUT", fmt.Sprintf("keys/%s/policies", idOrAlias), &policyRequest)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -26,8 +26,8 @@ var (
// NewMD5 and NewSHA1. // NewMD5 and NewSHA1.
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
h.Reset() h.Reset()
h.Write(space[:]) h.Write(space[:]) //nolint:errcheck
h.Write(data) h.Write(data) //nolint:errcheck
s := h.Sum(nil) s := h.Sum(nil)
var uuid UUID var uuid UUID
copy(uuid[:], s) copy(uuid[:], s)

118
vendor/github.com/google/uuid/null.go generated vendored Normal file
View File

@ -0,0 +1,118 @@
// Copyright 2021 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"bytes"
"database/sql/driver"
"encoding/json"
"fmt"
)
var jsonNull = []byte("null")
// NullUUID represents a UUID that may be null.
// NullUUID implements the SQL driver.Scanner interface so
// it can be used as a scan destination:
//
// var u uuid.NullUUID
// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&u)
// ...
// if u.Valid {
// // use u.UUID
// } else {
// // NULL value
// }
//
type NullUUID struct {
UUID UUID
Valid bool // Valid is true if UUID is not NULL
}
// Scan implements the SQL driver.Scanner interface.
func (nu *NullUUID) Scan(value interface{}) error {
if value == nil {
nu.UUID, nu.Valid = Nil, false
return nil
}
err := nu.UUID.Scan(value)
if err != nil {
nu.Valid = false
return err
}
nu.Valid = true
return nil
}
// Value implements the driver Valuer interface.
func (nu NullUUID) Value() (driver.Value, error) {
if !nu.Valid {
return nil, nil
}
// Delegate to UUID Value function
return nu.UUID.Value()
}
// MarshalBinary implements encoding.BinaryMarshaler.
func (nu NullUUID) MarshalBinary() ([]byte, error) {
if nu.Valid {
return nu.UUID[:], nil
}
return []byte(nil), nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (nu *NullUUID) UnmarshalBinary(data []byte) error {
if len(data) != 16 {
return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
}
copy(nu.UUID[:], data)
nu.Valid = true
return nil
}
// MarshalText implements encoding.TextMarshaler.
func (nu NullUUID) MarshalText() ([]byte, error) {
if nu.Valid {
return nu.UUID.MarshalText()
}
return jsonNull, nil
}
// UnmarshalText implements encoding.TextUnmarshaler.
func (nu *NullUUID) UnmarshalText(data []byte) error {
id, err := ParseBytes(data)
if err != nil {
nu.Valid = false
return err
}
nu.UUID = id
nu.Valid = true
return nil
}
// MarshalJSON implements json.Marshaler.
func (nu NullUUID) MarshalJSON() ([]byte, error) {
if nu.Valid {
return json.Marshal(nu.UUID)
}
return jsonNull, nil
}
// UnmarshalJSON implements json.Unmarshaler.
func (nu *NullUUID) UnmarshalJSON(data []byte) error {
if bytes.Equal(data, jsonNull) {
*nu = NullUUID{}
return nil // valid null UUID
}
err := json.Unmarshal(data, &nu.UUID)
nu.Valid = err == nil
return err
}

View File

@ -9,7 +9,7 @@ import (
"fmt" "fmt"
) )
// Scan implements sql.Scanner so UUIDs can be read from databases transparently // Scan implements sql.Scanner so UUIDs can be read from databases transparently.
// Currently, database types that map to string and []byte are supported. Please // Currently, database types that map to string and []byte are supported. Please
// consult database-specific driver documentation for matching types. // consult database-specific driver documentation for matching types.
func (uuid *UUID) Scan(src interface{}) error { func (uuid *UUID) Scan(src interface{}) error {

View File

@ -12,6 +12,7 @@ import (
"fmt" "fmt"
"io" "io"
"strings" "strings"
"sync"
) )
// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC // A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
@ -33,7 +34,27 @@ const (
Future // Reserved for future definition. Future // Reserved for future definition.
) )
var rander = rand.Reader // random function const randPoolSize = 16 * 16
var (
rander = rand.Reader // random function
poolEnabled = false
poolMu sync.Mutex
poolPos = randPoolSize // protected with poolMu
pool [randPoolSize]byte // protected with poolMu
)
type invalidLengthError struct{ len int }
func (err invalidLengthError) Error() string {
return fmt.Sprintf("invalid UUID length: %d", err.len)
}
// IsInvalidLengthError is matcher function for custom error invalidLengthError
func IsInvalidLengthError(err error) bool {
_, ok := err.(invalidLengthError)
return ok
}
// Parse decodes s into a UUID or returns an error. Both the standard UUID // Parse decodes s into a UUID or returns an error. Both the standard UUID
// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and // forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
@ -68,7 +89,7 @@ func Parse(s string) (UUID, error) {
} }
return uuid, nil return uuid, nil
default: default:
return uuid, fmt.Errorf("invalid UUID length: %d", len(s)) return uuid, invalidLengthError{len(s)}
} }
// s is now at least 36 bytes long // s is now at least 36 bytes long
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
@ -112,7 +133,7 @@ func ParseBytes(b []byte) (UUID, error) {
} }
return uuid, nil return uuid, nil
default: default:
return uuid, fmt.Errorf("invalid UUID length: %d", len(b)) return uuid, invalidLengthError{len(b)}
} }
// s is now at least 36 bytes long // s is now at least 36 bytes long
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
@ -243,3 +264,31 @@ func SetRand(r io.Reader) {
} }
rander = r rander = r
} }
// EnableRandPool enables internal randomness pool used for Random
// (Version 4) UUID generation. The pool contains random bytes read from
// the random number generator on demand in batches. Enabling the pool
// may improve the UUID generation throughput significantly.
//
// Since the pool is stored on the Go heap, this feature may be a bad fit
// for security sensitive applications.
//
// Both EnableRandPool and DisableRandPool are not thread-safe and should
// only be called when there is no possibility that New or any other
// UUID Version 4 generation function will be called concurrently.
func EnableRandPool() {
poolEnabled = true
}
// DisableRandPool disables the randomness pool if it was previously
// enabled with EnableRandPool.
//
// Both EnableRandPool and DisableRandPool are not thread-safe and should
// only be called when there is no possibility that New or any other
// UUID Version 4 generation function will be called concurrently.
func DisableRandPool() {
poolEnabled = false
defer poolMu.Unlock()
poolMu.Lock()
poolPos = randPoolSize
}

View File

@ -14,11 +14,21 @@ func New() UUID {
return Must(NewRandom()) return Must(NewRandom())
} }
// NewString creates a new random UUID and returns it as a string or panics.
// NewString is equivalent to the expression
//
// uuid.New().String()
func NewString() string {
return Must(NewRandom()).String()
}
// NewRandom returns a Random (Version 4) UUID. // NewRandom returns a Random (Version 4) UUID.
// //
// The strength of the UUIDs is based on the strength of the crypto/rand // The strength of the UUIDs is based on the strength of the crypto/rand
// package. // package.
// //
// Uses the randomness pool if it was enabled with EnableRandPool.
//
// A note about uniqueness derived from the UUID Wikipedia entry: // A note about uniqueness derived from the UUID Wikipedia entry:
// //
// Randomly generated UUIDs have 122 random bits. One's annual risk of being // Randomly generated UUIDs have 122 random bits. One's annual risk of being
@ -27,7 +37,10 @@ func New() UUID {
// equivalent to the odds of creating a few tens of trillions of UUIDs in a // equivalent to the odds of creating a few tens of trillions of UUIDs in a
// year and having one duplicate. // year and having one duplicate.
func NewRandom() (UUID, error) { func NewRandom() (UUID, error) {
return NewRandomFromReader(rander) if !poolEnabled {
return NewRandomFromReader(rander)
}
return newRandomFromPool()
} }
// NewRandomFromReader returns a UUID based on bytes read from a given io.Reader. // NewRandomFromReader returns a UUID based on bytes read from a given io.Reader.
@ -41,3 +54,23 @@ func NewRandomFromReader(r io.Reader) (UUID, error) {
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
return uuid, nil return uuid, nil
} }
func newRandomFromPool() (UUID, error) {
var uuid UUID
poolMu.Lock()
if poolPos == randPoolSize {
_, err := io.ReadFull(rander, pool[:])
if err != nil {
poolMu.Unlock()
return Nil, err
}
poolPos = 0
}
copy(uuid[:], pool[poolPos:(poolPos+16)])
poolPos += 16
poolMu.Unlock()
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
return uuid, nil
}

View File

@ -1,12 +0,0 @@
sudo: false
language: go
go:
- 1.12.4
branches:
only:
- master
script: make updatedeps test

View File

@ -26,6 +26,7 @@ fails so that the full request can be attempted again. See the
details. details.
Version 0.6.0 and before are compatible with Go prior to 1.12. From 0.6.1 onward, Go 1.12+ is required. Version 0.6.0 and before are compatible with Go prior to 1.12. From 0.6.1 onward, Go 1.12+ is required.
From 0.6.7 onward, Go 1.13+ is required.
Example Use Example Use
=========== ===========

View File

@ -35,11 +35,12 @@ import (
"net/url" "net/url"
"os" "os"
"regexp" "regexp"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/hashicorp/go-cleanhttp" cleanhttp "github.com/hashicorp/go-cleanhttp"
) )
var ( var (
@ -276,12 +277,16 @@ type Logger interface {
Printf(string, ...interface{}) Printf(string, ...interface{})
} }
// LeveledLogger interface implements the basic methods that a logger library needs // LeveledLogger is an interface that can be implemented by any logger or a
// logger wrapper to provide leveled logging. The methods accept a message
// string and a variadic number of key-value pairs. For log.Printf style
// formatting where message string contains a format specifier, use Logger
// interface.
type LeveledLogger interface { type LeveledLogger interface {
Error(string, ...interface{}) Error(msg string, keysAndValues ...interface{})
Info(string, ...interface{}) Info(msg string, keysAndValues ...interface{})
Debug(string, ...interface{}) Debug(msg string, keysAndValues ...interface{})
Warn(string, ...interface{}) Warn(msg string, keysAndValues ...interface{})
} }
// hookLogger adapts an LeveledLogger to Logger for use by the existing hook functions // hookLogger adapts an LeveledLogger to Logger for use by the existing hook functions
@ -357,6 +362,7 @@ type Client struct {
ErrorHandler ErrorHandler ErrorHandler ErrorHandler
loggerInit sync.Once loggerInit sync.Once
clientInit sync.Once
} }
// NewClient creates a new Client with default settings. // NewClient creates a new Client with default settings.
@ -398,21 +404,39 @@ func DefaultRetryPolicy(ctx context.Context, resp *http.Response, err error) (bo
return false, ctx.Err() return false, ctx.Err()
} }
// don't propagate other errors
shouldRetry, _ := baseRetryPolicy(resp, err)
return shouldRetry, nil
}
// ErrorPropagatedRetryPolicy is the same as DefaultRetryPolicy, except it
// propagates errors back instead of returning nil. This allows you to inspect
// why it decided to retry or not.
func ErrorPropagatedRetryPolicy(ctx context.Context, resp *http.Response, err error) (bool, error) {
// do not retry on context.Canceled or context.DeadlineExceeded
if ctx.Err() != nil {
return false, ctx.Err()
}
return baseRetryPolicy(resp, err)
}
func baseRetryPolicy(resp *http.Response, err error) (bool, error) {
if err != nil { if err != nil {
if v, ok := err.(*url.Error); ok { if v, ok := err.(*url.Error); ok {
// Don't retry if the error was due to too many redirects. // Don't retry if the error was due to too many redirects.
if redirectsErrorRe.MatchString(v.Error()) { if redirectsErrorRe.MatchString(v.Error()) {
return false, nil return false, v
} }
// Don't retry if the error was due to an invalid protocol scheme. // Don't retry if the error was due to an invalid protocol scheme.
if schemeErrorRe.MatchString(v.Error()) { if schemeErrorRe.MatchString(v.Error()) {
return false, nil return false, v
} }
// Don't retry if the error was due to TLS cert verification failure. // Don't retry if the error was due to TLS cert verification failure.
if _, ok := v.Err.(x509.UnknownAuthorityError); ok { if _, ok := v.Err.(x509.UnknownAuthorityError); ok {
return false, nil return false, v
} }
} }
@ -420,12 +444,19 @@ func DefaultRetryPolicy(ctx context.Context, resp *http.Response, err error) (bo
return true, nil return true, nil
} }
// 429 Too Many Requests is recoverable. Sometimes the server puts
// a Retry-After response header to indicate when the server is
// available to start processing request from client.
if resp.StatusCode == http.StatusTooManyRequests {
return true, nil
}
// Check the response code. We retry on 500-range responses to allow // Check the response code. We retry on 500-range responses to allow
// the server time to recover, as 500's are typically not permanent // the server time to recover, as 500's are typically not permanent
// errors and may relate to outages on the server side. This will catch // errors and may relate to outages on the server side. This will catch
// invalid response codes as well, like 0 and 999. // invalid response codes as well, like 0 and 999.
if resp.StatusCode == 0 || (resp.StatusCode >= 500 && resp.StatusCode != 501) { if resp.StatusCode == 0 || (resp.StatusCode >= 500 && resp.StatusCode != 501) {
return true, nil return true, fmt.Errorf("unexpected HTTP status %s", resp.Status)
} }
return false, nil return false, nil
@ -434,7 +465,21 @@ func DefaultRetryPolicy(ctx context.Context, resp *http.Response, err error) (bo
// DefaultBackoff provides a default callback for Client.Backoff which // DefaultBackoff provides a default callback for Client.Backoff which
// will perform exponential backoff based on the attempt number and limited // will perform exponential backoff based on the attempt number and limited
// by the provided minimum and maximum durations. // by the provided minimum and maximum durations.
//
// It also tries to parse Retry-After response header when a http.StatusTooManyRequests
// (HTTP Code 429) is found in the resp parameter. Hence it will return the number of
// seconds the server states it may be ready to process more requests from this client.
func DefaultBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration { func DefaultBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
if resp != nil {
if resp.StatusCode == http.StatusTooManyRequests || resp.StatusCode == http.StatusServiceUnavailable {
if s, ok := resp.Header["Retry-After"]; ok {
if sleep, err := strconv.ParseInt(s[0], 10, 64); err == nil {
return time.Second * time.Duration(sleep)
}
}
}
}
mult := math.Pow(2, float64(attemptNum)) * float64(min) mult := math.Pow(2, float64(attemptNum)) * float64(min)
sleep := time.Duration(mult) sleep := time.Duration(mult)
if float64(sleep) != mult || sleep > max { if float64(sleep) != mult || sleep > max {
@ -490,25 +535,31 @@ func PassthroughErrorHandler(resp *http.Response, err error, _ int) (*http.Respo
// Do wraps calling an HTTP method with retries. // Do wraps calling an HTTP method with retries.
func (c *Client) Do(req *Request) (*http.Response, error) { func (c *Client) Do(req *Request) (*http.Response, error) {
if c.HTTPClient == nil { c.clientInit.Do(func() {
c.HTTPClient = cleanhttp.DefaultPooledClient() if c.HTTPClient == nil {
} c.HTTPClient = cleanhttp.DefaultPooledClient()
}
})
logger := c.logger() logger := c.logger()
if logger != nil { if logger != nil {
switch v := logger.(type) { switch v := logger.(type) {
case Logger:
v.Printf("[DEBUG] %s %s", req.Method, req.URL)
case LeveledLogger: case LeveledLogger:
v.Debug("performing request", "method", req.Method, "url", req.URL) v.Debug("performing request", "method", req.Method, "url", req.URL)
case Logger:
v.Printf("[DEBUG] %s %s", req.Method, req.URL)
} }
} }
var resp *http.Response var resp *http.Response
var err error var attempt int
var shouldRetry bool
var doErr, checkErr error
for i := 0; ; i++ { for i := 0; ; i++ {
attempt++
var code int // HTTP response code var code int // HTTP response code
// Always rewind the request body when non-nil. // Always rewind the request body when non-nil.
@ -527,30 +578,30 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
if c.RequestLogHook != nil { if c.RequestLogHook != nil {
switch v := logger.(type) { switch v := logger.(type) {
case Logger:
c.RequestLogHook(v, req.Request, i)
case LeveledLogger: case LeveledLogger:
c.RequestLogHook(hookLogger{v}, req.Request, i) c.RequestLogHook(hookLogger{v}, req.Request, i)
case Logger:
c.RequestLogHook(v, req.Request, i)
default: default:
c.RequestLogHook(nil, req.Request, i) c.RequestLogHook(nil, req.Request, i)
} }
} }
// Attempt the request // Attempt the request
resp, err = c.HTTPClient.Do(req.Request) resp, doErr = c.HTTPClient.Do(req.Request)
if resp != nil { if resp != nil {
code = resp.StatusCode code = resp.StatusCode
} }
// Check if we should continue with retries. // Check if we should continue with retries.
checkOK, checkErr := c.CheckRetry(req.Context(), resp, err) shouldRetry, checkErr = c.CheckRetry(req.Context(), resp, doErr)
if err != nil { if doErr != nil {
switch v := logger.(type) { switch v := logger.(type) {
case Logger:
v.Printf("[ERR] %s %s request failed: %v", req.Method, req.URL, err)
case LeveledLogger: case LeveledLogger:
v.Error("request failed", "error", err, "method", req.Method, "url", req.URL) v.Error("request failed", "error", doErr, "method", req.Method, "url", req.URL)
case Logger:
v.Printf("[ERR] %s %s request failed: %v", req.Method, req.URL, doErr)
} }
} else { } else {
// Call this here to maintain the behavior of logging all requests, // Call this here to maintain the behavior of logging all requests,
@ -558,23 +609,18 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
if c.ResponseLogHook != nil { if c.ResponseLogHook != nil {
// Call the response logger function if provided. // Call the response logger function if provided.
switch v := logger.(type) { switch v := logger.(type) {
case Logger:
c.ResponseLogHook(v, resp)
case LeveledLogger: case LeveledLogger:
c.ResponseLogHook(hookLogger{v}, resp) c.ResponseLogHook(hookLogger{v}, resp)
case Logger:
c.ResponseLogHook(v, resp)
default: default:
c.ResponseLogHook(nil, resp) c.ResponseLogHook(nil, resp)
} }
} }
} }
// Now decide if we should continue. if !shouldRetry {
if !checkOK { break
if checkErr != nil {
err = checkErr
}
c.HTTPClient.CloseIdleConnections()
return resp, err
} }
// We do this before drainBody because there's no need for the I/O if // We do this before drainBody because there's no need for the I/O if
@ -585,7 +631,7 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
} }
// We're going to retry, consume any response to reuse the connection. // We're going to retry, consume any response to reuse the connection.
if err == nil && resp != nil { if doErr == nil {
c.drainBody(resp.Body) c.drainBody(resp.Body)
} }
@ -596,10 +642,10 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
} }
if logger != nil { if logger != nil {
switch v := logger.(type) { switch v := logger.(type) {
case Logger:
v.Printf("[DEBUG] %s: retrying in %s (%d left)", desc, wait, remain)
case LeveledLogger: case LeveledLogger:
v.Debug("retrying request", "request", desc, "timeout", wait, "remaining", remain) v.Debug("retrying request", "request", desc, "timeout", wait, "remaining", remain)
case Logger:
v.Printf("[DEBUG] %s: retrying in %s (%d left)", desc, wait, remain)
} }
} }
select { select {
@ -608,21 +654,44 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
return nil, req.Context().Err() return nil, req.Context().Err()
case <-time.After(wait): case <-time.After(wait):
} }
// Make shallow copy of http Request so that we can modify its body
// without racing against the closeBody call in persistConn.writeLoop.
httpreq := *req.Request
req.Request = &httpreq
}
// this is the closest we have to success criteria
if doErr == nil && checkErr == nil && !shouldRetry {
return resp, nil
}
defer c.HTTPClient.CloseIdleConnections()
err := doErr
if checkErr != nil {
err = checkErr
} }
if c.ErrorHandler != nil { if c.ErrorHandler != nil {
c.HTTPClient.CloseIdleConnections() return c.ErrorHandler(resp, err, attempt)
return c.ErrorHandler(resp, err, c.RetryMax+1)
} }
// By default, we close the response body and return an error without // By default, we close the response body and return an error without
// returning the response // returning the response
if resp != nil { if resp != nil {
resp.Body.Close() c.drainBody(resp.Body)
} }
c.HTTPClient.CloseIdleConnections()
return nil, fmt.Errorf("%s %s giving up after %d attempts", // this means CheckRetry thought the request was a failure, but didn't
req.Method, req.URL, c.RetryMax+1) // communicate why
if err == nil {
return nil, fmt.Errorf("%s %s giving up after %d attempt(s)",
req.Method, req.URL, attempt)
}
return nil, fmt.Errorf("%s %s giving up after %d attempt(s): %w",
req.Method, req.URL, attempt, err)
} }
// Try to read the response body so we can reuse this connection. // Try to read the response body so we can reuse this connection.
@ -632,10 +701,10 @@ func (c *Client) drainBody(body io.ReadCloser) {
if err != nil { if err != nil {
if c.logger() != nil { if c.logger() != nil {
switch v := c.logger().(type) { switch v := c.logger().(type) {
case Logger:
v.Printf("[ERR] error reading response body: %v", err)
case LeveledLogger: case LeveledLogger:
v.Error("error reading response body", "error", err) v.Error("error reading response body", "error", err)
case Logger:
v.Printf("[ERR] error reading response body: %v", err)
} }
} }
} }

View File

@ -1,7 +1,9 @@
package retryablehttp package retryablehttp
import ( import (
"errors"
"net/http" "net/http"
"net/url"
"sync" "sync"
) )
@ -39,5 +41,12 @@ func (rt *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
} }
// Execute the request. // Execute the request.
return rt.Client.Do(retryableReq) resp, err := rt.Client.Do(retryableReq)
// If we got an error returned by standard library's `Do` method, unwrap it
// otherwise we will wind up erroneously re-nesting the error.
if _, ok := err.(*url.Error); ok {
return resp, errors.Unwrap(err)
}
return resp, err
} }

8
vendor/modules.txt vendored
View File

@ -1,5 +1,5 @@
# github.com/IBM/keyprotect-go-client v0.7.0 # github.com/IBM/keyprotect-go-client v0.8.0
## explicit; go 1.12 ## explicit; go 1.15
github.com/IBM/keyprotect-go-client github.com/IBM/keyprotect-go-client
github.com/IBM/keyprotect-go-client/iam github.com/IBM/keyprotect-go-client/iam
# github.com/PuerkitoBio/purell v1.1.1 # github.com/PuerkitoBio/purell v1.1.1
@ -229,7 +229,7 @@ github.com/google/go-cmp/cmp/internal/value
# github.com/google/gofuzz v1.1.0 # github.com/google/gofuzz v1.1.0
## explicit; go 1.12 ## explicit; go 1.12
github.com/google/gofuzz github.com/google/gofuzz
# github.com/google/uuid v1.1.2 # github.com/google/uuid v1.3.0
## explicit ## explicit
github.com/google/uuid github.com/google/uuid
# github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 # github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
@ -262,7 +262,7 @@ github.com/hashicorp/go-multierror
## explicit; go 1.13 ## explicit; go 1.13
github.com/hashicorp/go-plugin github.com/hashicorp/go-plugin
github.com/hashicorp/go-plugin/internal/plugin github.com/hashicorp/go-plugin/internal/plugin
# github.com/hashicorp/go-retryablehttp v0.6.6 # github.com/hashicorp/go-retryablehttp v0.7.0
## explicit; go 1.13 ## explicit; go 1.13
github.com/hashicorp/go-retryablehttp github.com/hashicorp/go-retryablehttp
# github.com/hashicorp/go-rootcerts v1.0.2 # github.com/hashicorp/go-rootcerts v1.0.2