Пример #1
0
func annotated(start time.Time, typ string, fn func() (common.MapStr, []TaskRunner, error)) TaskRunner {
	return MakeCont(func() (common.MapStr, []TaskRunner, error) {
		event, cont, err := fn()
		if err != nil {
			if event == nil {
				event = common.MapStr{}
			}
			event["error"] = look.Reason(err)
		}

		if event != nil {
			event.Update(common.MapStr{
				"@timestamp": look.Timestamp(start),
				"duration":   look.RTT(time.Now().Sub(start)),
				"type":       typ,
				"up":         err == nil,
			})
		}

		for i := range cont {
			if fcont, ok := cont[i].(funcTask); ok {
				cont[i] = fcont.annotated(start, typ)
			} else {
				cont[i] = annotated(start, typ, cont[i].Run)
			}
		}
		return event, cont, nil
	})
}
Пример #2
0
func createPingFactory(
	config *Config,
	hostname string,
	port uint16,
	tls *transport.TLSConfig,
	request *http.Request,
	body []byte,
	validator RespCheck,
) func(*net.IPAddr) monitors.TaskRunner {
	fields := common.MapStr{
		"scheme": request.URL.Scheme,
		"port":   port,
		"url":    request.URL.String(),
	}

	timeout := config.Timeout
	isTLS := request.URL.Scheme == "https"
	checkRedirect := makeCheckRedirect(config.MaxRedirects)

	return monitors.MakePingIPFactory(fields, func(ip *net.IPAddr) (common.MapStr, error) {
		addr := net.JoinHostPort(ip.String(), strconv.Itoa(int(port)))
		d := &dialchain.DialerChain{
			Net: dialchain.ConstAddrDialer("tcp_connect_rtt", addr, timeout),
		}
		if isTLS {
			d.AddLayer(dialchain.TLSLayer("tls_handshake_rtt", tls, timeout))
		}

		measures := common.MapStr{}
		dialer, err := d.BuildWithMeasures(measures)
		if err != nil {
			return nil, err
		}

		var httpStart, httpEnd time.Time

		client := &http.Client{
			CheckRedirect: checkRedirect,
			Timeout:       timeout,
			Transport: &SimpleTransport{
				Dialer:       dialer,
				OnStartWrite: func() { httpStart = time.Now() },
				OnStartRead:  func() { httpEnd = time.Now() },
			},
		}

		event, err := execPing(client, request, body, timeout, validator)
		if event == nil {
			event = measures
		} else {
			event.Update(measures)
		}

		if !httpEnd.IsZero() {
			event["http_rtt"] = look.RTT(httpEnd.Sub(httpStart))
		}
		return event, err
	})
}
Пример #3
0
func createPingIPFactory(config *Config) func(*net.IPAddr) (common.MapStr, error) {
	return func(ip *net.IPAddr) (common.MapStr, error) {
		rtt, _, err := loop.ping(ip, config.Timeout, config.Wait)
		if err != nil {
			return nil, err
		}

		return common.MapStr{
			"icmp_rtt": look.RTT(rtt),
		}, nil
	}
}
Пример #4
0
func withStart(field string, start time.Time, r TaskRunner) TaskRunner {
	return MakeCont(func() (common.MapStr, []TaskRunner, error) {
		event, cont, err := r.Run()
		if event != nil {
			event[field] = look.RTT(time.Now().Sub(start))
		}

		for i := range cont {
			cont[i] = withStart(field, start, cont[i])
		}
		return event, cont, err
	})
}
Пример #5
0
func pingHost(
	dialer transport.Dialer,
	host string,
	timeout time.Duration,
	validator ConnCheck,
) (common.MapStr, reason.Reason) {
	start := time.Now()
	deadline := start.Add(timeout)

	conn, err := dialer.Dial("tcp", host)
	if err != nil {
		debugf("dial failed with: %v", err)
		return nil, reason.IOFailed(err)
	}
	defer conn.Close()
	if validator == nil {
		// no additional validation step => ping success
		return common.MapStr{}, nil
	}

	if err := conn.SetDeadline(deadline); err != nil {
		debugf("setting connection deadline failed with: %v", err)
		return nil, reason.IOFailed(err)
	}

	validateStart := time.Now()
	err = validator.Validate(conn)
	if err != nil && err != errRecvMismatch {
		debugf("check failed with: %v", err)
		return nil, reason.IOFailed(err)
	}

	end := time.Now()
	event := common.MapStr{
		"validate_rtt": look.RTT(end.Sub(validateStart)),
	}
	if err != nil {
		event["error"] = reason.FailValidate(err)
	}
	return event, nil
}
Пример #6
0
func execPing(
	client *http.Client,
	req *http.Request,
	body []byte,
	timeout time.Duration,
	validator func(*http.Response) error,
) (common.MapStr, reason.Reason) {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	req = req.WithContext(ctx)
	if len(body) > 0 {
		req.Body = ioutil.NopCloser(bytes.NewBuffer(body))
		req.ContentLength = int64(len(body))
	}

	start := time.Now()
	resp, err := client.Do(req)
	end := time.Now()
	if err != nil {
		return nil, reason.IOFailed(err)
	}
	defer resp.Body.Close()

	if err := validator(resp); err != nil {
		return nil, reason.ValidateFailed(err)
	}

	rtt := end.Sub(start)
	event := common.MapStr{
		"response": common.MapStr{
			"status": resp.StatusCode,
		},
		"rtt": look.RTT(rtt),
	}
	return event, nil
}
Пример #7
0
func MakeByHostJob(
	name, typ string,
	host string,
	settings IPSettings,
	pingFactory func(ip *net.IPAddr) TaskRunner,
) (Job, error) {
	network := settings.Network()
	if network == "" {
		return nil, errors.New("pinging hosts requires ipv4 or ipv6 mode enabled")
	}

	mode := settings.Mode
	if mode == PingAny {
		return MakeJob(name, typ, func() (common.MapStr, []TaskRunner, error) {
			event := common.MapStr{"host": host}

			dnsStart := time.Now()
			ip, err := net.ResolveIPAddr(network, host)
			if err != nil {
				return event, nil, err
			}

			dnsEnd := time.Now()
			dnsRTT := dnsEnd.Sub(dnsStart)
			event["resolve_rtt"] = look.RTT(dnsRTT)
			event["ip"] = ip.String()

			return WithFields(event, pingFactory(ip)).Run()
		}), nil
	}

	filter := makeIPFilter(network)
	return MakeJob(name, typ, func() (common.MapStr, []TaskRunner, error) {
		event := common.MapStr{"host": host}

		// TODO: check for better DNS IP lookup support:
		//         - The net.LookupIP drops ipv6 zone index
		//
		dnsStart := time.Now()
		ips, err := net.LookupIP(host)
		if err != nil {
			return event, nil, err
		}

		dnsEnd := time.Now()
		dnsRTT := dnsEnd.Sub(dnsStart)

		event["resolve_rtt"] = look.RTT(dnsRTT)
		if filter != nil {
			ips = filterIPs(ips, filter)
		}

		if len(ips) == 0 {
			err := fmt.Errorf("no %v address resolvable for host %v", network, host)
			return event, nil, err
		}

		// create ip ping tasks
		cont := make([]TaskRunner, len(ips))
		for i, ip := range ips {
			addr := &net.IPAddr{IP: ip}
			fields := event.Clone()
			fields["ip"] = ip.String()
			cont[i] = WithFields(fields, pingFactory(addr))
		}
		return nil, cont, nil
	}), nil
}
Пример #8
0
func measureEventRTT(event common.MapStr, name string) DialLayerCallback {
	return &MeasureLayerRTTCB{Callback: func(start, end time.Time) {
		event[name] = look.RTT(end.Sub(start))
	}}
}