diff --git a/accountservice/Dockerfile b/accountservice/Dockerfile
deleted file mode 100644
index 9f596c0..0000000
--- a/accountservice/Dockerfile
+++ /dev/null
@@ -1,6 +0,0 @@
-FROM iron/base
-EXPOSE 6767
-
-ADD accountservice-linux-amd64 /
-
-ENTRYPOINT ["./accountservice-linux-amd64"]
\ No newline at end of file
diff --git a/accountservice/dbclient/boltclient.go b/accountservice/dbclient/boltclient.go
deleted file mode 100644
index 43123de..0000000
--- a/accountservice/dbclient/boltclient.go
+++ /dev/null
@@ -1,107 +0,0 @@
-package dbclient
-
-import (
- "github.com/boltdb/bolt"
- "log"
- "strconv"
- "github.com/callistaenterprise/goblog/accountservice/model"
- "encoding/json"
- "fmt"
-)
-
-
-
-type IBoltClient interface {
- OpenBoltDb()
- QueryAccount(accountId string) (model.Account, error)
- Seed()
-}
-
-// Real implementation
-type BoltClient struct {
- boltDB *bolt.DB
-}
-
-func (bc *BoltClient) OpenBoltDb() {
- var err error
- bc.boltDB, err = bolt.Open("accounts.db", 0600, nil)
- if err != nil {
- log.Fatal(err)
- }
-}
-
-func (bc *BoltClient) QueryAccount(accountId string) (model.Account, error) {
- // Allocate an empty Account instance we'll let json.Unmarhal populate for us in a bit.
- account := model.Account{}
-
- // Read an object from the bucket using boltDB.View
- err := bc.boltDB.View(func(tx *bolt.Tx) error {
- // Read the bucket from the DB
- b := tx.Bucket([]byte("AccountBucket"))
-
- // Read the value identified by our accountId supplied as []byte
- accountBytes := b.Get([]byte(accountId))
- if accountBytes == nil {
- return fmt.Errorf("No account found for " + accountId)
- }
- // Unmarshal the returned bytes into the account struct we created at
- // the top of the function
- json.Unmarshal(accountBytes, &account)
-
- // Return nil to indicate nothing went wrong, e.g no error
- return nil
- })
- // If there were an error, return the error
- if err != nil {
- return model.Account{}, err
- }
- // Return the Account struct and nil as error.
- return account, nil
-}
-
-
-// Start seeding accounts
-func (bc *BoltClient) Seed() {
- bc.initializeBucket()
- bc.seedAccounts()
-}
-
-// Creates an "AccountBucket" in our BoltDB. It will overwrite any existing bucket of the same name.
-func (bc *BoltClient) initializeBucket() {
- bc.boltDB.Update(func(tx *bolt.Tx) error {
- _, err := tx.CreateBucket([]byte("AccountBucket"))
- if err != nil {
- return fmt.Errorf("create bucket failed: %s", err)
- }
- return nil
- })
-}
-
-// Seed (n) make-believe account objects into the AcountBucket bucket.
-func (bc *BoltClient) seedAccounts() {
-
- total := 100
- for i := 0; i < total; i++ {
-
- // Generate a key 10000 or larger
- key := strconv.Itoa(10000 + i)
-
- // Create an instance of our Account struct
- acc := model.Account{
- Id: key,
- Name: "Person_" + strconv.Itoa(i),
- }
-
- // Serialize the struct to JSON
- jsonBytes, _ := json.Marshal(acc)
-
- // Write the data to the AccountBucket
- bc.boltDB.Update(func(tx *bolt.Tx) error {
- b := tx.Bucket([]byte("AccountBucket"))
- err := b.Put([]byte(key), jsonBytes)
- return err
- })
- }
- fmt.Printf("Seeded %v fake accounts...\n", total)
-}
-
diff --git a/accountservice/dbclient/mockclient.go b/accountservice/dbclient/mockclient.go
deleted file mode 100644
index 1cad809..0000000
--- a/accountservice/dbclient/mockclient.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package dbclient
-
-import (
- "github.com/stretchr/testify/mock"
- "github.com/callistaenterprise/goblog/accountservice/model"
-)
-
-// MockBoltClient is a mock implementation of a datastore client for testing purposes
-type MockBoltClient struct {
- mock.Mock
-}
-
-func (m *MockBoltClient) QueryAccount(accountId string) (model.Account, error) {
- args := m.Mock.Called(accountId)
- return args.Get(0).(model.Account), args.Error(1)
-}
-
-func (m *MockBoltClient) OpenBoltDb() {
- // Does nothing
-}
-
-func (m *MockBoltClient) Seed() {
- // Does nothing
-}
diff --git a/accountservice/main.go b/accountservice/main.go
index 16760fd..9a96b44 100644
--- a/accountservice/main.go
+++ b/accountservice/main.go
@@ -3,19 +3,12 @@ package main
import (
"fmt"
"github.com/callistaenterprise/goblog/accountservice/service"
- "github.com/callistaenterprise/goblog/accountservice/dbclient"
)
var appName = "accountservice"
func main() {
fmt.Printf("Starting %v\n", appName)
- initializeBoltClient()
service.StartWebServer("6767")
}
-func initializeBoltClient() {
- service.DBClient = &dbclient.BoltClient{}
- service.DBClient.OpenBoltDb()
- service.DBClient.Seed()
-}
diff --git a/accountservice/model/account.go b/accountservice/model/account.go
deleted file mode 100644
index 87da3c2..0000000
--- a/accountservice/model/account.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package model
-
-type Account struct {
- Id string `json:"id"`
- Name string `json:"name"`
-}
-
-func (a *Account) ToString() string {
- return a.Id + " " + a.Name
-}
\ No newline at end of file
diff --git a/accountservice/service/handlers.go b/accountservice/service/handlers.go
deleted file mode 100644
index 3895e2b..0000000
--- a/accountservice/service/handlers.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package service
-
-import (
- "net/http"
- "github.com/gorilla/mux"
- "encoding/json"
- "strconv"
- "github.com/callistaenterprise/goblog/accountservice/dbclient"
- "fmt"
-)
-
-var DBClient dbclient.IBoltClient
-
-func GetAccount(w http.ResponseWriter, r *http.Request) {
-
- // Read the 'accountId' path parameter from the mux map
- var accountId = mux.Vars(r)["accountId"]
-
- // Read the account struct BoltDB
- account, err := DBClient.QueryAccount(accountId)
-
- // If err, return a 404
- if err != nil {
- fmt.Println("Some error occured serving " + accountId + ": " + err.Error())
- w.WriteHeader(http.StatusNotFound)
- return
- }
-
- // If found, marshal into JSON, write headers and content
- data, _ := json.Marshal(account)
- w.Header().Set("Content-Type", "application/json")
- w.Header().Set("Content-Length", strconv.Itoa(len(data)))
- w.WriteHeader(http.StatusOK)
- w.Write(data)
-}
\ No newline at end of file
diff --git a/accountservice/service/handlers_test.go b/accountservice/service/handlers_test.go
deleted file mode 100644
index f8d8aa6..0000000
--- a/accountservice/service/handlers_test.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package service
-
-import (
- . "github.com/smartystreets/goconvey/convey"
- "testing"
- "net/http/httptest"
- "github.com/callistaenterprise/goblog/accountservice/dbclient"
- "github.com/callistaenterprise/goblog/accountservice/model"
- "fmt"
- "encoding/json"
-)
-
-
-
-
-func TestGetAccount(t *testing.T) {
- mockRepo := &dbclient.MockBoltClient{}
-
- mockRepo.On("QueryAccount", "123").Return(model.Account{Id:"123", Name:"Person_123"}, nil)
- mockRepo.On("QueryAccount", "456").Return(model.Account{}, fmt.Errorf("Some error"))
- DBClient = mockRepo
-
- Convey("Given a HTTP request for /accounts/123", t, func() {
- req := httptest.NewRequest("GET", "/accounts/123", nil)
- resp := httptest.NewRecorder()
-
- Convey("When the request is handled by the Router", func() {
- NewRouter().ServeHTTP(resp, req)
-
- Convey("Then the response should be a 200", func() {
- So(resp.Code, ShouldEqual, 200)
-
- account := model.Account{}
- json.Unmarshal(resp.Body.Bytes(), &account)
- So(account.Id, ShouldEqual, "123")
- So(account.Name, ShouldEqual, "Person_123")
- })
- })
- })
-
- Convey("Given a HTTP request for /accounts/456", t, func() {
- req := httptest.NewRequest("GET", "/accounts/456", nil)
- resp := httptest.NewRecorder()
-
- Convey("When the request is handled by the Router", func() {
- NewRouter().ServeHTTP(resp, req)
-
- Convey("Then the response should be a 404", func() {
- So(resp.Code, ShouldEqual, 404)
- })
- })
- })
-}
-
-func TestGetAccountWrongPath(t *testing.T) {
-
- Convey("Given a HTTP request for /invalid/123", t, func() {
- req := httptest.NewRequest("GET", "/invalid/123", nil)
- resp := httptest.NewRecorder()
-
- Convey("When the request is handled by the Router", func() {
- NewRouter().ServeHTTP(resp, req)
-
- Convey("Then the response should be a 404", func() {
- So(resp.Code, ShouldEqual, 404)
- })
- })
- })
-}
\ No newline at end of file
diff --git a/accountservice/service/routes.go b/accountservice/service/routes.go
index dbbfa40..00044f6 100644
--- a/accountservice/service/routes.go
+++ b/accountservice/service/routes.go
@@ -20,6 +20,9 @@ var routes = Routes{
"GetAccount", // Name
"GET", // HTTP method
"/accounts/{accountId}", // Route pattern
- GetAccount,
+ func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json; charset=UTF-8")
+ w.Write([]byte("{\"result\":\"OK\"}"))
+ },
},
}
diff --git a/copyall.sh b/copyall.sh
index 92347eb..55698cd 100755
--- a/copyall.sh
+++ b/copyall.sh
@@ -5,5 +5,3 @@ export CGO_ENABLED=0
cd accountservice;go get;go build -o accountservice-linux-amd64;echo built `pwd`;cd ..
export GOOS=darwin
-
-docker build -t someprefix/accountservice accountservice/
\ No newline at end of file
diff --git a/loadtest/README.md b/loadtest/README.md
new file mode 100644
index 0000000..e139db0
--- /dev/null
+++ b/loadtest/README.md
@@ -0,0 +1,5 @@
+# Load test for the Go blog series
+
+### Usage
+
+ mvn gatling:execute -Dusers=1000 -Dduration=30 -DbaseUrl=http://192.168.99.100:6767
\ No newline at end of file
diff --git a/loadtest/pom.xml b/loadtest/pom.xml
new file mode 100644
index 0000000..7d37129
--- /dev/null
+++ b/loadtest/pom.xml
@@ -0,0 +1,65 @@
+
+
+ 4.0.0
+
+ se.callistaenterprise.goblog
+ loadtest
+ 1.0-SNAPSHOT
+
+
+
+ excilys
+ Excilys Repository
+ http://repository.excilys.com/content/groups/public
+
+
+
+
+
+ excilys
+ Excilys Repository
+ http://repository.excilys.com/content/groups/public
+
+
+
+
+ 2.1.7
+ 2.1.7
+
+
+
+
+ io.gatling.highcharts
+ gatling-charts-highcharts
+ ${gatling.version}
+ test
+
+
+
+
+
+
+ io.gatling
+ gatling-maven-plugin
+ ${gatling-plugin.version}
+
+ gatling
+ ${simulationClass}
+
+ -Dusers=1
+ -DbaseUrl=http://localhost:6767
+ -Dduration=30
+
+
+
+
+ test
+
+ execute
+
+
+
+
+
+
+
diff --git a/loadtest/src/test/resources/application.conf b/loadtest/src/test/resources/application.conf
new file mode 100755
index 0000000..caa0769
--- /dev/null
+++ b/loadtest/src/test/resources/application.conf
@@ -0,0 +1,9 @@
+####################################
+# Akka Actor Config File #
+####################################
+
+akka {
+ scheduler {
+ tick-duration = 50ms
+ }
+}
diff --git a/loadtest/src/test/resources/data/accounts.csv b/loadtest/src/test/resources/data/accounts.csv
new file mode 100644
index 0000000..4ab339b
--- /dev/null
+++ b/loadtest/src/test/resources/data/accounts.csv
@@ -0,0 +1,31 @@
+accountId
+10000
+10001
+10002
+10003
+10004
+10005
+10006
+10007
+10008
+10009
+10010
+10011
+10012
+10013
+10014
+10015
+10016
+10017
+10018
+10019
+10020
+10021
+10022
+10023
+10024
+10025
+10026
+10027
+10028
+10029
diff --git a/loadtest/src/test/resources/gatling.conf b/loadtest/src/test/resources/gatling.conf
new file mode 100755
index 0000000..6417340
--- /dev/null
+++ b/loadtest/src/test/resources/gatling.conf
@@ -0,0 +1,88 @@
+#########################
+# Gatling Configuration #
+#########################
+
+# This file contains all the settings configurable for Gatling with their default values
+
+gatling {
+ core {
+ #outputDirectoryBaseName = ""
+ #runDescription = ""
+ #encoding = "utf-8" # encoding for every file manipulation made in gatling
+ #simulationClass = ""
+ extract {
+ regex {
+ #cache = true
+ }
+ xpath {
+ #cache = true
+ }
+ jsonPath {
+ #cache = true
+ }
+ css {
+ #engine = jodd # can change to jsoup
+ }
+ }
+ timeOut {
+ #simulation = 86400 # in s
+ #actor = 5 # in s
+ }
+ directory {
+ #data = user-files/data
+ #requestBodies = user-files/request-bodies
+ #simulations = user-files/simulations
+ #reportsOnly = ""
+ #binaries = ""
+ #results = results
+ }
+ }
+ charting {
+ #noReports = false
+ #maxPlotPerSeries = 1000
+ #accuracy = 10 # in ms
+ indicators {
+ #lowerBound = 800 # in ms
+ #higherBound = 1200 # in ms
+ #percentile1 = 95 # in percents
+ #percentile2 = 99 # in percents
+ }
+ }
+ http {
+ #allowPoolingConnection = true
+ #allowSslConnectionPool = true
+ #compressionEnabled = true # Set if compression should be supported or not
+ #connectionTimeout = 60000 # Timeout of the connection to the server (ms)
+ #idleConnectionInPoolTimeoutInMs = 60000
+ #idleConnectionTimeoutInMs = 60000
+ #maxConnectionLifeTimeInMs = -1 # max duration a connection can stay open
+ #ioThreadMultiplier = 2
+ #maximumConnectionsPerHost = -1
+ #maximumConnectionsTotal = -1
+ #maxRetry = 4 # number of times that a request should be tried again
+ #requestCompressionLevel = -1
+ #requestTimeoutInMs = 60000 # Timeout of the requests (ms)
+ #useProxyProperties = false
+ #userAgent = "NING/1.0"
+ #useRawUrl = false
+ #warmUpUrl = "http://goo.gl/wqthq"
+ #rfc6265CookieEncoding = true # use rfc6265 cookie encoding style
+ }
+ data {
+ #writers = "console, file"
+ #reader = file
+ console {
+ #light = false
+ }
+ file {
+ bufferSize = 8192
+ }
+ graphite {
+ #light = false
+ #host = "localhost"
+ #port = 2003
+ #rootPathPrefix = "gatling"
+ #bucketWidth = 100
+ }
+ }
+}
diff --git a/loadtest/src/test/resources/logback.xml b/loadtest/src/test/resources/logback.xml
new file mode 100755
index 0000000..756e2a4
--- /dev/null
+++ b/loadtest/src/test/resources/logback.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%-5level] %logger{15} - %msg%n%rEx
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/loadtest/src/test/scala/se/callistaenterprise/goblog/Conf.scala b/loadtest/src/test/scala/se/callistaenterprise/goblog/Conf.scala
new file mode 100644
index 0000000..21bf62e
--- /dev/null
+++ b/loadtest/src/test/scala/se/callistaenterprise/goblog/Conf.scala
@@ -0,0 +1,12 @@
+package se.callistaenterprise.goblog
+
+import io.gatling.core.Predef._
+import io.gatling.http.Predef._
+import io.gatling.jdbc.Predef._
+
+object Conf {
+ var users = System.getProperty("users", "2").toInt
+ val baseUrl = System.getProperty("baseUrl", "http://localhost:6767")
+ var httpConf = http.baseURL(baseUrl)
+ var duration = System.getProperty("duration", "30").toInt
+}
\ No newline at end of file
diff --git a/loadtest/src/test/scala/se/callistaenterprise/goblog/Headers.scala b/loadtest/src/test/scala/se/callistaenterprise/goblog/Headers.scala
new file mode 100644
index 0000000..34fbb3e
--- /dev/null
+++ b/loadtest/src/test/scala/se/callistaenterprise/goblog/Headers.scala
@@ -0,0 +1,8 @@
+package se.callistaenterprise.goblog
+
+object Headers {
+ // HTTP Headers
+ val http_header = Map(
+ "Accept" -> "application/json;")
+
+ }
\ No newline at end of file
diff --git a/loadtest/src/test/scala/se/callistaenterprise/goblog/LoadTest.scala b/loadtest/src/test/scala/se/callistaenterprise/goblog/LoadTest.scala
new file mode 100644
index 0000000..9b263d6
--- /dev/null
+++ b/loadtest/src/test/scala/se/callistaenterprise/goblog/LoadTest.scala
@@ -0,0 +1,13 @@
+package se.callistaenterprise.goblog
+
+import io.gatling.core.Predef._
+import io.gatling.http.Predef._
+import io.gatling.jdbc.Predef._
+import scala.concurrent.duration._
+
+class LoadTest extends Simulation {
+
+ setUp(
+ Scenarios.scn_Browse.inject(rampUsers(Conf.users) over (Scenarios.rampUpTimeSecs seconds)).protocols(Conf.httpConf)
+ )
+}
\ No newline at end of file
diff --git a/loadtest/src/test/scala/se/callistaenterprise/goblog/Scenarios.scala b/loadtest/src/test/scala/se/callistaenterprise/goblog/Scenarios.scala
new file mode 100644
index 0000000..8b4bde2
--- /dev/null
+++ b/loadtest/src/test/scala/se/callistaenterprise/goblog/Scenarios.scala
@@ -0,0 +1,29 @@
+package se.callistaenterprise.goblog
+
+import io.gatling.core.Predef._
+import io.gatling.http.Predef._
+import io.gatling.jdbc.Predef._
+import scala.concurrent.duration._
+
+object Scenarios {
+
+ val rampUpTimeSecs = 10
+
+ /*
+ * HTTP scenarios
+ */
+
+ // Browse
+ val browse_guids = csv("accounts.csv").circular
+ val scn_Browse = scenario("GetAccounts")
+ .during(Conf.duration) {
+ feed(browse_guids)
+ .exec(
+ http("GetAccount")
+ .get("/accounts/" + "${accountId}")
+ .headers(Headers.http_header)
+ .check(status.is(200))
+ )
+ .pause(1)
+ }
+}
\ No newline at end of file