Skip to content

Commit

Permalink
Merge pull request #46 from SpectoLabs/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
Karolis Rusenas committed Jan 6, 2016
2 parents e16e355 + 44db997 commit ef92f86
Show file tree
Hide file tree
Showing 10 changed files with 84 additions and 44 deletions.
6 changes: 3 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[submodule "redigo"]
path = vendor/github.com/garyburd/redigo
url = https://github.com/garyburd/redigo
[submodule "boltdb"]
path = vendor/github.com/boltdb/bolt
url = https://github.com/boltdb/bolt
[submodule "logrus"]
path = vendor/github.com/Sirupsen/logrus
url = https://github.com/Sirupsen/logrus
Expand Down
37 changes: 36 additions & 1 deletion admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ import (
"github.com/meatballhat/negroni-logrus"
)

// jsonResponse struct encapsulates payload data
// recordedRequests struct encapsulates payload data
type recordedRequests struct {
Data []Payload `json:"data"`
}

type recordsCount struct {
Count int `json:"count"`
}

type StateRequest struct {
Mode string `json:"mode"`
Destination string `json:"destination"`
Expand Down Expand Up @@ -49,6 +53,8 @@ func getBoneRouter(d DBClient) *bone.Mux {
mux.Delete("/records", http.HandlerFunc(d.DeleteAllRecordsHandler))
mux.Post("/records", http.HandlerFunc(d.ImportRecordsHandler))

mux.Get("/count", http.HandlerFunc(d.RecordsCount))

mux.Get("/state", http.HandlerFunc(d.CurrentStateHandler))
mux.Post("/state", http.HandlerFunc(d.stateHandler))

Expand Down Expand Up @@ -87,6 +93,35 @@ func (d *DBClient) AllRecordsHandler(w http.ResponseWriter, req *http.Request) {
}
}

func (d *DBClient) RecordsCount(w http.ResponseWriter, req *http.Request) {
records, err := d.cache.GetAllRequests()

if err == nil {

w.Header().Set("Content-Type", "application/json")

var response recordsCount
response.Count = len(records)
b, err := json.Marshal(response)

if err != nil {
log.Error(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
} else {
w.Write(b)
return
}
} else {
log.WithFields(log.Fields{
"Error": err.Error(),
}).Error("Failed to get data from cache!")

w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(500) // can't process this entity
return
}
}

func (d *DBClient) ImportRecordsHandler(w http.ResponseWriter, req *http.Request) {

var requests recordedRequests
Expand Down
4 changes: 4 additions & 0 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ type Cache struct {
}

func getDB(name string) *bolt.DB {
log.WithFields(log.Fields{
"databaseName": name,
"Mode": AppConfig.mode,
}).Info("Initiating database")
db, err := bolt.Open(name, 0600, nil)
if err != nil {
log.Fatal(err)
Expand Down
3 changes: 1 addition & 2 deletions cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ func TestSetKey(t *testing.T) {
server, dbClient := testTools(201, `{'message': 'here'}`)
defer server.Close()

k := []byte("randomKey_here")
k := []byte("randomkeyhere")
v := []byte("value")

err := dbClient.cache.Set(k, v)
Expand All @@ -20,7 +20,6 @@ func TestSetKey(t *testing.T) {
value, err := dbClient.cache.Get(k)
expect(t, err, nil)
refute(t, value, nil)
// expect(t, value, v)
dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
}

Expand Down
23 changes: 16 additions & 7 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
![Hoverfly](static/images/hf-logo-std-r-transparent-medium.png)
## Dependencies without the sting

[Hoverfly](http://hoverfly.io) is a lightweight, open source [service virtualization](https://en.wikipedia.org/wiki/Service_virtualization) tool. Using Hoverfly, you can virtualize your application dependencies to create a self-contained development or test environment.
[Hoverfly](http://hoverfly.io) is a lightweight, open source [service virtualization](https://en.wikipedia.org/wiki/Service_virtualization) tool.
Using Hoverfly, you can virtualize your application dependencies to create a self-contained development or test environment.

Hoverfly is a proxy written in [Go](https://github.com/golang/go). It can capture HTTP(s) traffic between an application under test and external services, and then replace the external services. Another powerful feature: middleware modules, where users can introduce their own custom logic. **Middleware modules can be written in any language**. Hoverfly uses [Redis](http://redis.io/) for persistence.
Hoverfly is a proxy written in [Go](https://github.com/golang/go). It can capture HTTP(s) traffic between an application under test
and external services, and then replace the external services. Another powerful feature: middleware modules, where users
can introduce their own custom logic. **Middleware modules can be written in any language**.

More information about Hoverfly and how to use it:
* https://www.specto.io/speeding-up-your-slow-dependencies/
Expand All @@ -23,7 +26,7 @@ The Vagrant provisioning script will start hoverfly in the background in ["virtu

### Build it yourself

Ensure you have [Redis](http://redis.io), then use [Glide](https://github.com/Masterminds/glide) to fetch the dependencies with:
Use [Glide](https://github.com/Masterminds/glide) to fetch the dependencies (or you can also use _git submodule init_) with:

glide up

Expand All @@ -37,11 +40,15 @@ And run it:

### Pre-built binary

Pre-built Hoverfly binaries are available [here](https://github.com/SpectoLabs/hoverfly/releases/). You may find it easier to download a binary - however since the Hoverfly admin UI requires static files you will need to clone the Hoverfly repo first, and then copy the binary to the Hoverfly directory before executing it. You will also need [Redis](http://redis.io/).
Pre-built Hoverfly binaries are available [here](https://github.com/SpectoLabs/hoverfly/releases/).
You may find it easier to download a binary - however since the Hoverfly admin UI requires static files you will need
to clone the Hoverfly repo first, and then copy the binary to the Hoverfly directory before executing it.

## Admin UI

The Hoverfly admin UI is available at [http://localhost:8888/](http://localhost:8888/). It uses the [API](api) (as described below) to change state. It also allows you to wipe the captured requests/responses and shows the number of captured records. For other functions, such as export/import, you can use the API directly.
The Hoverfly admin UI is available at [http://localhost:8888/](http://localhost:8888/). It uses the [API](api)
(as described below) to change state. It also allows you to wipe the captured requests/responses and shows the number
of captured records. For other functions, such as export/import, you can use the API directly.

## Hoverfly is a proxy

Expand All @@ -58,15 +65,17 @@ You can specify which site to capture or virtualize with a regular expression (b
## Modes (Virtualize / Capture / Synthesize / Modify)

Hoverfly has different operating modes. Each mode changes the behavior of the proxy. Based on the selected mode, Hoverfly can
either capture the requests and responses, look for them in the cache, or send them directly to the middleware and respond with a payload that is generated by the middleware (more on middleware below).
either capture the requests and responses, look for them in the cache, or send them directly to the middleware and
respond with a payload that is generated by the middleware (more on middleware below).

### Virtualize

By default, the proxy starts in virtualize mode. You can apply middleware to each response.

### Capture

When capture mode is active, Hoverfly acts as a "man-in-the-middle". It makes requests on behalf of a client and records the responses. The response is then sent back to the original client.
When capture mode is active, Hoverfly acts as a "man-in-the-middle". It makes requests on behalf of a client and records
the responses. The response is then sent back to the original client.

To switch to capture mode, you can add the "--capture" flag during startup:

Expand Down
12 changes: 3 additions & 9 deletions settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"os"
)

// Initial structure of configuration
// Configuration - initial structure of configuration
type Configuration struct {
adminInterface string
mode string
Expand All @@ -13,7 +13,7 @@ type Configuration struct {
databaseName string
}

// AppCondig stores application configuration
// AppConfig stores application configuration
var AppConfig Configuration

func initSettings() {
Expand All @@ -24,14 +24,8 @@ func initSettings() {
if databaseName == "" {
databaseName = "requests.db"
}

// getting destination information
// AppConfig.destination = "get this from cache"

// proxy state
// should be taken from cache if we want to make it horizontally scalable (currently not needed)
AppConfig.databaseName = databaseName

// middleware configuration
AppConfig.middleware = os.Getenv("HoverflyMiddleware")

}
4 changes: 2 additions & 2 deletions static/js/dist/home-bundle.js

Large diffs are not rendered by default.

17 changes: 6 additions & 11 deletions static/js/src/home.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ let ModeInfoComponent = React.createClass({
<div>
<p>
When capture mode is active, Hoverfly intercepts requests and then makes them on behalf of the client.
In this mode, middleware is applied to outgoing traffic. Requests and responses are stored in Redis as JSON structures.
In this mode, middleware is applied to outgoing traffic. Requests and responses are stored in
embedded database as JSON structures.
</p>
</div>
)
Expand Down Expand Up @@ -95,23 +96,17 @@ let StatsComponent = React.createClass({
},

fetchData() {
var url = '/records';
var url = '/count';
var that = this;
request
.get(url)
.end(function (err, res) {
if (err) throw err;
if (that.isMounted()) {
// checking whether there are any records
if (res.body.data == null) {
that.setState({
'records': 0
});
} else {
that.setState({
'records': res.body.data.length
});
}
that.setState({
'records': res.body.count
});
}
});
},
Expand Down
21 changes: 12 additions & 9 deletions test_tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,23 @@ import (
"net/http"
"net/http/httptest"
"net/url"
"os"
"reflect"
"testing"
"time"

"github.com/boltdb/bolt"
)

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const (
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)

const testingDatabaseName = "test.db" // very original

// Client structure to be injected into functions to perform HTTP calls
type Client struct {
HTTPClient *http.Client
Expand Down Expand Up @@ -57,13 +67,6 @@ func testTools(code int, body string) (*httptest.Server, *DBClient) {
return server, dbClient
}

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const (
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)

var src = rand.NewSource(time.Now().UnixNano())

func GetRandomName(n int) []byte {
Expand All @@ -84,11 +87,11 @@ func GetRandomName(n int) []byte {
}

func setup() {
db := getDB("test.db")
db := getDB(testingDatabaseName)
TestDB = db
}

// teardown does some cleanup after tests
func teardown() {
// TODO: delete test.db file here
os.Remove(testingDatabaseName)
}
1 change: 1 addition & 0 deletions vendor/github.com/boltdb/bolt
Submodule bolt added at 34a0fa

0 comments on commit ef92f86

Please sign in to comment.