mirror of https://github.com/docker/docs.git
Add JSON marshalling and unmarshalling specify model types as a field on a Table object
Signed-off-by: Ying Li <ying.li@docker.com>
This commit is contained in:
parent
0826dbe201
commit
2f62e8d260
|
@ -3,6 +3,7 @@ package storage
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
@ -43,6 +44,47 @@ func (r RDBKey) TableName() string {
|
||||||
return "tuf_keys"
|
return "tuf_keys"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gorethink can't handle an UnmarshalJSON function (see https://github.com/dancannon/gorethink/issues/201),
|
||||||
|
// so do this here in an anonymous struct
|
||||||
|
func rdbTUFFileFromJSON(data []byte) (interface{}, error) {
|
||||||
|
a := struct {
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
DeletedAt time.Time `json:"deleted_at"`
|
||||||
|
Gun string `json:"gun"`
|
||||||
|
Role string `json:"role"`
|
||||||
|
Version int `json:"version"`
|
||||||
|
Sha256 string `json:"sha256"`
|
||||||
|
Data []byte `json:"data"`
|
||||||
|
TSchecksum string `json:"timestamp_checksum"`
|
||||||
|
}{}
|
||||||
|
if err := json.Unmarshal(data, &a); err != nil {
|
||||||
|
return RDBTUFFile{}, err
|
||||||
|
}
|
||||||
|
return RDBTUFFile{
|
||||||
|
Timing: rethinkdb.Timing{
|
||||||
|
CreatedAt: a.CreatedAt,
|
||||||
|
UpdatedAt: a.UpdatedAt,
|
||||||
|
DeletedAt: a.DeletedAt,
|
||||||
|
},
|
||||||
|
GunRoleVersion: []interface{}{a.Gun, a.Role, a.Version},
|
||||||
|
Gun: a.Gun,
|
||||||
|
Role: a.Role,
|
||||||
|
Version: a.Version,
|
||||||
|
Sha256: a.Sha256,
|
||||||
|
Data: a.Data,
|
||||||
|
TSchecksum: a.TSchecksum,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func rdbKeyFromJSON(data []byte) (interface{}, error) {
|
||||||
|
rdb := RDBKey{}
|
||||||
|
if err := json.Unmarshal(data, &rdb); err != nil {
|
||||||
|
return RDBKey{}, err
|
||||||
|
}
|
||||||
|
return rdb, nil
|
||||||
|
}
|
||||||
|
|
||||||
// RethinkDB implements a MetaStore against the Rethink Database
|
// RethinkDB implements a MetaStore against the Rethink Database
|
||||||
type RethinkDB struct {
|
type RethinkDB struct {
|
||||||
dbName string
|
dbName string
|
||||||
|
|
|
@ -27,6 +27,7 @@ var (
|
||||||
Config: map[string]string{
|
Config: map[string]string{
|
||||||
"write_acks": "majority",
|
"write_acks": "majority",
|
||||||
},
|
},
|
||||||
|
JSONUnmarshaller: rdbTUFFileFromJSON,
|
||||||
}
|
}
|
||||||
|
|
||||||
// PubKeysRethinkTable is the table definition of notary server's public key information for TUF roles
|
// PubKeysRethinkTable is the table definition of notary server's public key information for TUF roles
|
||||||
|
@ -36,5 +37,6 @@ var (
|
||||||
SecondaryIndexes: map[string][]string{
|
SecondaryIndexes: map[string][]string{
|
||||||
rdbGunRoleIdx: {"gun", "role"},
|
rdbGunRoleIdx: {"gun", "role"},
|
||||||
},
|
},
|
||||||
|
JSONUnmarshaller: rdbKeyFromJSON,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/notary/storage/rethinkdb"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRDBTUFFileMarshalling(t *testing.T) {
|
||||||
|
created := time.Now().AddDate(-1, -1, -1)
|
||||||
|
updated := time.Now().AddDate(0, -5, 0)
|
||||||
|
deleted := time.Time{}
|
||||||
|
data := []byte("Hello world")
|
||||||
|
|
||||||
|
createdMarshalled, err := json.Marshal(created)
|
||||||
|
require.NoError(t, err)
|
||||||
|
updatedMarshalled, err := json.Marshal(updated)
|
||||||
|
require.NoError(t, err)
|
||||||
|
deletedMarshalled, err := json.Marshal(deleted)
|
||||||
|
require.NoError(t, err)
|
||||||
|
dataMarshalled, err := json.Marshal(data)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
jsonBytes := []byte(fmt.Sprintf(`
|
||||||
|
{
|
||||||
|
"created_at": %s,
|
||||||
|
"updated_at": %s,
|
||||||
|
"deleted_at": %s,
|
||||||
|
"gun_role_version": ["completely", "invalid", "garbage"],
|
||||||
|
"gun": "namespaced/name",
|
||||||
|
"role": "timestamp",
|
||||||
|
"version": 5,
|
||||||
|
"sha256": "56ee4a23129fc22c6cb4b4ba5f78d730c91ab6def514e80d807c947bb21f0d63",
|
||||||
|
"data": %s,
|
||||||
|
"timestamp_checksum": "ebe6b6e082c94ef24043f1786a7046432506c3d193a47c299ed48ff4413ad7b0"
|
||||||
|
}
|
||||||
|
`, createdMarshalled, updatedMarshalled, deletedMarshalled, dataMarshalled))
|
||||||
|
|
||||||
|
unmarshalledAnon, err := TUFFilesRethinkTable.JSONUnmarshaller(jsonBytes)
|
||||||
|
require.NoError(t, err)
|
||||||
|
unmarshalled, ok := unmarshalledAnon.(RDBTUFFile)
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
// There is some weirdness with comparing time.Time due to a location pointer,
|
||||||
|
// so let's use time.Time's equal function to compare times, and then re-assign
|
||||||
|
// the timing struct to compare the rest of the RDBTUFFile struct
|
||||||
|
require.True(t, created.Equal(unmarshalled.CreatedAt))
|
||||||
|
require.True(t, updated.Equal(unmarshalled.UpdatedAt))
|
||||||
|
require.True(t, deleted.Equal(unmarshalled.DeletedAt))
|
||||||
|
|
||||||
|
expected := RDBTUFFile{
|
||||||
|
Timing: unmarshalled.Timing,
|
||||||
|
GunRoleVersion: []interface{}{"namespaced/name", "timestamp", 5},
|
||||||
|
Gun: "namespaced/name",
|
||||||
|
Role: "timestamp",
|
||||||
|
Version: 5,
|
||||||
|
Sha256: "56ee4a23129fc22c6cb4b4ba5f78d730c91ab6def514e80d807c947bb21f0d63",
|
||||||
|
Data: data,
|
||||||
|
TSchecksum: "ebe6b6e082c94ef24043f1786a7046432506c3d193a47c299ed48ff4413ad7b0",
|
||||||
|
}
|
||||||
|
require.Equal(t, expected, unmarshalled)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRDBTUFKeyMarshalling(t *testing.T) {
|
||||||
|
rdb := RDBKey{
|
||||||
|
Timing: rethinkdb.Timing{
|
||||||
|
CreatedAt: time.Now().AddDate(-1, -1, -1),
|
||||||
|
UpdatedAt: time.Now().AddDate(0, -5, 0),
|
||||||
|
DeletedAt: time.Time{},
|
||||||
|
},
|
||||||
|
Gun: "namespaced/name",
|
||||||
|
Role: "timestamp",
|
||||||
|
Cipher: "ecdsa",
|
||||||
|
Public: []byte("Hello world"),
|
||||||
|
}
|
||||||
|
jsonBytes, err := json.Marshal(rdb)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
unmarshalledAnon, err := PubKeysRethinkTable.JSONUnmarshaller(jsonBytes)
|
||||||
|
require.NoError(t, err)
|
||||||
|
unmarshalled, ok := unmarshalledAnon.(RDBKey)
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
// There is some weirdness with comparing time.Time due to a location pointer,
|
||||||
|
// so let's use time.Time's equal function to compare times, and then re-assign
|
||||||
|
// the timing struct to compare the rest of the RDBTUFFile struct
|
||||||
|
require.True(t, rdb.CreatedAt.Equal(unmarshalled.CreatedAt))
|
||||||
|
require.True(t, rdb.UpdatedAt.Equal(unmarshalled.UpdatedAt))
|
||||||
|
require.True(t, rdb.DeletedAt.Equal(unmarshalled.DeletedAt))
|
||||||
|
unmarshalled.Timing = rdb.Timing
|
||||||
|
|
||||||
|
require.Equal(t, rdb, unmarshalled)
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package keydbstore
|
package keydbstore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -39,10 +40,46 @@ type RDBPrivateKey struct {
|
||||||
Private string `gorethink:"private"`
|
Private string `gorethink:"private"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gorethink can't handle an UnmarshalJSON function (see https://github.com/dancannon/gorethink/issues/201),
|
||||||
|
// so do this here in an anonymous struct
|
||||||
|
func rdbPrivateKeyFromJSON(data []byte) (interface{}, error) {
|
||||||
|
a := struct {
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
DeletedAt time.Time `json:"deleted_at"`
|
||||||
|
KeyID string `json:"key_id"`
|
||||||
|
EncryptionAlg string `json:"encryption_alg"`
|
||||||
|
KeywrapAlg string `json:"keywrap_alg"`
|
||||||
|
Algorithm string `json:"algorithm"`
|
||||||
|
PassphraseAlias string `json:"passphrase_alias"`
|
||||||
|
Public string `json:"public"`
|
||||||
|
Private string `json:"private"`
|
||||||
|
}{}
|
||||||
|
if err := json.Unmarshal(data, &a); err != nil {
|
||||||
|
return RDBPrivateKey{}, err
|
||||||
|
}
|
||||||
|
return RDBPrivateKey{
|
||||||
|
Timing: rethinkdb.Timing{
|
||||||
|
CreatedAt: a.CreatedAt,
|
||||||
|
UpdatedAt: a.UpdatedAt,
|
||||||
|
DeletedAt: a.DeletedAt,
|
||||||
|
},
|
||||||
|
KeyID: a.KeyID,
|
||||||
|
EncryptionAlg: a.EncryptionAlg,
|
||||||
|
KeywrapAlg: a.KeywrapAlg,
|
||||||
|
Algorithm: a.Algorithm,
|
||||||
|
PassphraseAlias: a.PassphraseAlias,
|
||||||
|
Public: a.Public,
|
||||||
|
Private: a.Private,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// PrivateKeysRethinkTable is the table definition for notary signer's key information
|
// PrivateKeysRethinkTable is the table definition for notary signer's key information
|
||||||
var PrivateKeysRethinkTable = rethinkdb.Table{
|
var PrivateKeysRethinkTable = rethinkdb.Table{
|
||||||
Name: RDBPrivateKey{}.TableName(),
|
Name: RDBPrivateKey{}.TableName(),
|
||||||
PrimaryKey: RDBPrivateKey{}.KeyID,
|
PrimaryKey: RDBPrivateKey{}.KeyID,
|
||||||
|
JSONUnmarshaller: rdbPrivateKeyFromJSON,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName sets a specific table name for our RDBPrivateKey
|
// TableName sets a specific table name for our RDBPrivateKey
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
package keydbstore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRDBTUFFileMarshalling(t *testing.T) {
|
||||||
|
created := time.Now().AddDate(-1, -1, -1)
|
||||||
|
updated := time.Now().AddDate(0, -5, 0)
|
||||||
|
deleted := time.Time{}
|
||||||
|
|
||||||
|
createdMarshalled, err := json.Marshal(created)
|
||||||
|
require.NoError(t, err)
|
||||||
|
updatedMarshalled, err := json.Marshal(updated)
|
||||||
|
require.NoError(t, err)
|
||||||
|
deletedMarshalled, err := json.Marshal(deleted)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
jsonBytes := []byte(fmt.Sprintf(`
|
||||||
|
{
|
||||||
|
"created_at": %s,
|
||||||
|
"updated_at": %s,
|
||||||
|
"deleted_at": %s,
|
||||||
|
"key_id": "56ee4a23129fc22c6cb4b4ba5f78d730c91ab6def514e80d807c947bb21f0d63",
|
||||||
|
"encryption_alg": "A256GCM",
|
||||||
|
"keywrap_alg": "PBES2-HS256+A128KW",
|
||||||
|
"algorithm": "ecdsa",
|
||||||
|
"passphrase_alias": "timestamp_1",
|
||||||
|
"public": "Hello world public",
|
||||||
|
"private": "Hello world private"
|
||||||
|
}
|
||||||
|
`, createdMarshalled, updatedMarshalled, deletedMarshalled))
|
||||||
|
fmt.Println(string(jsonBytes))
|
||||||
|
|
||||||
|
unmarshalledAnon, err := PrivateKeysRethinkTable.JSONUnmarshaller(jsonBytes)
|
||||||
|
require.NoError(t, err)
|
||||||
|
unmarshalled, ok := unmarshalledAnon.(RDBPrivateKey)
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
// There is some weirdness with comparing time.Time due to a location pointer,
|
||||||
|
// so let's use time.Time's equal function to compare times, and then re-assign
|
||||||
|
// the timing struct to compare the rest of the RDBTUFFile struct
|
||||||
|
require.True(t, created.Equal(unmarshalled.CreatedAt))
|
||||||
|
require.True(t, updated.Equal(unmarshalled.UpdatedAt))
|
||||||
|
require.True(t, deleted.Equal(unmarshalled.DeletedAt))
|
||||||
|
|
||||||
|
expected := RDBPrivateKey{
|
||||||
|
Timing: unmarshalled.Timing,
|
||||||
|
KeyID: "56ee4a23129fc22c6cb4b4ba5f78d730c91ab6def514e80d807c947bb21f0d63",
|
||||||
|
EncryptionAlg: "A256GCM",
|
||||||
|
KeywrapAlg: "PBES2-HS256+A128KW",
|
||||||
|
Algorithm: "ecdsa",
|
||||||
|
PassphraseAlias: "timestamp_1",
|
||||||
|
Public: "Hello world public",
|
||||||
|
Private: "Hello world private",
|
||||||
|
}
|
||||||
|
require.Equal(t, expected, unmarshalled)
|
||||||
|
}
|
|
@ -31,6 +31,9 @@ type Table struct {
|
||||||
// on the list of fields in the corrensponding slice value.
|
// on the list of fields in the corrensponding slice value.
|
||||||
SecondaryIndexes map[string][]string
|
SecondaryIndexes map[string][]string
|
||||||
Config map[string]string
|
Config map[string]string
|
||||||
|
//JSONUnmarshaller takes a byte slice representing JSON data and knows how
|
||||||
|
//to unmarshal them into a model representing this table
|
||||||
|
JSONUnmarshaller func([]byte) (interface{}, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t Table) term(dbName string) gorethink.Term {
|
func (t Table) term(dbName string) gorethink.Term {
|
||||||
|
|
Loading…
Reference in New Issue