Skip to content

Commit

Permalink
update code to match latest API spec (#71)
Browse files Browse the repository at this point in the history
* update code to match latest API spec

* Add privileged mode capabilities

* Update transport tests

* fix compilation issues - pending test update

* update endpoint tests

* update remaining tests

* Update client code

* fix error switch case type

* fix comment and interval validation

* update var

* update changelog
  • Loading branch information
joe94 authored Nov 3, 2020
1 parent 8ddf224 commit f49bf37
Show file tree
Hide file tree
Showing 21 changed files with 967 additions and 793 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

## [v0.3.8]
- Update code to abide by latest API spec in the main repo readme. [#71](https://github.com/xmidt-org/argus/pull/71)

## [v0.3.7]
### Changed
- Changes the PUT creation route to a POST. [#68](https://github.com/xmidt-org/argus/pull/68)
Expand Down Expand Up @@ -82,7 +85,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [v0.1.0] Tue May 07 2020 Jack Murdock - 0.1.0
- initial creation

[Unreleased]: https://github.com/xmidt-org/argus/compare/v0.3.7...HEAD
[Unreleased]: https://github.com/xmidt-org/argus/compare/v0.3.8...HEAD
[v0.3.8]: https://github.com/xmidt-org/argus/compare/v0.3.7...v0.3.8
[v0.3.7]: https://github.com/xmidt-org/argus/compare/v0.3.6...v0.3.7
[v0.3.6]: https://github.com/xmidt-org/argus/compare/v0.3.5...v0.3.6
[v0.3.5]: https://github.com/xmidt-org/argus/compare/v0.3.4...v0.3.5
Expand Down
20 changes: 14 additions & 6 deletions argus.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ servers:
dynamo:
# endpoint is used to set a custom aws endpoint.
# (Optional)
endpoint: "http://localhost:8042"
endpoint: "http://localhost:8000"

# table is the name of the table that is already configured with bucket and id as the key.
table: "gifnoc"
Expand All @@ -104,11 +104,6 @@ dynamo:

# itemTTL configures the default time based ttls for each item.
itemTTL:
# defaultTTL is used if not ttl is provided via the api.
# refer to https://golang.org/pkg/time/#ParseDuration for valid strings.
# (Optional) default: 5m
defaultTTL: "5m"

# maxTTL is limit the maxTTL provided via the api.
# refer to https://golang.org/pkg/time/#ParseDuration for valid strings.
# (Optional) default: 1y
Expand All @@ -122,13 +117,26 @@ itemTTL:
# WARNING! Be sure to remove this from your production config
authHeader: ["dXNlcjpwYXNz"]

# request is a config section related to operation authorization
# and request validation.
request:
authorization:
# adminToken serves as a master key which allows performing operations on any
# item regardless of their ownership status.
adminToken: "Hzu1WpIe7S8G"

validation:
# maxTTL specifies the cap for the TTL of items when values are specified.
maxTTL: "24h"

# jwtValidator provides Bearer auth configuration
jwtValidator:
keys:
factory:
uri: "http://sample-jwt-validator-uri/{keyId}"
purpose: 0
updateInterval: 604800000000000

# capabilityCheck provides the details needed for checking an incoming JWT's
# capabilities. If the type of check isn't provided, no checking is done. The
# type can be "monitor" or "enforce". If it is empty or a different value, no
Expand Down
178 changes: 90 additions & 88 deletions chrysom/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,30 @@ import (
"github.com/go-kit/kit/log/level"
"github.com/go-kit/kit/metrics/provider"
"github.com/xmidt-org/argus/model"
"github.com/xmidt-org/argus/store"
"github.com/xmidt-org/bascule/acquire"
)

// PushResult is a simple type to indicate the result type for the
// PushItem operation.
type PushResult string

// Types of pushItem successful results.
const (
CreatedPushResult PushResult = "created"
UpdatedPushResult PushResult = "ok"
)

type ClientConfig struct {
HTTPClient *http.Client
Bucket string
PullInterval time.Duration
Address string
Auth Auth
DefaultTTL int64
MetricsProvider provider.Provider
Logger log.Logger
Listener Listener
AdminToken string
}

type Auth struct {
Expand All @@ -58,15 +69,15 @@ type loggerGroup struct {
}

type Client struct {
client *http.Client
ticker *time.Ticker
auth acquire.Acquirer
metrics *measures
listener Listener
bucketName string
remoteStoreAddress string
defaultStoreItemTTL int64
loggers loggerGroup
client *http.Client
ticker *time.Ticker
auth acquire.Acquirer
metrics *measures
listener Listener
bucketName string
remoteStoreAddress string
loggers loggerGroup
adminToken string
}

func initLoggers(logger log.Logger) loggerGroup {
Expand All @@ -93,15 +104,15 @@ func CreateClient(config ClientConfig) (*Client, error) {
return nil, err
}
clientStore := &Client{
client: config.HTTPClient,
ticker: time.NewTicker(config.PullInterval),
auth: auth,
metrics: initMetrics(config.MetricsProvider),
loggers: initLoggers(config.Logger),
listener: config.Listener,
remoteStoreAddress: config.Address,
defaultStoreItemTTL: config.DefaultTTL,
bucketName: config.Bucket,
client: config.HTTPClient,
ticker: time.NewTicker(config.PullInterval),
auth: auth,
metrics: initMetrics(config.MetricsProvider),
loggers: initLoggers(config.Logger),
listener: config.Listener,
remoteStoreAddress: config.Address,
bucketName: config.Bucket,
adminToken: config.AdminToken,
}

if config.PullInterval > 0 {
Expand All @@ -121,18 +132,20 @@ func validateConfig(config *ClientConfig) error {
if config.Bucket == "" {
config.Bucket = "testing"
}
if config.DefaultTTL < 1 {
config.DefaultTTL = 300
}
if config.MetricsProvider == nil {
return errors.New("a metrics provider is required")
}

if config.PullInterval == 0 {
config.PullInterval = time.Second * 5
}

if config.Logger == nil {
config.Logger = log.NewNopLogger()
}
return nil
}

func determineTokenAcquirer(config ClientConfig) (acquire.Acquirer, error) {
defaultAcquirer := &acquire.DefaultAcquirer{}
if config.Auth.JWT.AuthURL != "" && config.Auth.JWT.Buffer != 0 && config.Auth.JWT.Timeout != 0 {
Expand All @@ -146,38 +159,46 @@ func determineTokenAcquirer(config ClientConfig) (acquire.Acquirer, error) {
return defaultAcquirer, nil
}

func (c *Client) GetItems(owner string) ([]model.Item, error) {
func (c *Client) GetItems(owner string, adminMode bool) ([]model.Item, error) {
request, err := http.NewRequest("GET", fmt.Sprintf("%s/api/v1/store/%s", c.remoteStoreAddress, c.bucketName), nil)
if err != nil {
return []model.Item{}, err
return nil, err
}
err = acquire.AddAuth(request, c.auth)
if err != nil {
return []model.Item{}, err
return nil, err
}
if owner != "" {
request.Header.Add("X-Midt-Owner", owner)
request.Header.Set(store.ItemOwnerHeaderKey, owner)
}

if adminMode {
if c.adminToken == "" {
return nil, errors.New("adminToken needed to run as admin")
}
request.Header.Set(store.AdminTokenHeaderKey, c.adminToken)
}

response, err := c.client.Do(request)
if err != nil {
return []model.Item{}, err
return nil, err
}
if response.StatusCode == 404 {
return []model.Item{}, nil
}
if response.StatusCode != 200 {
c.loggers.Error.Log("msg", "DB responded with non-200 response for request to get items", "code", response.StatusCode)
return []model.Item{}, errors.New("failed to get items, non 200 statuscode")
return nil, errors.New("failed to get items, non 200 statuscode")
}
data, err := ioutil.ReadAll(response.Body)
if err != nil {
return []model.Item{}, err
return nil, err
}

body := map[string]model.Item{}
err = json.Unmarshal(data, &body)
if err != nil {
return []model.Item{}, err
return nil, err
}

responseData := make([]model.Item, len(body))
Expand All @@ -189,97 +210,78 @@ func (c *Client) GetItems(owner string) ([]model.Item, error) {
return responseData, nil
}

func (c *Client) Push(item model.Item, owner string) (string, error) {
func (c *Client) Push(item model.Item, owner string, adminMode bool) (PushResult, error) {
if item.Identifier == "" {
return "", errors.New("identifier can't be empty")
}
if item.TTL < 1 {
item.TTL = c.defaultStoreItemTTL
}
data, err := json.Marshal(&item)
if err != nil {
return "", err
}
request, err := http.NewRequest("POST", fmt.Sprintf("%s/api/v1/store/%s", c.remoteStoreAddress, c.bucketName), bytes.NewReader(data))
if err != nil {
return "", err
}
err = acquire.AddAuth(request, c.auth)
if err != nil {
return "", err
}
if owner != "" {
request.Header.Add("X-Midt-Owner", owner)
}
response, err := c.client.Do(request)
if err != nil {
return "", err
}
if response.StatusCode != 200 {
c.loggers.Error.Log("msg", "DB responded with non-200 response for request to add/update an item", "code", response.StatusCode)
return "", errors.New("Failed to put item as DB responded with non-200 statuscode")
}
responsePayload, _ := ioutil.ReadAll(response.Body)
key := model.Key{}
err = json.Unmarshal(responsePayload, &key)
if err != nil {
return "", err
}
return key.ID, nil
}

func (c *Client) Update(item model.Item, id string, owner string) (string, error) {
if item.Identifier == "" {
return "", errors.New("identifier can't be empty")
if item.UUID == "" {
return "", errors.New("uuid can't be empty")
}
if item.TTL < 1 {
item.TTL = c.defaultStoreItemTTL

if item.TTL != nil && *item.TTL < 1 {
return "", errors.New("when provided, TTL must be > 0")
}

data, err := json.Marshal(&item)
if err != nil {
return "", err
}
request, err := http.NewRequest("PUT", fmt.Sprintf("%s/api/v1/store/%s/%s", c.remoteStoreAddress, c.bucketName, id), bytes.NewReader(data))
request, err := http.NewRequest("PUT", fmt.Sprintf("%s/api/v1/store/%s/%s", c.remoteStoreAddress, c.bucketName, item.UUID), bytes.NewReader(data))
if err != nil {
return "", err
}
err = acquire.AddAuth(request, c.auth)
if err != nil {
return "", err
}
if owner != "" {
request.Header.Add("X-Midt-Owner", owner)
request.Header.Add(store.ItemOwnerHeaderKey, owner)

if adminMode {
if c.adminToken == "" {
return "", errors.New("adminToken needed to run as admin")
}
request.Header.Set(store.AdminTokenHeaderKey, c.adminToken)
}

response, err := c.client.Do(request)
if err != nil {
return "", err
}
if response.StatusCode != 200 {
c.loggers.Error.Log("msg", "DB responded with non-200 response for request to update an item", "code", response.StatusCode)
return "", errors.New("Failed to put item as DB responded with non-200 statuscode")
}
responsePayload, _ := ioutil.ReadAll(response.Body)
key := model.Key{}
err = json.Unmarshal(responsePayload, &key)
if err != nil {
return "", err

switch response.StatusCode {
case http.StatusCreated:
return CreatedPushResult, nil
case http.StatusOK:
return UpdatedPushResult, nil
}
return key.ID, nil

c.loggers.Error.Log("msg", "DB responded with non-successful response for request to update an item", "code", response.StatusCode)
return "", errors.New("Failed to set item as DB responded with non-success statuscode")
}

func (c *Client) Remove(id string, owner string) (model.Item, error) {
request, err := http.NewRequest("DELETE", fmt.Sprintf("%s/api/v1/store/%s/%s", c.remoteStoreAddress, c.bucketName, id), nil)
func (c *Client) Remove(uuid string, owner string, adminMode bool) (model.Item, error) {
if uuid == "" {
return model.Item{}, errors.New("uuid can't be empty")
}
request, err := http.NewRequest("DELETE", fmt.Sprintf("%s/api/v1/store/%s/%s", c.remoteStoreAddress, c.bucketName, uuid), nil)
if err != nil {
return model.Item{}, err
}
err = acquire.AddAuth(request, c.auth)
if err != nil {
return model.Item{}, err
}
if owner != "" {
request.Header.Add("X-Midt-Owner", owner)

request.Header.Add(store.ItemOwnerHeaderKey, owner)

if adminMode {
if c.adminToken == "" {
return model.Item{}, errors.New("adminToken needed to run as admin")
}
request.Header.Set(store.AdminTokenHeaderKey, c.adminToken)
}

response, err := c.client.Do(request)
if err != nil {
return model.Item{}, err
Expand Down Expand Up @@ -309,7 +311,7 @@ func (c *Client) Start(ctx context.Context) error {
go func() {
for range c.ticker.C {
outcome := SuccessOutcome
items, err := c.GetItems("")
items, err := c.GetItems("", true)
if err == nil {
c.listener.Update(items)
} else {
Expand Down
6 changes: 3 additions & 3 deletions chrysom/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ type PushReader interface {
type Pusher interface {
// Push applies user configurable for registering an item returning the id
// i.e. updated the storage with said item.
Push(item model.Item, owner string) (string, error)
Push(item model.Item, owner string, adminMode bool) (PushResult, error)

// Remove will remove the item from the store
Remove(id string, owner string) (model.Item, error)
Remove(uuid string, owner string, adminMode bool) (model.Item, error)
}

type Listener interface {
Expand All @@ -54,7 +54,7 @@ func (listener ListenerFunc) Update(items []model.Item) {

type Reader interface {
// GeItems will return all the current items or an error.
GetItems(owner string) ([]model.Item, error)
GetItems(owner string, adminMode bool) ([]model.Item, error)

Start(ctx context.Context) error

Expand Down
Loading

0 comments on commit f49bf37

Please sign in to comment.