Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding network performance measurement testcases. (util-image changes) #1593

Merged
merged 1 commit into from
Dec 11, 2020

Conversation

hanamantagoudvk
Copy link

What type of PR is this?

/kind feature
/kind regression

What this PR does / why we need it:

This review is one of the changeset done as part of the enhancement/feature (Reference #1498)

This changeset has worker-pod implementation.

@k8s-ci-robot k8s-ci-robot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. kind/feature Categorizes issue or PR as related to a new feature. kind/regression Categorizes issue or PR as related to a regression from a prior release. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Nov 24, 2020
@k8s-ci-robot
Copy link
Contributor

Hi @hanamantagoudvk. Thanks for your PR.

I'm waiting for a kubernetes member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@k8s-ci-robot k8s-ci-robot added needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Nov 24, 2020
@hanamantagoudvk
Copy link
Author

/cc @aojea

@k8s-ci-robot k8s-ci-robot requested a review from aojea November 24, 2020 14:08
@@ -0,0 +1,31 @@
FROM golang:1.15.2-alpine3.12 AS build-env
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need alpine?

Can't you simply use golang:1.15.2?

There are frequent security vulneraibilities in those more complex images.

Copy link

@VivekThrivikraman-est VivekThrivikraman-est Nov 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed build env to use golang image(for running we need to install tools hence that image isn't changed).

@@ -0,0 +1,31 @@
FROM golang:1.15.2-alpine3.12 AS build-env
ARG gopkg=k8s.io/perf-tests/util-images/phases/netperfbenchmark
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is "phases" here and below?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initial implementation was using phases and this is coming from there, will update appropriately.

@@ -0,0 +1,17 @@
PROJECT = k8s-testimages
IMG = gcr.io/$(PROJECT)/netperfbenchmark
TAG = v0.0.1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0.1

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will update the version.

See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by convention we generally don't use /* */ style comments for anything else than the copyright

Please switch to:

// Package ...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW - this comment is useless - it's obvious what main is doing :)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the comment.

@@ -0,0 +1,7 @@
module k8s.io/perf-tests/util-images/network/netperfbenchmark

go 1.12
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we go higher than 1.12 - it's already out of support window IIRC

Can we use 1.15?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed it to 1.15.

}

func startListening(port string, handlers map[string]func(http.ResponseWriter, *http.Request)) {
klog.Info("In StartHTTPServer")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment isn't meaningful...

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified

}

//Metrics returns the metrics collected
func Metrics(res http.ResponseWriter, req *http.Request) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also going to wait with further review until you rewrite it to be better structured (comment I added in line 42).

Copy link

@VivekThrivikraman-est VivekThrivikraman-est Nov 25, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have made the changes as suggested.

createResp(startWrkResponse{error: "missing/invalid required parameters"}, &res)
return
}
go schedule(protocolUDP, ts, dur, "iperf", []string{"-s", "-f", "K", "-u", "-e", "-i", dur, "-P", numcl})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is significant code duplication - instead can you share the code and just (just call it with different parameters)

Copy link

@VivekThrivikraman-est VivekThrivikraman-est Nov 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified the code as suggested.

}
}

func parseTCP(result []string) []float64 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those parse* functions - can you move them to util.go file or sth like that

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified as suggested.

return sumResult
}

func parseUDP(result []string) []float64 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is huge code duplication between those parse methods.

Can we please reuse that more?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified the code for reusing logic.

go 1.12

require (
k8s.io/klog v0.2.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1.0.0?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed it to 1.0.0.

return
}
go schedule(protocolTCP, ts, dur,
"iperf", []string{"-c", destIP, "-f", "K", "-l",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add a comment explaining the parameters, something like this would work:

// -c destIP connect to destIP
// -f K format Kbits
// -l 20 read/write buffer size 20 butes
// -b 1M bandwidth 1 Mbits
// -i 1 repert stats every 1 sec
// -t dur run durring dur seconds

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The modified code has commands for all protocols declared as part of variable declaration together. Hence have put comment for each tool type with all options we have used. Please let me know if that would suffice.

createResp(startWrkResponse{error: "missing/invalid required parameters"}, &res)
return
}
go schedule(protocolTCP, ts, dur, "iperf", []string{"-s", "-f", "K", "-i", dur, "-P", numcl})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add comment explaining the iperf commands please

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified as described in previous comment.

createResp(startWrkResponse{error: "missing/invalid required parameters"}, &res)
return
}
go schedule(protocolTCP, ts, dur, "iperf", []string{"-s", "-f", "K", "-i", dur, "-P", numcl})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, this iperf -s will never die, I didn't go further in the code, sorry, how do you exit it?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are giving option to wait for specified num of clients before closing, hence after the required number of clients have connected(and collected metrics), iperf would close the server.

resultStatus <- "OK"
}

func parseResult() ([]float64, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a question, should not be easier to parse the iperf output in csv format?

       -y, --reportstyle C|c
              if set to C or c report results as CSV (comma separated values)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iperf2 documentation(https://iperf.fr/iperf-doc.php#doc) has not listed this option, hence didn't notice this option. This would have really helped reducing the parsing code. But after trying with -y looks like its not listing all the metrics, I was not able to see Latency metrics returned for UDP. Will see further on if I am missing something.

@k8s-ci-robot k8s-ci-robot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Nov 27, 2020
@hanamantagoudvk hanamantagoudvk changed the base branch from master to release-1.12 November 28, 2020 07:58
@hanamantagoudvk hanamantagoudvk changed the base branch from release-1.12 to master November 28, 2020 07:58
@k8s-ci-robot k8s-ci-robot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels Nov 28, 2020
@k8s-ci-robot
Copy link
Contributor

Thanks for your pull request. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

📝 Please follow instructions at https://git.k8s.io/community/CLA.md#the-contributor-license-agreement to sign the CLA.

It may take a couple minutes for the CLA signature to be fully registered; after that, please reply here with a new comment and we'll verify. Thanks.


  • If you've already signed a CLA, it's possible we don't have your GitHub username or you're using a different email address. Check your existing CLA data and verify that your email is set on your git commits.
  • If you signed the CLA as a corporation, please sign in with your organization's credentials at https://identity.linuxfoundation.org/projects/cncf to be authorized.
  • If you have done the above and are still having issues with the CLA being reported as unsigned, please log a ticket with the Linux Foundation Helpdesk: https://support.linuxfoundation.org/
  • Should you encounter any issues with the Linux Foundation Helpdesk, send a message to the backup e-mail support address at: [email protected]

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

@k8s-ci-robot k8s-ci-robot added cncf-cla: no Indicates the PR's author has not signed the CNCF CLA. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. and removed cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. cncf-cla: no Indicates the PR's author has not signed the CNCF CLA. labels Nov 28, 2020
)

func main() {
klog.Infof("Worker Pod started")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either make the comment more meaningful (what Pod etc.) or probably even better remove it.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

limitations under the License.
*/

// Package util contains utility methods for worker
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: let's remove this comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.


func main() {
klog.Infof("Worker Pod started")
worker := worker.Worker{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's maybe create a helper NewWorker method in worker pkg.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.


// Number of metrics for each protocol
const (
tcpMtrCnt = 2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't use abbreviations.

tcpMetricsCount

and so on

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

iperfTCPFn = []string{"", "", "", "Sum", "Avg"}
)

// ParseResult parses the response received for each protocol type
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those aren't universal utilities.

Let's move these to pkg/worker (those are helper functions for worker in fact).
Actually, let's move this whole file there

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.


// StartUDPServer starts iperf server for udp measurements
func (w *Worker) StartUDPServer(res http.ResponseWriter, req *http.Request) {
klog.Info("In StartUDPServer")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Starting UDP Server

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

w.startWork(&res, req, util.ProtocolUDP, "iperf", udpServerCmd)
}

// StartUDPClient starts iperf server for udp measurements
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please sort them somehow (first HTTP, TCP, UDP, server before client or whatever)?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.


// Handler handles http requests for http measurements
func (w *Worker) Handler(res http.ResponseWriter, req *http.Request) {
fmt.Fprintf(res, "hi\n")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Writing locally seems strange - you should write to response writer?

Copy link

@VivekThrivikraman-est VivekThrivikraman-est Dec 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have modified inline with other responses, but I thought Fprintf writes to the ResponseWriter itself in this case.

}

// StartTCPClient starts iperf client for tcp measurements
func (w *Worker) StartTCPClient(res http.ResponseWriter, req *http.Request) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of calling it "res", let's call it "rw" (for ResponseWriter).
Same everywhere else

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

func (w *Worker) createResponse(resp interface{}, wr *http.ResponseWriter) {
klog.Info("Inside create resp")
(*wr).Header().Set("Content-Type", "application/json")
(*wr).WriteHeader(http.StatusOK)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, sending StatusOK if the response has an error seems wrong to me.

The createResponse shouldn't be type-agnostic and should send the correct http code.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

@hanamantagoudvk hanamantagoudvk changed the title [WIP] Adding network performance measurement testcases. (util-image changes) Adding network performance measurement testcases. (util-image changes) Nov 30, 2020
@k8s-ci-robot k8s-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Nov 30, 2020

// startResponse is the response sent back to controller on starting a measurement
type startWorkResponse struct {
error string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesn't address the question - you can serialize error to string.
[Though, I'm actually fine with leaving string here.]

That said, I would suggest redoing it to the following:

type MetricsResponse struct {
  Result []float64   // (the question is whether we don't want to be more explicit here, but we can leave it for now
  // Maybe also StartTime - but see my comment below first.
}

type Status struct {
  Message string
}

And basically:

  • if you return non-200 code, then you simply return Status message
  • if you return 200 code, for start you don't return anything, for metrics you return MetricsResponse

}

func (w *Worker) listenToController() {
h := map[string]func(http.ResponseWriter, *http.Request){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

type handlersMap map[string]func(http.ResponseWriter, *http.Request)

and use handlersMap here and below

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified

}
err := http.ListenAndServe(":"+port, nil)
if err != nil {
klog.Fatalf("Failed starting http server for port: %v, Error: %v", port, err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Failed to start http server on port %v: %v", port, err)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified

for urlPath, handler := range handlers {
http.HandleFunc(urlPath, handler)
}
err := http.ListenAndServe(":"+port, nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

if err := ...; err != nil {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified

var status string
select {
case status = <-w.resultStatus:
if status != "OK" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is mixing couple things in the same variable - the fact that we already finished processing and the result.

It would be much cleaner patter to have:

type Worker struct {
  result   []float64
  err       error
  stopCh chan(struct{})
  ...

And basically:

  • stopCh is a channel that defines you whether you already finished processing or not
  • result, err stores the result

So here you will have:

select {
case <- w.stopCh:
  // send metrics
default:
  // send metrics collection in progress

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified


// ParseResult parses the response received for each protocol type
func ParseResult(protocol string, result []string, testDuration string) ([]float64, error) {
klog.Infof("Parsing response for %v", protocol)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This log doesn't bring any value.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

}

func parseIperfResponse(result []string, testDuration string, protocol string, metricCount int, zeroFormat string, fractionZeroFormat string, operators []string) []float64 {
klog.Info("Parsing iperf response")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

continue
}
split := strings.Split(formattedString, " ")
//for bug in iperf tcp
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What bug? I don't understand what you're trying to achieve.

Here you really need comment what is the bug and what youre trying to achieve.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW - it should be done the other way - if this condition is not met, continue (to reduce nesting in the code).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the comment and modified the code.

}
tmp, err := strconv.ParseFloat(v, 64)
if err != nil {
klog.Errorf("Conversion error %v", err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we really proceed here?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added return with error

aggregatedResult := make([]float64, 0, httpMetricsCount)
multiSpaceRegex := regexp.MustCompile(`\s+`)
for _, op := range result {
if canAppend != true && strings.HasPrefix(op, "Transactions:") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, you're trying to remove all lines before "Transactions:" and all after "Shortest transaction".

It would be much cleaner, to first trim the "result" value and do the actually parsing here.
Something like:

result = trimSiegeResponse(result)

trimSiegeResponse {
var beginIndex, endIndex int
for beginIndex := 0; beginIndex < len(result) && !strings.HasPrefix("Transactions":); beginIndex++ {}
if beginIndex == len(result) {
return nil
}
beingIndex++
for endIndex := beginIndex; endIndex < len(result) && !strings.hasPrefix("Short..."); endIndex++ {}
return result[beginIndex:endIndex]
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified

@wojtek-t wojtek-t self-assigned this Dec 6, 2020
type Worker struct {
resultStatus chan string
result []string
testDuration string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes - please make that duration.

The rationale is that in our tests we have conventions of passing duration (e.g. 1s, 2m, etc.). And conversions will allow you to use whatever format is needed below.

return
}
klog.Info("Metrics collected")
parsedResult, err := ParseResult(w.protocol, w.result, w.testDuration)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please extract this parsing to a separate method.

And let's call it from schedule(), not from here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

}
}

func (w *Worker) sendResponse(response interface{}, rw http.ResponseWriter, statusCode int) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be more intuitive to switch order of parameters to:

rw, statusCode, response

[It's basically the order in which you use them]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

klog.Infof("About to wait for futuretime: %v", futureTimestamp)
klog.Infof("Current time: %v", time.Now().Unix())
time.Sleep(time.Duration(futureTimestamp-time.Now().Unix()) * time.Second)
w.startedAt = time.Now()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exactly you would like to check with it?
I don't understand what this can be useful for.

}
go w.schedule(protocol, tsint, valMap["duration"], command, arguments)
w.sendResponse(nil, rw, http.StatusOK)
return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove - it's not needed

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

split := strings.Split(formattedString, " ")
// iperf gives aggregated result in a row with "SUM", but many fields are not getting aggregated
// hence not depending on iperf's aggregation
// if len(split) >= metricCount+1 && "SUM" != split[1] && split[2] == zeroFormat+"-"+testDuration+fractionZeroFormat {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

}

// GetValuesFromURL returns a map with values parsed from http request,for attributes specified in paramsToSearch
func GetValuesFromURL(request *http.Request, parametersToSearch []string) (map[string]string, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it have to be public?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

for beginIndex = 0; beginIndex < len(result) && !strings.HasPrefix(result[beginIndex], "Transactions:"); beginIndex++ {
}
if beginIndex == len(result) {
return nil, errors.New("Error parsing Siege response")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unexpected Siege response: lack of Transactions: line

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

for endIndex = beginIndex + 1; endIndex < len(result) && !strings.HasPrefix(result[endIndex], "Shortest transaction"); endIndex++ {
}
if endIndex == len(result) {
return nil, errors.New("Error parsing Siege response")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

Copy link
Member

@wojtek-t wojtek-t left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're getting relatively close with this PR. Hopefully 1-2 more passes.

case <-w.stopCh:
if w.err != nil {
klog.Errorf("Error collecting metrics: %v", w.err)
w.sendResponse(rw, http.StatusInternalServerError, status{Message: "metrics collection failed: " + w.err.Error()})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

message := fmt.Sprintf("metrics collection failed: %v", w.err)
w.sendResponse(rw, http.Statu..., status{Message: message})

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

w.sendResponse(rw, http.StatusInternalServerError, status{Message: "metrics collection failed: " + w.err.Error()})
return
}
klog.Info("Metrics collected")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: let's remove

var reply metricResponse
reply.Result = w.parsedResult
timeDifference := w.futureTime.Sub(w.startedAt)
reply.WorkerStartTime = "StartedAt: " + w.startedAt.String() + " FutureTime: " + w.futureTime.String() + " diffTime: " + timeDifference.String()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't understand why do you need this. Why passing "FutureTime" (i.e. the time when real processing really started" isn't enough?

Can you answer this question?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We wanted the workers to start measuring at almost the same time, especially for the N:1 case. When value of N is high we suspect the time of starting measurement of 1st worker and nth worker could differ by many seconds.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So that I understand. And I buy that.

But that is why you pass the timestamp to the worker when it should start it's work. But that is future time.
[And I understand what it is for.]

I'm asking what do you need "startedAt" for.
This is the time at which worker received the request but didn't really do anything useful.
I don't understand what this one is for and you still didn't answer that.

if !strings.Contains(formattedString, zeroFormat+"-"+testDuration+fractionZeroFormat) {
continue
}
split := strings.Split(formattedString, " ")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you encapsulate the above 5 lines or so into a separate functIon?

Like parseIperfLine or sth like that?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

for i, v := range split {
if i == 1 {
sessionID[v] = true
continue
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sessionID[split[1]] = true

before the for loop

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

continue
}
//first index and hte last is ""
if i == 0 || i == 2 || i == metricCount+3 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given this and the above, it would clear to have:

for i := 3; i < len(split); i++ {
  ...
}

Also - how many items in split can be? Can there be more than "metricsCount+3"?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified the loop to avoid specific checks (metricsCount+3 would be the last element in split).

}
tmp, err := strconv.ParseFloat(v, 64)
if err != nil {
klog.Errorf("Conversion error %v", err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's print what you were trying to parse - otherwise you will never debug it.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

for _, op := range result {
formattedString := multiSpaceRegex.ReplaceAllString(op, " ")
split := strings.Split(formattedString, ":")
klog.Infof("Formatted: %v", formattedString)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

formattedString := multiSpaceRegex.ReplaceAllString(op, " ")
split := strings.Split(formattedString, ":")
klog.Infof("Formatted: %v", formattedString)
if len(split) > 1 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if len(split) <= 1 {
continue
}

to avoid nesting

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

split := strings.Split(formattedString, ":")
klog.Infof("Formatted: %v", formattedString)
if len(split) > 1 {
split := strings.Split(split[1], " ")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name collision - please rename to avoid confusion

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

select {
case <-w.stopCh:
if w.err != nil {
klog.Errorf("Error collecting metrics: %v", w.err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's not log it here - instead, let's log it where you set w.err (in schedule function)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

w.sendResponse(rw, http.StatusInternalServerError, status{Message: message})
return
}
var reply metricResponse
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

reply := metricResponse{
  Result:           w.parsedResult,
  WorkerDelay: w.workerDelay,
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified

reply.WorkerDelay = w.workerDelay
w.sendResponse(rw, http.StatusOK, reply)
default:
klog.Info("Metric collection in progress")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's remove this log

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the log.

rw.Header().Set("Content-Type", "application/json")
marshalledResponse, err := json.Marshal(response)
if err != nil {
klog.Infof("Error marshalling to json: %v", err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Errorf

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

continue
}
sessionID[split[1]] = true
//Index 0,1,2 and last are not metrics
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// The first three and the last items are not metrics in iperf summary.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

}

func parseIperfLine(line string, zeroFormat string, fractionZeroFormat string, testDuration string) []string {
unitRegex := regexp.MustCompile(`%|\[\s+|\]\s+|KBytes\s+|KBytes/sec\s*|sec\s+|pps\s*|ms\s+|/|\(|\)\s+`)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make those file variables, instead of creating them in every call to the function

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

continue
}
parameter := argument[strings.Index(argument, "{")+1 : strings.Index(argument, "}")]
klog.Infof("arg with template: %v", parameter)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merge this log with the below one

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

@@ -0,0 +1,17 @@
PROJECT = k8s-testimages
IMG = gcr.io/$(PROJECT)/netperfbenchmark
TAG = 0.01
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0.1

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

.PHONY: push
push: build
docker push $(IMG):$(TAG)
docker push $(IMG):latest
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need latest? Let's not push latest for now (same for build).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified

Copy link
Member

@wojtek-t wojtek-t left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me make another pass tomorrow - but I think we're pretty close with this PR now.

Copy link
Member

@wojtek-t wojtek-t left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple more minor comments.

}

// metricResponse is the response sent back to controller after collecting measurement
type metricResponse struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that it's actually kind-of API, let's make it public.

[Also fix comment then]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

}

// status is the response sent back to controller when the request is unsuccessful
type status struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here - make public

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

// Start starts the worker
func (w *Worker) Start() {
w.stopCh = make(chan struct{})
// TODO(#1631): make controller and worker communicate using k8s API(with CustomResources)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: whitespace after API (i.e. "... API (Custom...")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.


// Handler handles http requests for http measurements
func (w *Worker) Handler(rw http.ResponseWriter, request *http.Request) {
w.sendResponse(rw, http.StatusOK, "hi")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: let's change "hi" to "ok"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

w.sendResponse(rw, http.StatusOK, "hi")
}

func (w *Worker) startWork(rw http.ResponseWriter, request *http.Request, protocol string, command string, arguments []string) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

... protocol, command string,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

w.sendResponse(rw, http.StatusOK, nil)
}

func (w *Worker) schedule(startTimestamp int64, duration string, protocol string, command string, arguments []string) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

... protocol, command string,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

return aggregatedResult, nil
}

func parseIperfLine(line string, zeroFormat string, fractionZeroFormat string, testDuration string) []string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only line parameter is used

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

interval := zeroFormat + "-" + testDuration + fractionZeroFormat
for _, line := range result {
split := parseIperfLine(line, zeroFormat, fractionZeroFormat, testDuration)
// iperf gives aggregated result in a row with "SUM", but many fields are not getting aggregated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: please shorter the line (e.g break after not)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

}

func parseIperfLine(line string, zeroFormat string, fractionZeroFormat string, testDuration string) []string {
formattedString := hyphenSpaceRegex.ReplaceAllString(multiSpaceRegex.ReplaceAllString(unitRegex.ReplaceAllString(line, " "), " "), "-")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can you please shorten the line by splitting it?
e.g.

noUnitsLine := unitRegex.ReplaceAllString(line, " ")
formatterLine := hyphenSpaceRegex.ReplaceAllString(multiSpaceRegex.ReplaceAllString(noUnitsLine, " "), "-")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.


func trimSiegeResponse(result []string) ([]string, error) {
var beginIndex, endIndex int
for beginIndex = 0; beginIndex < len(result) && !strings.HasPrefix(result[beginIndex], "Transactions:"); beginIndex++ {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

isBeginLine := func(index int) {
  return strings.HasPrefix(result[index], "Transactions:")
}
for beginIndex = 0; beginIndex < len(result) && !isBeginLine(beginIndex); beginIndex++ {
}

same for end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified.

Copy link
Member

@wojtek-t wojtek-t left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some very minor comments.

Also please squash commits.

}
}

func parseIperfResponse(result []string, testDuration string, protocol string, metricCount int, zeroFormat string, fractionZeroFormat string, operators []string) ([]float64, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

result []string, testDuration string, metricCount int, protocol, zeroFormat, fractionZeroFormat string, operators []string

for _, searchString := range parametersToSearch {
val := values.Get(searchString)
if val == "" {
return nil, errors.New("missing required parameters")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what parameter?

fmt.Errorf("missing URL parameter: %s", param)

func getValuesFromURL(request *http.Request, parametersToSearch []string) (map[string]string, error) {
values := request.URL.Query()
paramMap := make(map[string]string)
for _, searchString := range parametersToSearch {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/searchString/param/

RUN cd iperf-2.0.9 && ./configure --prefix=/usr/local --bindir /usr/local/bin && make && make install


ENV VERSION=3.1.4
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SIEGE_VERSION

@wojtek-t
Copy link
Member

/ok-to-test

@k8s-ci-robot k8s-ci-robot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Dec 10, 2020
@wojtek-t
Copy link
Member

@VivekThrivikraman-est VivekThrivikraman-est force-pushed the netperf-msmt-utilImg branch 2 times, most recently from 7af3fb8 to 3618317 Compare December 11, 2020 14:17
Copy link
Member

@wojtek-t wojtek-t left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last few minor comments - once addressed I will approve.

"k8s.io/klog"
)

// Worker hold data required for facilitating measurement
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please finish comments with a dot ("... measurement.")

Same everywhere else below and above.

}
timestamp, err := strconv.ParseInt(valMap["timestamp"], 10, 64)
if err != nil {
klog.Infof("Invalid timestamp: %v %v", valMap["timestamp"], err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

klog.Errorf("Invalid timestamp %v: %v", valMap["timestamp"], err)

@wojtek-t
Copy link
Member

/lgtm
/approve

Thanks

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Dec 11, 2020
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: hanamantagoudvk, wojtek-t

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Dec 11, 2020
@k8s-ci-robot k8s-ci-robot merged commit 7093fc4 into kubernetes:master Dec 11, 2020
@aojea
Copy link
Member

aojea commented Dec 11, 2020

awesome 👏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/feature Categorizes issue or PR as related to a new feature. kind/regression Categorizes issue or PR as related to a regression from a prior release. lgtm "Looks good to me", indicates that a PR is ready to be merged. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants