Remove server-side paging logic for ExternalAccountKeys
This commit is contained in:
parent
6929e31fe0
commit
30859d3c83
6 changed files with 72 additions and 208 deletions
12
acme/db.go
12
acme/db.go
|
@ -21,7 +21,7 @@ type DB interface {
|
|||
|
||||
CreateExternalAccountKey(ctx context.Context, provisionerName, reference string) (*ExternalAccountKey, error)
|
||||
GetExternalAccountKey(ctx context.Context, provisionerName, keyID string) (*ExternalAccountKey, error)
|
||||
GetExternalAccountKeys(ctx context.Context, provisionerName, cursor string, limit int) ([]*ExternalAccountKey, string, error)
|
||||
GetExternalAccountKeys(ctx context.Context, provisionerName string) ([]*ExternalAccountKey, error)
|
||||
GetExternalAccountKeyByReference(ctx context.Context, provisionerName, reference string) (*ExternalAccountKey, error)
|
||||
DeleteExternalAccountKey(ctx context.Context, provisionerName, keyID string) error
|
||||
UpdateExternalAccountKey(ctx context.Context, provisionerName string, eak *ExternalAccountKey) error
|
||||
|
@ -58,7 +58,7 @@ type MockDB struct {
|
|||
|
||||
MockCreateExternalAccountKey func(ctx context.Context, provisionerName, reference string) (*ExternalAccountKey, error)
|
||||
MockGetExternalAccountKey func(ctx context.Context, provisionerName, keyID string) (*ExternalAccountKey, error)
|
||||
MockGetExternalAccountKeys func(ctx context.Context, provisionerName string, cursor string, limit int) ([]*ExternalAccountKey, string, error)
|
||||
MockGetExternalAccountKeys func(ctx context.Context, provisionerName string) ([]*ExternalAccountKey, error)
|
||||
MockGetExternalAccountKeyByReference func(ctx context.Context, provisionerName, reference string) (*ExternalAccountKey, error)
|
||||
MockDeleteExternalAccountKey func(ctx context.Context, provisionerName, keyID string) error
|
||||
MockUpdateExternalAccountKey func(ctx context.Context, provisionerName string, eak *ExternalAccountKey) error
|
||||
|
@ -149,13 +149,13 @@ func (m *MockDB) GetExternalAccountKey(ctx context.Context, provisionerName, key
|
|||
}
|
||||
|
||||
// GetExternalAccountKeys mock
|
||||
func (m *MockDB) GetExternalAccountKeys(ctx context.Context, provisionerName, cursor string, limit int) ([]*ExternalAccountKey, string, error) {
|
||||
func (m *MockDB) GetExternalAccountKeys(ctx context.Context, provisionerName string) ([]*ExternalAccountKey, error) {
|
||||
if m.MockGetExternalAccountKeys != nil {
|
||||
return m.MockGetExternalAccountKeys(ctx, provisionerName, cursor, limit)
|
||||
return m.MockGetExternalAccountKeys(ctx, provisionerName)
|
||||
} else if m.MockError != nil {
|
||||
return nil, "", m.MockError
|
||||
return nil, m.MockError
|
||||
}
|
||||
return m.MockRet1.([]*ExternalAccountKey), "", m.MockError
|
||||
return m.MockRet1.([]*ExternalAccountKey), m.MockError
|
||||
}
|
||||
|
||||
// GetExternalAccountKeyByReference mock
|
||||
|
|
|
@ -258,44 +258,23 @@ func (db *DB) DeleteExternalAccountKey(ctx context.Context, provisionerName, key
|
|||
}
|
||||
|
||||
// GetExternalAccountKeys retrieves all External Account Binding keys for a provisioner
|
||||
func (db *DB) GetExternalAccountKeys(ctx context.Context, provisionerName, cursor string, limit int) ([]*acme.ExternalAccountKey, string, error) {
|
||||
func (db *DB) GetExternalAccountKeys(ctx context.Context, provisionerName string) ([]*acme.ExternalAccountKey, error) {
|
||||
|
||||
// TODO: lookup by provisioner based on index
|
||||
entries, err := db.db.List(externalAccountKeyTable)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// set sane limits; based on the Admin API limits
|
||||
switch {
|
||||
case limit <= 0:
|
||||
limit = 20
|
||||
case limit > 100:
|
||||
limit = 100
|
||||
}
|
||||
|
||||
foundCursorKey := false
|
||||
keys := []*acme.ExternalAccountKey{}
|
||||
for _, entry := range entries { // entries is sorted alphabetically on the key (ID) of the EAK; no need to sort this again.
|
||||
dbeak := new(dbExternalAccountKey)
|
||||
if err = json.Unmarshal(entry.Value, dbeak); err != nil {
|
||||
return nil, "", errors.Wrapf(err, "error unmarshaling external account key %s into ExternalAccountKey", string(entry.Key))
|
||||
return nil, errors.Wrapf(err, "error unmarshaling external account key %s into ExternalAccountKey", string(entry.Key))
|
||||
}
|
||||
if dbeak.Provisioner != provisionerName {
|
||||
continue
|
||||
}
|
||||
// look for the entry pointed to by the cursor (the next item to return) and start selecting items after finding it
|
||||
if cursor != "" && !foundCursorKey {
|
||||
if cursor == dbeak.ID {
|
||||
// from here on, items should be selected for the result.
|
||||
foundCursorKey = true
|
||||
} else {
|
||||
// skip the IDs not matching the cursor to look for.
|
||||
continue
|
||||
}
|
||||
}
|
||||
// return if the limit of items was found in the previous iteration; the next cursor is set to the next item to return
|
||||
if len(keys) == limit {
|
||||
return keys, dbeak.ID, nil
|
||||
}
|
||||
keys = append(keys, &acme.ExternalAccountKey{
|
||||
ID: dbeak.ID,
|
||||
KeyBytes: dbeak.KeyBytes,
|
||||
|
@ -307,7 +286,7 @@ func (db *DB) GetExternalAccountKeys(ctx context.Context, provisionerName, curso
|
|||
})
|
||||
}
|
||||
|
||||
return keys, "", nil
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// GetExternalAccountKeyByReference retrieves an External Account Binding key with unique reference
|
||||
|
|
|
@ -1085,17 +1085,13 @@ func TestDB_GetExternalAccountKeys(t *testing.T) {
|
|||
keyID1 := "keyID1"
|
||||
keyID2 := "keyID2"
|
||||
keyID3 := "keyID3"
|
||||
keyID4 := "keyID4"
|
||||
prov := "acmeProv"
|
||||
ref := "ref"
|
||||
type test struct {
|
||||
db nosql.DB
|
||||
err error
|
||||
cursor string
|
||||
nextCursor string
|
||||
limit int
|
||||
acmeErr *acme.Error
|
||||
eaks []*acme.ExternalAccountKey
|
||||
db nosql.DB
|
||||
err error
|
||||
acmeErr *acme.Error
|
||||
eaks []*acme.ExternalAccountKey
|
||||
}
|
||||
var tests = map[string]func(t *testing.T) test{
|
||||
"ok": func(t *testing.T) test {
|
||||
|
@ -1173,103 +1169,6 @@ func TestDB_GetExternalAccountKeys(t *testing.T) {
|
|||
},
|
||||
}
|
||||
},
|
||||
"ok/paging-single-entry": func(t *testing.T) test {
|
||||
now := clock.Now()
|
||||
dbeak1 := &dbExternalAccountKey{
|
||||
ID: keyID1,
|
||||
Provisioner: prov,
|
||||
Reference: ref,
|
||||
AccountID: "",
|
||||
KeyBytes: []byte{1, 3, 3, 7},
|
||||
CreatedAt: now,
|
||||
}
|
||||
b1, err := json.Marshal(dbeak1)
|
||||
assert.FatalError(t, err)
|
||||
dbeak2 := &dbExternalAccountKey{
|
||||
ID: keyID2,
|
||||
Provisioner: prov,
|
||||
Reference: ref,
|
||||
AccountID: "",
|
||||
KeyBytes: []byte{1, 3, 3, 7},
|
||||
CreatedAt: now,
|
||||
}
|
||||
b2, err := json.Marshal(dbeak2)
|
||||
assert.FatalError(t, err)
|
||||
dbeak3 := &dbExternalAccountKey{
|
||||
ID: keyID3,
|
||||
Provisioner: "differentProvisioner",
|
||||
Reference: ref,
|
||||
AccountID: "",
|
||||
KeyBytes: []byte{1, 3, 3, 7},
|
||||
CreatedAt: now,
|
||||
}
|
||||
b3, err := json.Marshal(dbeak3)
|
||||
assert.FatalError(t, err)
|
||||
dbeak4 := &dbExternalAccountKey{
|
||||
ID: keyID4,
|
||||
Provisioner: prov,
|
||||
Reference: ref,
|
||||
AccountID: "",
|
||||
KeyBytes: []byte{1, 3, 3, 7},
|
||||
CreatedAt: now,
|
||||
}
|
||||
b4, err := json.Marshal(dbeak4)
|
||||
assert.FatalError(t, err)
|
||||
return test{
|
||||
db: &db.MockNoSQLDB{
|
||||
MList: func(bucket []byte) ([]*nosqldb.Entry, error) {
|
||||
assert.Equals(t, bucket, externalAccountKeyTable)
|
||||
return []*nosqldb.Entry{
|
||||
{
|
||||
Bucket: bucket,
|
||||
Key: []byte(keyID1),
|
||||
Value: b1,
|
||||
},
|
||||
{
|
||||
Bucket: bucket,
|
||||
Key: []byte(keyID2),
|
||||
Value: b2,
|
||||
},
|
||||
{
|
||||
Bucket: bucket,
|
||||
Key: []byte(keyID3),
|
||||
Value: b3,
|
||||
},
|
||||
{
|
||||
Bucket: bucket,
|
||||
Key: []byte(keyID4),
|
||||
Value: b4,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
cursor: keyID2,
|
||||
limit: 1,
|
||||
nextCursor: keyID4,
|
||||
eaks: []*acme.ExternalAccountKey{
|
||||
{
|
||||
ID: keyID2,
|
||||
Provisioner: prov,
|
||||
Reference: ref,
|
||||
AccountID: "",
|
||||
KeyBytes: []byte{1, 3, 3, 7},
|
||||
CreatedAt: now,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
"ok/paging-max-limit": func(t *testing.T) test {
|
||||
return test{
|
||||
db: &db.MockNoSQLDB{
|
||||
MList: func(bucket []byte) ([]*nosqldb.Entry, error) {
|
||||
assert.Equals(t, bucket, externalAccountKeyTable)
|
||||
return []*nosqldb.Entry{}, nil
|
||||
},
|
||||
},
|
||||
limit: 1337,
|
||||
eaks: []*acme.ExternalAccountKey{},
|
||||
}
|
||||
},
|
||||
"fail/db.List-error": func(t *testing.T) test {
|
||||
return test{
|
||||
db: &db.MockNoSQLDB{
|
||||
|
@ -1304,7 +1203,7 @@ func TestDB_GetExternalAccountKeys(t *testing.T) {
|
|||
tc := run(t)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
d := DB{db: tc.db}
|
||||
if eaks, nextCursor, err := d.GetExternalAccountKeys(context.Background(), prov, tc.cursor, tc.limit); err != nil {
|
||||
if eaks, err := d.GetExternalAccountKeys(context.Background(), prov); err != nil {
|
||||
switch k := err.(type) {
|
||||
case *acme.Error:
|
||||
if assert.NotNil(t, tc.acmeErr) {
|
||||
|
@ -1330,7 +1229,6 @@ func TestDB_GetExternalAccountKeys(t *testing.T) {
|
|||
assert.Equals(t, eak.AccountID, tc.eaks[i].AccountID)
|
||||
assert.Equals(t, eak.BoundAt, tc.eaks[i].BoundAt)
|
||||
}
|
||||
assert.Equals(t, nextCursor, tc.nextCursor)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -30,8 +30,7 @@ func (r *CreateExternalAccountKeyRequest) Validate() error {
|
|||
|
||||
// GetExternalAccountKeysResponse is the type for GET /admin/acme/eab responses
|
||||
type GetExternalAccountKeysResponse struct {
|
||||
EAKs []*linkedca.EABKey `json:"eaks"`
|
||||
NextCursor string `json:"nextCursor"`
|
||||
EAKs []*linkedca.EABKey `json:"eaks"`
|
||||
}
|
||||
|
||||
// requireEABEnabled is a middleware that ensures ACME EAB is enabled
|
||||
|
@ -149,26 +148,19 @@ func (h *Handler) DeleteExternalAccountKey(w http.ResponseWriter, r *http.Reques
|
|||
api.JSON(w, &DeleteResponse{Status: "ok"})
|
||||
}
|
||||
|
||||
// GetExternalAccountKeys returns a segment of ACME EAB Keys.
|
||||
// GetExternalAccountKeys returns ACME EAB Keys. If a reference is specified,
|
||||
// only the ExternalAccountKey with that reference is returned. Otherwise all
|
||||
// ExternalAccountKeys in the system for a specific provisioner are returned.
|
||||
func (h *Handler) GetExternalAccountKeys(w http.ResponseWriter, r *http.Request) {
|
||||
prov := chi.URLParam(r, "prov")
|
||||
reference := chi.URLParam(r, "ref")
|
||||
|
||||
var (
|
||||
key *acme.ExternalAccountKey
|
||||
keys []*acme.ExternalAccountKey
|
||||
err error
|
||||
cursor string
|
||||
nextCursor string
|
||||
limit int
|
||||
key *acme.ExternalAccountKey
|
||||
keys []*acme.ExternalAccountKey
|
||||
err error
|
||||
)
|
||||
|
||||
if cursor, limit, err = api.ParseCursor(r); err != nil {
|
||||
api.WriteError(w, admin.WrapError(admin.ErrorBadRequestType, err,
|
||||
"error parsing cursor and limit from query params"))
|
||||
return
|
||||
}
|
||||
|
||||
if reference != "" {
|
||||
if key, err = h.acmeDB.GetExternalAccountKeyByReference(r.Context(), prov, reference); err != nil {
|
||||
api.WriteError(w, admin.WrapErrorISE(err, "error retrieving external account key with reference '%s'", reference))
|
||||
|
@ -178,7 +170,7 @@ func (h *Handler) GetExternalAccountKeys(w http.ResponseWriter, r *http.Request)
|
|||
keys = []*acme.ExternalAccountKey{key}
|
||||
}
|
||||
} else {
|
||||
if keys, nextCursor, err = h.acmeDB.GetExternalAccountKeys(r.Context(), prov, cursor, limit); err != nil {
|
||||
if keys, err = h.acmeDB.GetExternalAccountKeys(r.Context(), prov); err != nil {
|
||||
api.WriteError(w, admin.WrapErrorISE(err, "error retrieving external account keys"))
|
||||
return
|
||||
}
|
||||
|
@ -198,7 +190,6 @@ func (h *Handler) GetExternalAccountKeys(w http.ResponseWriter, r *http.Request)
|
|||
}
|
||||
|
||||
api.JSON(w, &GetExternalAccountKeysResponse{
|
||||
EAKs: eaks,
|
||||
NextCursor: nextCursor,
|
||||
EAKs: eaks,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -840,23 +840,6 @@ func TestHandler_GetExternalAccountKeys(t *testing.T) {
|
|||
err *admin.Error
|
||||
}
|
||||
var tests = map[string]func(t *testing.T) test{
|
||||
"fail/parse-cursor": func(t *testing.T) test {
|
||||
chiCtx := chi.NewRouteContext()
|
||||
chiCtx.URLParams.Add("prov", "provName")
|
||||
req := httptest.NewRequest("GET", "/foo?limit=A", nil)
|
||||
ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
|
||||
return test{
|
||||
ctx: ctx,
|
||||
statusCode: 400,
|
||||
req: req,
|
||||
err: &admin.Error{
|
||||
Status: 400,
|
||||
Type: admin.ErrorBadRequestType.String(),
|
||||
Detail: "bad request",
|
||||
Message: "error parsing cursor and limit from query params: limit 'A' is not an integer: strconv.Atoi: parsing \"A\": invalid syntax",
|
||||
},
|
||||
}
|
||||
},
|
||||
"fail/acmeDB.GetExternalAccountKeyByReference": func(t *testing.T) test {
|
||||
chiCtx := chi.NewRouteContext()
|
||||
chiCtx.URLParams.Add("prov", "provName")
|
||||
|
@ -889,11 +872,9 @@ func TestHandler_GetExternalAccountKeys(t *testing.T) {
|
|||
req := httptest.NewRequest("GET", "/foo", nil)
|
||||
ctx := context.WithValue(context.Background(), chi.RouteCtxKey, chiCtx)
|
||||
db := &acme.MockDB{
|
||||
MockGetExternalAccountKeys: func(ctx context.Context, provisionerName, cursor string, limit int) ([]*acme.ExternalAccountKey, string, error) {
|
||||
MockGetExternalAccountKeys: func(ctx context.Context, provisionerName string) ([]*acme.ExternalAccountKey, error) {
|
||||
assert.Equals(t, "provName", provisionerName)
|
||||
assert.Equals(t, "", cursor)
|
||||
assert.Equals(t, 0, limit)
|
||||
return nil, "", errors.New("force")
|
||||
return nil, errors.New("force")
|
||||
},
|
||||
}
|
||||
return test{
|
||||
|
@ -927,8 +908,7 @@ func TestHandler_GetExternalAccountKeys(t *testing.T) {
|
|||
statusCode: 200,
|
||||
req: req,
|
||||
resp: GetExternalAccountKeysResponse{
|
||||
EAKs: []*linkedca.EABKey{},
|
||||
NextCursor: "",
|
||||
EAKs: []*linkedca.EABKey{},
|
||||
},
|
||||
db: db,
|
||||
err: nil,
|
||||
|
@ -968,7 +948,6 @@ func TestHandler_GetExternalAccountKeys(t *testing.T) {
|
|||
BoundAt: timestamppb.New(boundAt),
|
||||
},
|
||||
},
|
||||
NextCursor: "",
|
||||
},
|
||||
db: db,
|
||||
err: nil,
|
||||
|
@ -983,10 +962,8 @@ func TestHandler_GetExternalAccountKeys(t *testing.T) {
|
|||
var boundAt time.Time
|
||||
boundAtSet := time.Now().Add(-12 * time.Hour)
|
||||
db := &acme.MockDB{
|
||||
MockGetExternalAccountKeys: func(ctx context.Context, provisionerName, cursor string, limit int) ([]*acme.ExternalAccountKey, string, error) {
|
||||
MockGetExternalAccountKeys: func(ctx context.Context, provisionerName string) ([]*acme.ExternalAccountKey, error) {
|
||||
assert.Equals(t, "provName", provisionerName)
|
||||
assert.Equals(t, "", cursor)
|
||||
assert.Equals(t, 0, limit)
|
||||
return []*acme.ExternalAccountKey{
|
||||
{
|
||||
ID: "eakID1",
|
||||
|
@ -1011,7 +988,7 @@ func TestHandler_GetExternalAccountKeys(t *testing.T) {
|
|||
BoundAt: boundAtSet,
|
||||
AccountID: "accountID",
|
||||
},
|
||||
}, "nextCursorValue", nil
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
return test{
|
||||
|
@ -1043,7 +1020,6 @@ func TestHandler_GetExternalAccountKeys(t *testing.T) {
|
|||
Account: "accountID",
|
||||
},
|
||||
},
|
||||
NextCursor: "nextCursorValue",
|
||||
},
|
||||
db: db,
|
||||
err: nil,
|
||||
|
@ -1058,10 +1034,8 @@ func TestHandler_GetExternalAccountKeys(t *testing.T) {
|
|||
var boundAt time.Time
|
||||
boundAtSet := time.Now().Add(-12 * time.Hour)
|
||||
db := &acme.MockDB{
|
||||
MockGetExternalAccountKeys: func(ctx context.Context, provisionerName, cursor string, limit int) ([]*acme.ExternalAccountKey, string, error) {
|
||||
MockGetExternalAccountKeys: func(ctx context.Context, provisionerName string) ([]*acme.ExternalAccountKey, error) {
|
||||
assert.Equals(t, "provName", provisionerName)
|
||||
assert.Equals(t, "eakID1", cursor)
|
||||
assert.Equals(t, 10, limit)
|
||||
return []*acme.ExternalAccountKey{
|
||||
{
|
||||
ID: "eakID1",
|
||||
|
@ -1086,7 +1060,7 @@ func TestHandler_GetExternalAccountKeys(t *testing.T) {
|
|||
BoundAt: boundAtSet,
|
||||
AccountID: "accountID",
|
||||
},
|
||||
}, "eakID4", nil
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
return test{
|
||||
|
@ -1118,7 +1092,6 @@ func TestHandler_GetExternalAccountKeys(t *testing.T) {
|
|||
Account: "accountID",
|
||||
},
|
||||
},
|
||||
NextCursor: "eakID4",
|
||||
},
|
||||
db: db,
|
||||
err: nil,
|
||||
|
@ -1166,7 +1139,6 @@ func TestHandler_GetExternalAccountKeys(t *testing.T) {
|
|||
if !cmp.Equal(tc.resp, response, opts...) {
|
||||
t.Errorf("h.GetExternalAccountKeys diff =\n%s", cmp.Diff(tc.resp, response, opts...))
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -560,7 +560,7 @@ retry:
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetExternalAccountKeysPaginate returns a page from the the GET /admin/acme/eab request to the CA.
|
||||
// GetExternalAccountKeysPaginate returns a page from the GET /admin/acme/eab request to the CA.
|
||||
func (c *AdminClient) GetExternalAccountKeysPaginate(provisionerName, reference string, opts ...AdminOption) (*adminAPI.GetExternalAccountKeysResponse, error) {
|
||||
var retried bool
|
||||
o := new(adminOptions)
|
||||
|
@ -669,21 +669,45 @@ retry:
|
|||
|
||||
// GetExternalAccountKeys returns all ACME EAB Keys from the GET /admin/acme/eab request to the CA.
|
||||
func (c *AdminClient) GetExternalAccountKeys(provisionerName, reference string, opts ...AdminOption) ([]*linkedca.EABKey, error) {
|
||||
var (
|
||||
cursor = ""
|
||||
eaks = []*linkedca.EABKey{}
|
||||
)
|
||||
for {
|
||||
resp, err := c.GetExternalAccountKeysPaginate(provisionerName, reference, WithAdminCursor(cursor), WithAdminLimit(100))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
eaks = append(eaks, resp.EAKs...)
|
||||
if resp.NextCursor == "" {
|
||||
return eaks, nil
|
||||
}
|
||||
cursor = resp.NextCursor
|
||||
var retried bool
|
||||
o := new(adminOptions)
|
||||
if err := o.apply(opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p := path.Join(adminURLPrefix, "acme/eab", provisionerName)
|
||||
if reference != "" {
|
||||
p = path.Join(p, "/", reference)
|
||||
}
|
||||
u := c.endpoint.ResolveReference(&url.URL{
|
||||
Path: p,
|
||||
RawQuery: o.rawQuery(),
|
||||
})
|
||||
tok, err := c.generateAdminToken(u.Path)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error generating admin token")
|
||||
}
|
||||
req, err := http.NewRequest("GET", u.String(), http.NoBody)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "create GET %s request failed", u)
|
||||
}
|
||||
req.Header.Add("Authorization", tok)
|
||||
retry:
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "client GET %s failed", u)
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
if !retried && c.retryOnError(resp) {
|
||||
retried = true
|
||||
goto retry
|
||||
}
|
||||
return nil, readAdminError(resp.Body)
|
||||
}
|
||||
var body = new(adminAPI.GetExternalAccountKeysResponse)
|
||||
if err := readJSON(resp.Body, body); err != nil {
|
||||
return nil, errors.Wrapf(err, "error reading %s", u)
|
||||
}
|
||||
return body.EAKs, nil
|
||||
}
|
||||
|
||||
func readAdminError(r io.ReadCloser) error {
|
||||
|
|
Loading…
Reference in a new issue