diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2172355 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +.* +Dockerfile +LICENSE* +NOTICE* +README* +iperf3_windows.go diff --git a/Dockerfile b/Dockerfile index 433caf4..16b7cd7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,18 @@ +FROM golang:alpine AS build-env + +# Download dependencies +WORKDIR /go/src +COPY go.mod go.sum ./ +RUN go mod download + +# Build current sources +COPY . . +RUN go build -o /go/bin/iperf3_exporter + +# Prepare runtime environment FROM alpine:latest LABEL maintainer="Edgard Castro " - +EXPOSE 9579 RUN apk add --no-cache iperf3 -COPY iperf3_exporter /bin/iperf3_exporter - +COPY --from=build-env /go/bin/iperf3_exporter /bin/ ENTRYPOINT ["/bin/iperf3_exporter"] -EXPOSE 9579 diff --git a/iperf3_exporter.go b/iperf3_exporter.go index 797f8cd..ec4e1c0 100644 --- a/iperf3_exporter.go +++ b/iperf3_exporter.go @@ -64,10 +64,11 @@ type iperfResult struct { // the prometheus metrics package. type Exporter struct { target string - port int + port int period time.Duration timeout time.Duration mutex sync.RWMutex + reverse bool success *prometheus.Desc sentSeconds *prometheus.Desc @@ -77,11 +78,12 @@ type Exporter struct { } // NewExporter returns an initialized Exporter. -func NewExporter(target string, port int, period time.Duration, timeout time.Duration) *Exporter { +func NewExporter(target string, port int, period time.Duration, timeout time.Duration, reverse bool) *Exporter { return &Exporter{ target: target, port: port, period: period, + reverse: reverse, timeout: timeout, success: prometheus.NewDesc(prometheus.BuildFQName(namespace, "", "success"), "Was the last iperf3 probe successful.", nil, nil), sentSeconds: prometheus.NewDesc(prometheus.BuildFQName(namespace, "", "sent_seconds"), "Total seconds spent sending packets.", nil, nil), @@ -110,7 +112,12 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) { ctx, cancel := context.WithTimeout(context.Background(), e.timeout) defer cancel() - out, err := exec.CommandContext(ctx, iperfCmd, "-J", "-t", strconv.FormatFloat(e.period.Seconds(), 'f', 0, 64), "-c", e.target, "-p", strconv.Itoa(e.port)).Output() + var iperfArgs []string + iperfArgs = []string{"-J", "-t", strconv.FormatFloat(e.period.Seconds(), 'f', 0, 64), "-c", e.target, "-p", strconv.Itoa(e.port)} + if e.reverse{ + iperfArgs = append(iperfArgs, "-R") + } + out, err := exec.CommandContext(ctx, iperfCmd, iperfArgs...).Output() if err != nil { ch <- prometheus.MustNewConstMetric(e.success, prometheus.GaugeValue, 0) iperfErrors.Inc() @@ -140,22 +147,36 @@ func handler(w http.ResponseWriter, r *http.Request) { iperfErrors.Inc() return } - + var targetPort int port := r.URL.Query().Get("port") if port != "" { - var err error + var err error targetPort, err = strconv.Atoi(port) if err != nil { http.Error(w, fmt.Sprintf("'port' parameter must be an integer: %s", err), http.StatusBadRequest) iperfErrors.Inc() return } - } + } if targetPort == 0 { targetPort = 5201 } - + + var reverseMode bool + reverse_mode := r.URL.Query().Get("reverse_mode") + if reverse_mode != "" { + var err error + reverseMode, err = strconv.ParseBool(reverse_mode) + if err != nil { + http.Error(w, fmt.Sprintf("'reverse_mode' parameter must be true or false (boolean): %s", err), http.StatusBadRequest) + iperfErrors.Inc() + return + } + } else { + reverseMode = false + } + var runPeriod time.Duration period := r.URL.Query().Get("period") if period != "" { @@ -198,7 +219,7 @@ func handler(w http.ResponseWriter, r *http.Request) { start := time.Now() registry := prometheus.NewRegistry() - exporter := NewExporter(target, targetPort, runPeriod, runTimeout) + exporter := NewExporter(target, targetPort, runPeriod, runTimeout, reverseMode) registry.MustRegister(exporter) // Delegate http serving to Prometheus client library, which will call collector.Collect.