Skip to content

Commit

Permalink
[knox] add no cache mode (#103)
Browse files Browse the repository at this point in the history
* add no cache mode

* add uncached apiClient type

* embed cached client

* fix new UncachedClient

* embed UncachedClient to cachedClient

* fix build

* fix buildable

* Retry buildable

* Retry buildable

* Retry buildable

* fix buildable for dev_client

* gofmt

* retry buildable

* retry buildable

* gofmt
  • Loading branch information
yoyouwen authored Mar 1, 2024
1 parent d42b583 commit b584dfb
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 51 deletions.
184 changes: 138 additions & 46 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,26 +195,17 @@ type HTTP interface {

// HTTPClient is a client that uses HTTP to talk to Knox.
type HTTPClient struct {
// Host is used as the host for http connections
Host string
//AuthHandler returns the authorization string for authenticating to knox. Users should be prefixed by 0u, machines by 0m. On fail, return empty string.
AuthHandler func() string
// KeyFolder is the location of cached keys on the file system. If empty, does not check for cached keys.
KeyFolder string
// Client is the http client for making network calls
Client HTTP
// Version is the current client version, useful for debugging and sent as a header
Version string
UncachedClient UncachedHTTPClient
}

// NewClient creates a new client to connect to talk to Knox.
func NewClient(host string, client HTTP, authHandler func() string, keyFolder, version string) APIClient {
return &HTTPClient{
Host: host,
Client: client,
AuthHandler: authHandler,
KeyFolder: keyFolder,
Version: version,
KeyFolder: keyFolder,
UncachedClient: NewUncachedClient(host, client, authHandler, version),
}
}

Expand Down Expand Up @@ -244,18 +235,7 @@ func (c *HTTPClient) CacheGetKey(keyID string) (*Key, error) {

// NetworkGetKey gets a knox key by keyID and only uses network without the caches.
func (c *HTTPClient) NetworkGetKey(keyID string) (*Key, error) {
key := &Key{}
err := c.getHTTPData("GET", "/v0/keys/"+keyID+"/", nil, key)
if err != nil {
return nil, err
}

// do not return the invalid format remote keys
if key.ID == "" || key.ACL == nil || key.VersionList == nil || key.VersionHash == "" {
return nil, fmt.Errorf("invalid key content for the remote key")
}

return key, err
return c.UncachedClient.NetworkGetKey(keyID)
}

// GetKey gets a knox key by keyID.
Expand Down Expand Up @@ -292,14 +272,7 @@ func (c *HTTPClient) CacheGetKeyWithStatus(keyID string, status VersionStatus) (
// NetworkGetKeyWithStatus gets a knox key by keyID and given version status (always calls network).
func (c *HTTPClient) NetworkGetKeyWithStatus(keyID string, status VersionStatus) (*Key, error) {
// If clients need to know
s, err := status.MarshalJSON()
if err != nil {
return nil, err
}

key := &Key{}
err = c.getHTTPData("GET", "/v0/keys/"+keyID+"/?status="+string(s), nil, key)
return key, err
return c.UncachedClient.NetworkGetKeyWithStatus(keyID, status)
}

// GetKeyWithStatus gets a knox key by keyID and status (leverages cache).
Expand All @@ -313,6 +286,123 @@ func (c *HTTPClient) GetKeyWithStatus(keyID string, status VersionStatus) (*Key,

// CreateKey creates a knox key with given keyID data and ACL.
func (c *HTTPClient) CreateKey(keyID string, data []byte, acl ACL) (uint64, error) {
return c.UncachedClient.CreateKey(keyID, data, acl)
}

// GetKeys gets all Knox (if empty map) or gets all keys in map that do not match key version hash.
func (c *HTTPClient) GetKeys(keys map[string]string) ([]string, error) {
return c.UncachedClient.GetKeys(keys)
}

// DeleteKey deletes a key from Knox.
func (c HTTPClient) DeleteKey(keyID string) error {
return c.UncachedClient.DeleteKey(keyID)
}

// GetACL gets a knox key by keyID.
func (c *HTTPClient) GetACL(keyID string) (*ACL, error) {
return c.UncachedClient.GetACL(keyID)
}

// PutAccess will add an ACL rule to a specific key.
func (c *HTTPClient) PutAccess(keyID string, a ...Access) error {
return c.UncachedClient.PutAccess(keyID, a...)
}

// AddVersion adds a key version to a specific key.
func (c *HTTPClient) AddVersion(keyID string, data []byte) (uint64, error) {
return c.UncachedClient.AddVersion(keyID, data)
}

// UpdateVersion either promotes or demotes a specific key version.
func (c *HTTPClient) UpdateVersion(keyID, versionID string, status VersionStatus) error {
return c.UncachedClient.UpdateVersion(keyID, versionID, status)
}

func (c *HTTPClient) getClient() (HTTP, error) {
if c.UncachedClient.Client == nil {
c.UncachedClient.Client = &http.Client{}
}
return c.UncachedClient.Client, nil
}

func (c *HTTPClient) getHTTPData(method string, path string, body url.Values, data interface{}) error {
return c.UncachedClient.getHTTPData(method, path, body, data)
}

// UncachedHTTPClient is a client that uses HTTP to talk to Knox without caching.
type UncachedHTTPClient struct {
// Host is used as the host for http connections
Host string
//AuthHandler returns the authorization string for authenticating to knox. Users should be prefixed by 0u, machines by 0m. On fail, return empty string.
AuthHandler func() string
// Client is the http client for making network calls
Client HTTP
// Version is the current client version, useful for debugging and sent as a header
Version string
}

// NewClient creates a new uncached client to connect to talk to Knox.
func NewUncachedClient(host string, client HTTP, authHandler func() string, version string) UncachedHTTPClient {
return UncachedHTTPClient{
Host: host,
Client: client,
AuthHandler: authHandler,
Version: version,
}
}

// NetworkGetKey gets a knox key by keyID and only uses network without the caches.
func (c *UncachedHTTPClient) NetworkGetKey(keyID string) (*Key, error) {
key := &Key{}
err := c.getHTTPData("GET", "/v0/keys/"+keyID+"/", nil, key)
if err != nil {
return nil, err
}

// do not return the invalid format remote keys
if key.ID == "" || key.ACL == nil || key.VersionList == nil || key.VersionHash == "" {
return nil, fmt.Errorf("invalid key content for the remote key")
}

return key, err
}

// CacheGetKey acts same as NetworkGetKey for UncachedHTTPClient.
func (c *UncachedHTTPClient) CacheGetKey(keyID string) (*Key, error) {
return c.NetworkGetKey(keyID)
}

// GetKey gets a knox key by keyID.
func (c *UncachedHTTPClient) GetKey(keyID string) (*Key, error) {
return c.NetworkGetKey(keyID)
}

// CacheGetKeyWithStatus acts same as NetworkGetKeyWithStatus for UncachedHTTPClient.
func (c *UncachedHTTPClient) CacheGetKeyWithStatus(keyID string, status VersionStatus) (*Key, error) {
return c.NetworkGetKeyWithStatus(keyID, status)
}

// NetworkGetKeyWithStatus gets a knox key by keyID and given version status (always calls network).
func (c *UncachedHTTPClient) NetworkGetKeyWithStatus(keyID string, status VersionStatus) (*Key, error) {
// If clients need to know
s, err := status.MarshalJSON()
if err != nil {
return nil, err
}

key := &Key{}
err = c.getHTTPData("GET", "/v0/keys/"+keyID+"/?status="+string(s), nil, key)
return key, err
}

// GetKeyWithStatus gets a knox key by keyID and status (no cache).
func (c *UncachedHTTPClient) GetKeyWithStatus(keyID string, status VersionStatus) (*Key, error) {
return c.NetworkGetKeyWithStatus(keyID, status)
}

// CreateKey creates a knox key with given keyID data and ACL.
func (c *UncachedHTTPClient) CreateKey(keyID string, data []byte, acl ACL) (uint64, error) {
var i uint64
d := url.Values{}
d.Set("id", keyID)
Expand All @@ -327,7 +417,7 @@ func (c *HTTPClient) CreateKey(keyID string, data []byte, acl ACL) (uint64, erro
}

// GetKeys gets all Knox (if empty map) or gets all keys in map that do not match key version hash.
func (c *HTTPClient) GetKeys(keys map[string]string) ([]string, error) {
func (c *UncachedHTTPClient) GetKeys(keys map[string]string) ([]string, error) {
var l []string

d := url.Values{}
Expand All @@ -340,20 +430,20 @@ func (c *HTTPClient) GetKeys(keys map[string]string) ([]string, error) {
}

// DeleteKey deletes a key from Knox.
func (c HTTPClient) DeleteKey(keyID string) error {
func (c UncachedHTTPClient) DeleteKey(keyID string) error {
err := c.getHTTPData("DELETE", "/v0/keys/"+keyID+"/", nil, nil)
return err
}

// GetACL gets a knox key by keyID.
func (c *HTTPClient) GetACL(keyID string) (*ACL, error) {
func (c *UncachedHTTPClient) GetACL(keyID string) (*ACL, error) {
acl := &ACL{}
err := c.getHTTPData("GET", "/v0/keys/"+keyID+"/access/", nil, acl)
return acl, err
}

// PutAccess will add an ACL rule to a specific key.
func (c *HTTPClient) PutAccess(keyID string, a ...Access) error {
func (c *UncachedHTTPClient) PutAccess(keyID string, a ...Access) error {
d := url.Values{}
s, err := json.Marshal(a)
if err != nil {
Expand All @@ -365,7 +455,7 @@ func (c *HTTPClient) PutAccess(keyID string, a ...Access) error {
}

// AddVersion adds a key version to a specific key.
func (c *HTTPClient) AddVersion(keyID string, data []byte) (uint64, error) {
func (c *UncachedHTTPClient) AddVersion(keyID string, data []byte) (uint64, error) {
var i uint64
d := url.Values{}
d.Set("data", base64.StdEncoding.EncodeToString(data))
Expand All @@ -374,7 +464,7 @@ func (c *HTTPClient) AddVersion(keyID string, data []byte) (uint64, error) {
}

// UpdateVersion either promotes or demotes a specific key version.
func (c *HTTPClient) UpdateVersion(keyID, versionID string, status VersionStatus) error {
func (c *UncachedHTTPClient) UpdateVersion(keyID, versionID string, status VersionStatus) error {
d := url.Values{}
s, err := status.MarshalJSON()
if err != nil {
Expand All @@ -386,14 +476,14 @@ func (c *HTTPClient) UpdateVersion(keyID, versionID string, status VersionStatus
return err
}

func (c *HTTPClient) getClient() (HTTP, error) {
func (c *UncachedHTTPClient) getClient() (HTTP, error) {
if c.Client == nil {
c.Client = &http.Client{}
}
return c.Client, nil
}

func (c *HTTPClient) getHTTPData(method string, path string, body url.Values, data interface{}) error {
func (c *UncachedHTTPClient) getHTTPData(method string, path string, body url.Values, data interface{}) error {
r, err := http.NewRequest(method, "https://"+c.Host+path, bytes.NewBufferString(body.Encode()))

if err != nil {
Expand Down Expand Up @@ -452,12 +542,14 @@ func getHTTPResp(cli HTTP, r *http.Request, resp *Response) error {
// MockClient builds a client that ignores certs and talks to the given host.
func MockClient(host, keyFolder string) *HTTPClient {
return &HTTPClient{
Host: host,
AuthHandler: func() string {
return "TESTAUTH"
},
KeyFolder: keyFolder,
Client: &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}},
Version: "mock",
UncachedClient: UncachedHTTPClient{
Host: host,
AuthHandler: func() string {
return "TESTAUTH"
},
Client: &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}},
Version: "mock",
},
}
}
3 changes: 2 additions & 1 deletion client/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ var clientGetKeyMetrics = func(map[string]string) {}
func Run(
client knox.APIClient,
p *VisibilityParams,
loginCommand *Command) {
loginCommand *Command,
) {

cli = client
if p != nil {
Expand Down
5 changes: 5 additions & 0 deletions client/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package client
import (
"encoding/json"
"fmt"
"github.com/pinterest/knox"
"path"
"strconv"
"time"
Expand Down Expand Up @@ -57,6 +58,10 @@ func parseTimeout(val string) (time.Duration, error) {
}

func runRegister(cmd *Command, args []string) *ErrorStatus {
if _, ok := cli.(*knox.UncachedHTTPClient); ok {
fmt.Println("Cannot Register in No Cache mode")
return nil
}
timeout, err := parseTimeout(*registerTimeout)
if err != nil {
return &ErrorStatus{fmt.Errorf("Invalid value for timeout flag: %s", err.Error()), false}
Expand Down
10 changes: 6 additions & 4 deletions cmd/dev_client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,12 @@ func main() {
}

cli := &knox.HTTPClient{
Host: hostname,
AuthHandler: authHandler,
KeyFolder: keyFolder,
Client: &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}},
KeyFolder: keyFolder,
UncachedClient: knox.UncachedHTTPClient{
Host: hostname,
AuthHandler: authHandler,
Client: &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}},
},
}

loginCommand := client.NewLoginCommand(clientID, tokenEndpoint, "", "", "", "")
Expand Down

0 comments on commit b584dfb

Please sign in to comment.