Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceRoot}",
"args": ["-p", ".data/sample.yaml"]
}
]
}
5 changes: 5 additions & 0 deletions bindings/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,8 @@ type Proposal struct {
Method string `yaml:"method,omitempty"`
Url string `yaml:"url,omitempty"`
}

type Executor interface {
Initialize(protocol Protocol)
Execute(proposal Proposal) (Test, error)
}
42 changes: 37 additions & 5 deletions engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@ package engine

import (
"infantry/bindings"
"infantry/protocols/http"
"reflect"
)

// https://www.geeksforgeeks.org/function-as-a-field-in-golang-structure/

var _report bindings.Report
var _plan bindings.Plan
var _executor bindings.Executor

func Start(plan bindings.Plan) (bindings.Report, error) {
SetupReport(_report)
FirePlanStartEvent(nil)
CreateExecutor(_plan.Protocol)
for _, stage := range plan.Setup.Stages {
ExecuteStage(stage, plan.Proposals)
}
Expand All @@ -21,17 +25,41 @@ func Start(plan bindings.Plan) (bindings.Report, error) {
return _report, nil
}

func CreateExecutor(protocol bindings.Protocol) {
//TODO - verify this detects protocol
if (reflect.DeepEqual(bindings.ProtocolHttp{}, protocol.Http)) {
_executor = http.HttpExecutor{}
}

_executor.Initialize(protocol)
}

func ExecuteStage(stage bindings.Stage, proposals []bindings.Proposal) {
FireStageStartedEvent(nil)

var currentIteration = 0
var currentUsers = stage.AddUsersPerIterations
ExecuteEachSecondConcurrent(stage.Iterations, func() {
ExecuteUserProposals(proposals, stage.AddUsersPerIterations, stage.MaxUsersAtOnce)
if currentIteration > stage.Iterations {
return
}

ExecuteUserProposals(proposals, currentUsers)

var usersToAdd = stage.MaxUsersAtOnce - currentUsers
if usersToAdd > 0 {
currentUsers += usersToAdd
}

currentIteration++
})

FireStageCompletedEvent(nil)
}

func ExecuteUserProposals(proposals []bindings.Proposal, addUsers int, maxUsers int) {
func ExecuteUserProposals(proposals []bindings.Proposal, currentUsers int) {
FireProposalStartedEvent(nil)
ExecuteNumberOfTimesConcurrent(addUsers, maxUsers, func() {
ExecuteNumberOfTimesConcurrent(currentUsers, func() {
ExecuteTasks(User{}, proposals)
})
FireProposalCompletedEvent(nil)
Expand All @@ -40,10 +68,14 @@ func ExecuteUserProposals(proposals []bindings.Proposal, addUsers int, maxUsers
func ExecuteTasks(user User, proposals []bindings.Proposal) {
for _, proposal := range proposals {
FireProposalTaskStartedEvent(proposal)
var resp, err = user.ExecuteProposal(_plan.Protocol, proposal)

var resp, err = _executor.Execute((proposal))

if err != nil {
FireProposalTaskFailureEvent(resp)
return
}
//else { FireProposalTaskSuccessEvent(resp) }

FireProposalTaskSuccessEvent(resp)
}
}
32 changes: 20 additions & 12 deletions engine/loops.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,42 @@ package engine
// https://stackoverflow.com/a/16466581

import (
"fmt"
"sync"
"time"
)

// ExecuteEachSecondConcurrent Executes a function each second concurrently with a max count
func ExecuteEachSecondConcurrent(maxSeconds int, delegate func()) {
var wg sync.WaitGroup
wg.Add(maxSeconds)
for _ = range time.Tick(time.Duration(1) * time.Second) {
go func() {
d := time.NewTicker(1 * time.Second)

exitChannel := make(chan bool)

go func() {
time.Sleep(time.Duration(maxSeconds) * time.Second)
exitChannel <- true
}()

for {
select {
case <-exitChannel:
fmt.Println("Completed!")
d.Stop()
return
case timeElapsed := <-d.C:
fmt.Printf("elapsed: %+v\n", timeElapsed)
delegate()
wg.Done()
}()
}
}
wg.Wait()
}

// ExecuteNumberOfTimesConcurrent Executes a function n number of times with a max concurrent number using a guard
func ExecuteNumberOfTimesConcurrent(times int, maxConcurrently int, delegate func()) {
var guard = make(chan struct{}, maxConcurrently)
func ExecuteNumberOfTimesConcurrent(times int, delegate func()) {
var wg sync.WaitGroup
wg.Add(times)
for i := 1; i <= times; i++ {
go func(n int) {
guard <- struct{}{}
delegate()
wg.Done()
<-guard
}(i)
}
wg.Wait()
Expand Down
7 changes: 4 additions & 3 deletions engine/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package engine

import (
"fmt"
"github.com/google/uuid"
"infantry/bindings"

"github.com/google/uuid"
)

type User struct {
Expand All @@ -13,8 +14,8 @@ type User struct {
// ExecuteProposal Creates a virtual user and executes the proposal
func (user User) ExecuteProposal(protocol bindings.Protocol, proposal bindings.Proposal) (bindings.Test, error) {
fmt.Printf("protocol: %+v\n", protocol)
if protocol.Http != bindings.ProtocolHttp{} {
/*if protocol.Http != bindings.ProtocolHttp{} {

}
}*/
return bindings.Test{}, nil
}
15 changes: 15 additions & 0 deletions protocols/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ package http
import (
"bytes"
"encoding/json"
"fmt"
"infantry/bindings"
"net/http"
"time"
)

type HttpExecutor struct{}

type Agent struct {
Host string
Url string
Expand All @@ -17,6 +20,8 @@ type Agent struct {
Headers []bindings.Header
}

var httpClient http.Client

// CreateTest Creates the base test struct for reporting
func CreateTest(method string, uri string) bindings.Test {
var test bindings.Test
Expand Down Expand Up @@ -100,3 +105,13 @@ func TestOptions(uri string, headers []bindings.Header, skipSsl bool) bindings.T
test := CreateTest(http.MethodOptions, uri)
return test
}

func (httpExecutor HttpExecutor) Initialize(protocol bindings.Protocol) {
//TODO - handle more protocol options on creation
httpClient = CreateHttpClient(protocol.Http.Tls.IgnoreSslErrors)
}

func (httpExecutor HttpExecutor) Execute(proposal bindings.Proposal) (bindings.Test, error) {
fmt.Println("Executing")
return bindings.Test{}, nil
}