-
Notifications
You must be signed in to change notification settings - Fork 561
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
Conversation
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 Once the patch is verified, the new status will be reflected by the 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. |
/cc @aojea |
@@ -0,0 +1,31 @@ | |||
FROM golang:1.15.2-alpine3.12 AS build-env |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0.1
There was a problem hiding this comment.
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. | ||
*/ | ||
/* |
There was a problem hiding this comment.
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 ...
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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}) |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1.0.0?
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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}) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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}) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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.
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.
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. |
) | ||
|
||
func main() { | ||
klog.Infof("Worker Pod started") |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding 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{} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Starting UDP Server
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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)?
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modified.
|
||
// startResponse is the response sent back to controller on starting a measurement | ||
type startWorkResponse struct { | ||
error string |
There was a problem hiding this comment.
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){ |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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 {
There was a problem hiding this comment.
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" { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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:") { |
There was a problem hiding this comment.
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]
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modified
type Worker struct { | ||
resultStatus chan string | ||
result []string | ||
testDuration string |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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]
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modified.
There was a problem hiding this 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()}) |
There was a problem hiding this comment.
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})
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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, " ") |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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"?
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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], " ") |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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,
}
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Errorf
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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+`) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0.1
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modified
There was a problem hiding this 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.
There was a problem hiding this 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 { |
There was a problem hiding this comment.
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]
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here - make public
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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...")
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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"
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
... protocol, command string,
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
... protocol, command string,
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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, " "), " "), "-") |
There was a problem hiding this comment.
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, " "), "-")
There was a problem hiding this comment.
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++ { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modified.
There was a problem hiding this 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) { |
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SIEGE_VERSION
/ok-to-test |
7af3fb8
to
3618317
Compare
There was a problem hiding this 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 |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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)
3618317
to
de11670
Compare
/lgtm Thanks |
[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 |
awesome 👏 |
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.