Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into gomod
Browse files Browse the repository at this point in the history
# Conflicts:
#	.travis.yml
#	Makefile
  • Loading branch information
cliedeman committed Sep 15, 2018
2 parents da51e4d + d0d31d8 commit ae8ec08
Show file tree
Hide file tree
Showing 62 changed files with 3,947 additions and 297 deletions.
9 changes: 7 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ go:
- "1.10"
- tip

install:
- go get -u gopkg.in/alecthomas/gometalinter.v2
- gometalinter.v2 --install

script:
- touch .env
- go mod vendor
- make test ARGS='-coverprofile=coverage.txt -covermode=atomic'
- make lint-warn
- make test ARGS='-v -race -count=2 -coverprofile=coverage.txt -covermode=atomic ./...'
- gometalinter.v2 --enable-all --disable=vetshadow --disable=gocyclo --disable=unparam --disable=nakedret --disable=lll --disable=dupl --disable=gosec --disable=gochecknoinits --disable=gochecknoglobals --disable=test --deadline=120s
- gometalinter.v2 --disable-all --enable=vetshadow --enable=gocyclo --enable=unparam --enable=nakedret --enable=lll --enable=dupl --enable=gosec --enable=gochecknoinits --enable=gochecknoglobals --deadline=120s || true

after_success:
- bash <(curl -s https://codecov.io/bash)
52 changes: 52 additions & 0 deletions API_SUPPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@
- [X] `GET`
- [X] `PUT`
- [X] `DELETE`
- `/nodebalancers/$id/configs/$id/rebuild`
- [X] `POST`

## Networking

Expand Down Expand Up @@ -289,6 +291,56 @@
- `/account/users/$username/password`
- [ ] `POST`

## Profile

### Personalized User Settings

- `/profile`
- [ ] `GET`
- [ ] `PUT`

### Granted OAuth Apps

- `/profile/apps`
- [ ] `GET`
- `/profile/apps/$id`
- [ ] `GET`
- [ ] `DELETE`

### Grants to Linode Resources

- `/profile/grants`
- [ ] `GET`

### SSH Keys

- `/profile/sshkeys`
- [x] `GET`
- [x] `POST`
- `/profile/sshkeys/$id`
- [x] `GET`
- [x] `PUT`
- [x] `DELETE`

### Two-Factor

- `/profile/tfa-disable`
- [ ] `POST`
- `/profile/tfa-enable`
- [ ] `POST`
- `/profile/tfa-enable-confirm`
- [ ] `POST`

### Personal Access API Tokens

- `/profile/tokens`
- [ ] `GET`
- [ ] `POST`
- `/profile/tokens/$id`
- [ ] `GET`
- [ ] `PUT`
- [ ] `DELETE`

## Images

- `/images`
Expand Down
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
# Change Log

<a name="v0.5.1"></a>
## [v0.5.1](https://github.com/linode/linodego/compare/v0.5.0...v0.5.1) (2018-09-10)

### Fixes

* Domain.Status was not imported from API responses correctly

<a name="v0.5.0"></a>
## [v0.5.0](https://github.com/linode/linodego/compare/v0.4.0...v0.5.0) (2018-09-09)

### Breaking Changes

* List functions return slice of thing instead of slice of pointer to thing

### Feature

* add SSHKeys methods to client (also affects InstanceCreate, InstanceDiskCreate)
* add RebuildNodeBalancerConfig (and CreateNodeBalancerConfig with Nodes)

### Fixes

* Event.TimeRemaining wouldn't parse all possible API value
* Tests no longer rely on known/special instance and volume ids

<a name="0.4.0"></a>
## [0.4.0](https://github.com/linode/linodego/compare/v0.3.0...0.4.0) (2018-08-27)

Expand Down
14 changes: 3 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,13 @@ include .env
BIN_DIR := $(GOPATH)/bin
GOMETALINTER := $(BIN_DIR)/gometalinter.v2
GOMETALINTER_ARGS := --enable-all --disable=vetshadow --disable=gocyclo --disable=unparam --disable=nakedret --disable=lll --disable=dupl --disable=gosec --disable=gochecknoinits --disable=gochecknoglobals --disable=test
GOMETALINTER_WARN_ARGS := --disable-all --enable=vetshadow --enable=gocyclo --enable=unparam --enable=nakedret --enable=lll --enable=dupl --enable=gosec --enable=gochecknoinits --enable=gochecknoglobals --deadline=120s
GOMETALINTER_WARN_ARGS := --disable-all --enable=vetshadow --enable=gocyclo --enable=unparam --enable=nakedret --enable=lll --enable=dupl --enable=gosec --enable=gochecknoinits --enable=gochecknoglobals --deadline=120s

.PHONY: build example refresh-fixtures clean-fixtures

LINODE_FIXTURE_INSTANCE:=76859403
LINODE_FIXTURE_VOLUME:=6574839201

.PHONY: test
test: build lint
@LINODE_TEST_INSTANCE=$(LINODE_FIXTURE_INSTANCE) \
LINODE_TEST_VOLUME=$(LINODE_FIXTURE_VOLUME) \
LINODE_FIXTURE_MODE="play" \
@LINODE_FIXTURE_MODE="play" \
LINODE_TOKEN="awesometokenawesometokenawesometoken" \
GO111MODULE="on" \
go test $(ARGS)
Expand Down Expand Up @@ -42,19 +37,16 @@ refresh-fixtures: clean-fixtures fixtures
fixtures:
@echo "* Running fixtures"
@LINODE_TOKEN=$(LINODE_TOKEN) \
LINODE_TEST_INSTANCE=$(LINODE_TEST_INSTANCE) \
LINODE_TEST_VOLUME=$(LINODE_TEST_VOLUME) \
LINODE_FIXTURE_MODE="record" go test $(ARGS)
@echo "* Santizing fixtures"
@for yaml in fixtures/*yaml; do \
sed -E -i "" -e "s/$(LINODE_TOKEN)/awesometokenawesometokenawesometoken/g" \
-e "s/$(LINODE_TEST_INSTANCE)/$(LINODE_FIXTURE_INSTANCE)/g" \
-e 's/20[0-9]{2}-[01][0-9]-[0-3][0-9]T[0-2][0-9]:[0-9]{2}:[0-9]{2}/2018-01-02T03:04:05/g' \
-e 's/nb-[0-9]{1,3}-[0-9]{1,3}-[0-9]{1,3}-[0-9]{1,3}\./nb-10-20-30-40./g' \
-e 's/192\.168\.((1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\.)(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])/192.168.030.040/g' \
-e '/^192\.168/!s/((1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])/010.020.030.040/g' \
-e 's/(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/1234::5678/g' \
-e "s/$(LINODE_TEST_VOLUME)/$(LINODE_FIXTURE_VOLUME)/g" $$yaml; \
$$yaml; \
done

.PHONY: godoc
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,8 @@ When performing a `POST` or `PUT` request, multiple field related errors will be
Run `make test` to run the unit tests. This is the same as running `go test` except that `make test` will
execute the tests while playing back API response fixtures that were recorded during a previous development build.

`go test` can be used without the fixtures, so long as `LINODE_TEST_INSTANCE` and `LINODE_TEST_VOLUME` are set
to an instance ID and volume ID that exists on your account. The Linode instance must have a backup and a snapshot to
match the test expectations. Copy `env.sample` to `.env` and configure your persistent test settings, including an API token.
`go test` can be used without the fixtures. Copy `env.sample` to `.env` and configure your persistent test
settings, including an API token.

`go test -short` can be used to run live API tests that do not require an account token.

Expand Down
67 changes: 60 additions & 7 deletions account_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ package linodego

import (
"context"
"encoding/json"
"fmt"
"log"
"strconv"
"strings"
"time"
)

Expand All @@ -23,7 +27,7 @@ type Event struct {
PercentComplete int `json:"percent_complete"`

// The rate of completion of the Event. Only some Events will return rate; for example, migration and resize Events.
Rate string `json:"rate"`
Rate *string `json:"rate"`

// If this Event has been read.
Read bool `json:"read"`
Expand All @@ -32,7 +36,8 @@ type Event struct {
Seen bool `json:"seen"`

// The estimated time remaining until the completion of this Event. This value is only returned for in-progress events.
TimeRemaining int `json:"time_remaining"`
TimeRemainingMsg json.RawMessage `json:"time_remaining"`
TimeRemaining *int `json:"-"`

// The username of the User who caused the Event.
Username string `json:"username"`
Expand Down Expand Up @@ -141,7 +146,7 @@ type EventEntity struct {
// EventsPagedResponse represents a paginated Events API response
type EventsPagedResponse struct {
*PageOptions
Data []*Event `json:"data"`
Data []Event `json:"data"`
}

// endpoint gets the endpoint URL for Event
Expand All @@ -165,17 +170,17 @@ func (e Event) endpointWithID(c *Client) string {

// appendData appends Events when processing paginated Event responses
func (resp *EventsPagedResponse) appendData(r *EventsPagedResponse) {
(*resp).Data = append(resp.Data, r.Data...)
resp.Data = append(resp.Data, r.Data...)
}

// ListEvents gets a collection of Event objects representing actions taken
// on the Account. The Events returned depend on the token grants and the grants
// of the associated user.
func (c *Client) ListEvents(ctx context.Context, opts *ListOptions) ([]*Event, error) {
func (c *Client) ListEvents(ctx context.Context, opts *ListOptions) ([]Event, error) {
response := EventsPagedResponse{}
err := c.listHelper(ctx, &response, opts)
for _, el := range response.Data {
el.fixDates()
for i := range response.Data {
response.Data[i].fixDates()
}
if err != nil {
return nil, err
Expand All @@ -200,6 +205,7 @@ func (c *Client) GetEvent(ctx context.Context, id int) (*Event, error) {
// fixDates converts JSON timestamps to Go time.Time values
func (e *Event) fixDates() *Event {
e.Created, _ = parseDates(e.CreatedStr)
e.TimeRemaining = unmarshalTimeRemaining(e.TimeRemainingMsg)
return e
}

Expand All @@ -222,3 +228,50 @@ func (c *Client) MarkEventsSeen(ctx context.Context, event *Event) error {

return err
}

func unmarshalTimeRemaining(m json.RawMessage) *int {
jsonBytes, err := m.MarshalJSON()
if err != nil {
panic(jsonBytes)
}

if len(jsonBytes) == 4 && string(jsonBytes) == "null" {
return nil
}

var timeStr string
if err := json.Unmarshal(jsonBytes, &timeStr); err == nil && len(timeStr) > 0 {
if dur, err := durationToSeconds(timeStr); err != nil {
panic(err)
} else {
return &dur
}
} else {
var intPtr int
if err := json.Unmarshal(jsonBytes, &intPtr); err == nil {
return &intPtr
}
}

log.Println("[WARN] Unexpected unmarshalTimeRemaining value: ", jsonBytes)
return nil
}

// durationToSeconds takes a hh:mm:ss string and returns the number of seconds
func durationToSeconds(s string) (int, error) {
multipliers := [3]int{60 * 60, 60, 1}
segs := strings.Split(s, ":")
if len(segs) > len(multipliers) {
return 0, fmt.Errorf("too many ':' separators in time duration: %s", s)
}
var d int
l := len(segs)
for i := 0; i < l; i++ {
m, err := strconv.Atoi(segs[i])
if err != nil {
return 0, err
}
d += m * multipliers[i+len(multipliers)-l]
}
return d, nil
}
34 changes: 34 additions & 0 deletions account_events_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package linodego_test

import (
"context"
"testing"
)

func TestListEvents_resizing(t *testing.T) {
if testing.Short() {
t.Skip("Skipping test in short mode.")
}

client, teardown := createTestClient(t, "fixtures/TestListEvents_resizing")
defer teardown()

events, err := client.ListEvents(context.Background(), nil)
if err != nil {
t.Errorf("Error getting Events, expected struct, got error %v", err)
}

// TODO(displague) this test is dependent on specific fixture data, mock it here, or just test
// fixDates directly
if events[2].TimeRemaining != nil {
t.Errorf("Error listing Events, expected resize event time_remaining to be nil, got %v", events[2].TimeRemaining)
}

if events[1].TimeRemaining == nil || *events[1].TimeRemaining != 0 {
t.Errorf("Error listing Events, expected resize event time_remaining to be 0 seconds, got %v", events[1].TimeRemaining)
}

if events[0].TimeRemaining == nil || *events[0].TimeRemaining != 60+23 {
t.Errorf("Error listing Events, expected resize event time_remaining to be 83 seconds, got %v", events[0].TimeRemaining)
}
}
20 changes: 10 additions & 10 deletions account_invoices.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type InvoiceItem struct {
// InvoicesPagedResponse represents a paginated Invoice API response
type InvoicesPagedResponse struct {
*PageOptions
Data []*Invoice `json:"data"`
Data []Invoice `json:"data"`
}

// endpoint gets the endpoint URL for Invoice
Expand All @@ -47,15 +47,15 @@ func (InvoicesPagedResponse) endpoint(c *Client) string {

// appendData appends Invoices when processing paginated Invoice responses
func (resp *InvoicesPagedResponse) appendData(r *InvoicesPagedResponse) {
(*resp).Data = append(resp.Data, r.Data...)
resp.Data = append(resp.Data, r.Data...)
}

// ListInvoices gets a paginated list of Invoices against the Account
func (c *Client) ListInvoices(ctx context.Context, opts *ListOptions) ([]*Invoice, error) {
func (c *Client) ListInvoices(ctx context.Context, opts *ListOptions) ([]Invoice, error) {
response := InvoicesPagedResponse{}
err := c.listHelper(ctx, &response, opts)
for _, el := range response.Data {
el.fixDates()
for i := range response.Data {
response.Data[i].fixDates()
}
if err != nil {
return nil, err
Expand Down Expand Up @@ -94,7 +94,7 @@ func (c *Client) GetInvoice(ctx context.Context, id int) (*Invoice, error) {
// InvoiceItemsPagedResponse represents a paginated Invoice Item API response
type InvoiceItemsPagedResponse struct {
*PageOptions
Data []*InvoiceItem `json:"data"`
Data []InvoiceItem `json:"data"`
}

// endpointWithID gets the endpoint URL for InvoiceItems associated with a specific Invoice
Expand All @@ -108,15 +108,15 @@ func (InvoiceItemsPagedResponse) endpointWithID(c *Client, id int) string {

// appendData appends InvoiceItems when processing paginated Invoice Item responses
func (resp *InvoiceItemsPagedResponse) appendData(r *InvoiceItemsPagedResponse) {
(*resp).Data = append(resp.Data, r.Data...)
resp.Data = append(resp.Data, r.Data...)
}

// ListInvoiceItems gets the invoice items associated with a specific Invoice
func (c *Client) ListInvoiceItems(ctx context.Context, id int, opts *ListOptions) ([]*InvoiceItem, error) {
func (c *Client) ListInvoiceItems(ctx context.Context, id int, opts *ListOptions) ([]InvoiceItem, error) {
response := InvoiceItemsPagedResponse{}
err := c.listHelperWithID(ctx, &response, id, opts)
for _, el := range response.Data {
el.fixDates()
for i := range response.Data {
response.Data[i].fixDates()
}
if err != nil {
return nil, err
Expand Down
Loading

0 comments on commit ae8ec08

Please sign in to comment.