Skip to content

Commit

Permalink
Merge pull request #1154 from convox/20160826-2
Browse files Browse the repository at this point in the history
[RELEASE] 20160826-2
  • Loading branch information
ddollar authored Aug 27, 2016
2 parents 7c1cb44 + a0238e6 commit 1fa95f5
Show file tree
Hide file tree
Showing 87 changed files with 2,332 additions and 1,333 deletions.
10 changes: 7 additions & 3 deletions Godeps/Godeps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 11 additions & 10 deletions api/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import (
"os"
"sync"
"time"

"github.com/convox/logger"
)
import "github.com/convox/logger"

type Cache map[string]map[string]*CacheItem

Expand All @@ -20,7 +21,7 @@ type CacheItem struct {
var (
cache = Cache{}
lock = sync.Mutex{}
log = logger.New("ns=rack.api.cache")
log = logger.New("ns=api.cache")
)

func Get(collection string, key interface{}) interface{} {
Expand All @@ -34,28 +35,28 @@ func Get(collection string, key interface{}) interface{} {
hash, err := hashKey(key)

if err != nil {
log.Log("fn=get collection=%q key=%q status=error error=%q", collection, hash, err)
log.Logf("fn=get collection=%q key=%q status=error error=%q", collection, hash, err)
return nil
}

if cache[collection] == nil {
log.Log("fn=get collection=%q key=%q status=miss", collection, hash)
log.Logf("fn=get collection=%q key=%q status=miss", collection, hash)
return nil
}

item := cache[collection][hash]

if item == nil {
log.Log("fn=get collection=%q key=%q status=miss", collection, hash)
log.Logf("fn=get collection=%q key=%q status=miss", collection, hash)
return nil
}

if item.Expires.Before(time.Now()) {
log.Log("fn=get collection=%q key=%q status=expired", collection, hash)
log.Logf("fn=get collection=%q key=%q status=expired", collection, hash)
return nil
}

log.Log("fn=get collection=%q key=%q status=hit", collection, hash)
log.Logf("fn=get collection=%q key=%q status=hit", collection, hash)
return item.Item
}

Expand All @@ -70,7 +71,7 @@ func Set(collection string, key, value interface{}, ttl time.Duration) error {
hash, err := hashKey(key)

if err != nil {
log.Log("fn=set collection=%q key=%q status=error error=%q", collection, hash, err)
log.Logf("fn=set collection=%q key=%q status=error error=%q", collection, hash, err)
return err
}

Expand All @@ -79,7 +80,7 @@ func Set(collection string, key, value interface{}, ttl time.Duration) error {
Expires: time.Now().Add(ttl),
}

log.Log("fn=set collection=%q key=%q status=success", collection, hash)
log.Logf("fn=set collection=%q key=%q status=success", collection, hash)
return nil
}

Expand All @@ -90,7 +91,7 @@ func Clear(collection string, key interface{}) error {
hash, err := hashKey(key)

if err != nil {
log.Log("fn=clearcollection=%q key=%q status=error error=%q", collection, hash, err)
log.Logf("fn=clearcollection=%q key=%q status=error error=%q", collection, hash, err)
return err
}

Expand Down
56 changes: 9 additions & 47 deletions api/controllers/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ package controllers
import (
"encoding/base64"
"fmt"
"math/rand"
"net/http"
"os"
"strings"
"time"

log "github.com/Sirupsen/logrus"
"github.com/convox/logger"
"github.com/convox/rack/api/httperr"
"golang.org/x/net/websocket"
)
Expand All @@ -21,16 +20,18 @@ type ApiWebsocketFunc func(*websocket.Conn) *httperr.Error

func api(at string, handler ApiHandlerFunc) http.HandlerFunc {
return func(rw http.ResponseWriter, r *http.Request) {
start := time.Now()
log := logger.New("ns=api.controllers").At(at).Start()

if !passwordCheck(r) {
log.Errorf("invalid authorization")
rw.Header().Set("WWW-Authenticate", `Basic realm="Convox System"`)
rw.WriteHeader(401)
rw.Write([]byte("invalid authorization"))
return
}

if !versionCheck(r) {
log.Errorf("invalid version")
rw.WriteHeader(403)
rw.Write([]byte("client outdated, please update with `convox update`"))
return
Expand All @@ -39,47 +40,13 @@ func api(at string, handler ApiHandlerFunc) http.HandlerFunc {
err := handler(rw, r)

if err != nil {
log.Error(err)
rw.WriteHeader(err.Code())
RenderError(rw, err)
logError(at, err)
return
}

log.WithFields(log.Fields{
"ns": "kernel",
"at": at,
"state": "success",
"measure#handler.elapsed": fmt.Sprintf("%0.3fms", float64(time.Since(start).Nanoseconds())/1000000),
}).Info()
}
}

func logError(at string, err *httperr.Error) {
l := log.WithFields(log.Fields{
"ns": "kernel",
"at": at,
"state": "error",
})

if err.User() {
l.WithField("count#error.user", 1).Warn(err.Error())
return
}

err.Save()

id := rand.Int31()

l.WithFields(log.Fields{
"id": id,
"count#error": 1,
}).Warn(err.Error())

for i, t := range err.Trace() {
l.WithFields(log.Fields{
"id": id,
"line": i,
}).Warn(t)
log.Success()
}
}

Expand Down Expand Up @@ -138,7 +105,7 @@ func versionCheck(r *http.Request) bool {

func ws(at string, handler ApiWebsocketFunc) websocket.Handler {
return websocket.Handler(func(ws *websocket.Conn) {
start := time.Now()
log := logger.New("ns=api.controllers").At(at).Start()

if !passwordCheck(ws.Request()) {
ws.Write([]byte("ERROR: invalid authorization\n"))
Expand All @@ -153,16 +120,11 @@ func ws(at string, handler ApiWebsocketFunc) websocket.Handler {
err := handler(ws)

if err != nil {
log.Error(err)
ws.Write([]byte(fmt.Sprintf("ERROR: %v\n", err)))
logError(at, err)
return
}

log.WithFields(log.Fields{
"ns": "kernel",
"at": at,
"state": "success",
"measure#websocket.handler.elapsed": fmt.Sprintf("%0.3fms", float64(time.Since(start).Nanoseconds())/1000000),
}).Info()
log.Success()
})
}
12 changes: 12 additions & 0 deletions api/controllers/controllers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package controllers_test

import (
"bytes"

"github.com/convox/logger"
)

func init() {
var buf bytes.Buffer
logger.Output = &buf
}
60 changes: 18 additions & 42 deletions api/controllers/formation.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ package controllers
import (
"net/http"
"strconv"
"strings"

"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/convox/rack/api/httperr"
"github.com/convox/rack/api/models"
"github.com/gorilla/mux"
Expand All @@ -14,14 +12,7 @@ import (
func FormationList(rw http.ResponseWriter, r *http.Request) *httperr.Error {
app := mux.Vars(r)["app"]

_, err := models.GetApp(app)

if awsError(err) == "ValidationError" {
return httperr.Errorf(404, "no such app: %s", app)
}

formation, err := models.ListFormation(app)

formation, err := models.Provider().FormationList(app)
if err != nil {
return httperr.Server(err)
}
Expand All @@ -34,31 +25,26 @@ func FormationSet(rw http.ResponseWriter, r *http.Request) *httperr.Error {
app := vars["app"]
process := vars["process"]

_, err := models.GetApp(app)
if awsError(err) == "ValidationError" {
return httperr.Errorf(404, "no such app: %s", app)
pf, err := models.Provider().FormationGet(app, process)
if err != nil {
return httperr.Server(err)
}

opts := models.FormationOptions{}

// update based on form input
if cc := GetForm(r, "count"); cc != "" {
c, err := strconv.Atoi(cc)
if err != nil {
return httperr.Errorf(403, "count must be numeric")
}

opts.Count = cc

switch {
// critical fix: old clients default to count=-1 for "no change"
// assert a minimum client version before setting count=-1 which now deletes a service / ELB
if c == -1 && r.Header.Get("Version") < "20160602213113" {
opts.Count = ""
}

case r.Header.Get("Version") < "20160602213113" && c == -1:
// backwards compatibility: other old clients use count=-2 for "no change"
if c == -2 {
opts.Count = ""
case c == -2:
default:
pf.Count = c
}
}

Expand All @@ -68,11 +54,11 @@ func FormationSet(rw http.ResponseWriter, r *http.Request) *httperr.Error {
return httperr.Errorf(403, "cpu must be numeric")
}

opts.CPU = cc

switch {
// backwards compatibility: other old clients use cpu=-1 for "no change"
if c == -1 {
opts.CPU = ""
case c == -1:
default:
pf.CPU = c
}
}

Expand All @@ -82,25 +68,15 @@ func FormationSet(rw http.ResponseWriter, r *http.Request) *httperr.Error {
return httperr.Errorf(403, "memory must be numeric")
}

opts.Memory = mm

switch {
// backwards compatibility: other old clients use memory=-1 or memory=0 for "no change"
if m == -1 || m == 0 {
opts.Memory = ""
case m == -1 || m == 0:
default:
pf.Memory = m
}
}

err = models.SetFormation(app, process, opts)
if ae, ok := err.(awserr.Error); ok {
if ae.Code() == "ValidationError" {
switch {
case strings.Contains(ae.Error(), "No updates are to be performed"):
return httperr.Errorf(403, "no updates are to be performed: %s", app)
case strings.Contains(ae.Error(), "can not be updated"):
return httperr.Errorf(403, "app is already updating: %s", app)
}
}
}
err = models.Provider().FormationSave(app, pf)
if err != nil {
return httperr.Server(err)
}
Expand Down
Loading

0 comments on commit 1fa95f5

Please sign in to comment.