From 64a27884726e4173020143a0d1c642c00c3f6bf1 Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Tue, 5 Mar 2024 15:06:51 +0100 Subject: [PATCH 1/7] worker: improve logging when scanning hosts --- worker/worker.go | 62 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/worker/worker.go b/worker/worker.go index b335a5f6c..79ad1b1b0 100644 --- a/worker/worker.go +++ b/worker/worker.go @@ -9,6 +9,7 @@ import ( "math/big" "net" "net/http" + "os" "runtime" "sort" "strings" @@ -52,6 +53,8 @@ const ( ) var ( + errHostOnPrivateNetwork = errors.New("host is on a private network") + ErrShuttingDown = errors.New("worker is shutting down") ) @@ -259,13 +262,6 @@ func (w *worker) rhpScanHandler(jc jape.Context) { return } - // apply the timeout - if rsr.Timeout > 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, time.Duration(rsr.Timeout)) - defer cancel() - } - // only scan hosts if we are online peers, err := w.bus.SyncerPeers(ctx) if jc.Check("failed to fetch peers from bus", err) != nil { @@ -278,7 +274,7 @@ func (w *worker) rhpScanHandler(jc jape.Context) { // scan host var errStr string - settings, priceTable, elapsed, err := w.scanHost(ctx, rsr.HostKey, rsr.HostIP) + settings, priceTable, elapsed, err := w.scanHost(time.Duration(rsr.Timeout), rsr.HostKey, rsr.HostIP) if err != nil { errStr = err.Error() } @@ -1390,9 +1386,18 @@ func (w *worker) Shutdown(ctx context.Context) error { return nil } -func (w *worker) scanHost(ctx context.Context, hostKey types.PublicKey, hostIP string) (rhpv2.HostSettings, rhpv3.HostPriceTable, time.Duration, error) { +func (w *worker) scanHost(timeout time.Duration, hostKey types.PublicKey, hostIP string) (rhpv2.HostSettings, rhpv3.HostPriceTable, time.Duration, error) { + logger := w.logger.With("host", hostKey).With("hostIP", hostIP).With("timeout", timeout) + // prepare a helper for scanning scan := func() (rhpv2.HostSettings, rhpv3.HostPriceTable, time.Duration, error) { + // apply timeout + ctx := w.shutdownCtx + var cancel context.CancelFunc + if timeout > 0 { + ctx, cancel = context.WithTimeout(w.shutdownCtx, timeout) + defer cancel() + } // resolve hostIP. We don't want to scan hosts on private networks. if !w.allowPrivateIPs { host, _, err := net.SplitHostPort(hostIP) @@ -1405,7 +1410,7 @@ func (w *worker) scanHost(ctx context.Context, hostKey types.PublicKey, hostIP s } for _, addr := range addrs { if isPrivateIP(addr.IP) { - return rhpv2.HostSettings{}, rhpv3.HostPriceTable{}, 0, errors.New("host is on a private network") + return rhpv2.HostSettings{}, rhpv3.HostPriceTable{}, 0, errHostOnPrivateNetwork } } } @@ -1418,8 +1423,9 @@ func (w *worker) scanHost(ctx context.Context, hostKey types.PublicKey, hostIP s // NOTE: we overwrite the NetAddress with the host address here since we // just used it to dial the host we know it's valid settings.NetAddress = hostIP + return nil } - return err + return fmt.Errorf("failed to fetch host settings: %w", err) }) elapsed := time.Since(start) if err != nil { @@ -1430,7 +1436,7 @@ func (w *worker) scanHost(ctx context.Context, hostKey types.PublicKey, hostIP s var pt rhpv3.HostPriceTable err = w.transportPoolV3.withTransportV3(ctx, hostKey, settings.SiamuxAddr(), func(ctx context.Context, t *transportV3) error { if hpt, err := RPCPriceTable(ctx, t, func(pt rhpv3.HostPriceTable) (rhpv3.PaymentMethod, error) { return nil, nil }); err != nil { - return err + return fmt.Errorf("failed to fetch host price table: %w", err) } else { pt = hpt.HostPriceTable return nil @@ -1444,12 +1450,17 @@ func (w *worker) scanHost(ctx context.Context, hostKey types.PublicKey, hostIP s if err != nil { // scan: second try select { - case <-ctx.Done(): + case <-w.shutdownCtx.Done(): + return rhpv2.HostSettings{}, rhpv3.HostPriceTable{}, 0, w.shutdownCtx.Err() case <-time.After(time.Second): } settings, pt, duration, err = scan() + + logger = logger.With("elapsed", duration) if err == nil { - w.logger.Debugf("successfully scanned host %v after retry", hostKey) + logger.Debugf("successfully scanned host on second try") + } else if !isErrHostUnreachable(err) { + logger.Debugw("failed to scan host", zap.Error(err)) } } @@ -1477,7 +1488,7 @@ func (w *worker) scanHost(ctx context.Context, hostKey types.PublicKey, hostIP s }, }) if scanErr != nil { - w.logger.Errorf("failed to record host scan: %v", scanErr) + logger.Errorw("failed to record host scan", zap.Error(scanErr)) } return settings, pt, duration, err } @@ -1494,6 +1505,27 @@ func discardTxnOnErr(ctx context.Context, bus Bus, l *zap.SugaredLogger, txn typ cancel() } +func isErrHostUnreachable(err error) bool { + if isError(err, os.ErrDeadlineExceeded) { + return true + } else if isError(err, context.DeadlineExceeded) { + return true + } else if isError(err, errHostOnPrivateNetwork) { + return true + } else if isError(err, errors.New("no route to host")) { + return true + } else if isError(err, errors.New("no such host")) { + return true + } else if isError(err, errors.New("connection refused")) { + return true + } else if isError(err, errors.New("unknown port")) { + return true + } else if isError(err, errors.New("cannot assign requested address")) { + return true + } + return false +} + func isErrDuplicateTransactionSet(err error) bool { return err != nil && strings.Contains(err.Error(), modules.ErrDuplicateTransactionSet.Error()) } From 7f0cf5ccf82df6734755f939e30b004f4bd8843d Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Wed, 6 Mar 2024 10:36:20 +0100 Subject: [PATCH 2/7] worker: address comments --- api/worker.go | 4 ++++ worker/worker.go | 62 +++++++++++++++++++----------------------------- 2 files changed, 29 insertions(+), 37 deletions(-) diff --git a/api/worker.go b/api/worker.go index 7ee2800f4..39f075718 100644 --- a/api/worker.go +++ b/api/worker.go @@ -19,6 +19,10 @@ var ( // ErrContractSetNotSpecified is returned by the worker API by endpoints that // need a contract set to be able to upload data. ErrContractSetNotSpecified = errors.New("contract set is not specified") + + // ErrHostOnPrivateNetwork is returned by the worker API when a host can't + // be scanned since it is on a private network. + ErrHostOnPrivateNetwork = errors.New("host is on a private network") ) type ( diff --git a/worker/worker.go b/worker/worker.go index 79ad1b1b0..2fe9d334a 100644 --- a/worker/worker.go +++ b/worker/worker.go @@ -53,8 +53,6 @@ const ( ) var ( - errHostOnPrivateNetwork = errors.New("host is on a private network") - ErrShuttingDown = errors.New("worker is shutting down") ) @@ -274,7 +272,7 @@ func (w *worker) rhpScanHandler(jc jape.Context) { // scan host var errStr string - settings, priceTable, elapsed, err := w.scanHost(time.Duration(rsr.Timeout), rsr.HostKey, rsr.HostIP) + settings, priceTable, elapsed, err := w.scanHost(ctx, time.Duration(rsr.Timeout), rsr.HostKey, rsr.HostIP) if err != nil { errStr = err.Error() } @@ -1386,16 +1384,15 @@ func (w *worker) Shutdown(ctx context.Context) error { return nil } -func (w *worker) scanHost(timeout time.Duration, hostKey types.PublicKey, hostIP string) (rhpv2.HostSettings, rhpv3.HostPriceTable, time.Duration, error) { +func (w *worker) scanHost(ctx context.Context, timeout time.Duration, hostKey types.PublicKey, hostIP string) (rhpv2.HostSettings, rhpv3.HostPriceTable, time.Duration, error) { logger := w.logger.With("host", hostKey).With("hostIP", hostIP).With("timeout", timeout) // prepare a helper for scanning scan := func() (rhpv2.HostSettings, rhpv3.HostPriceTable, time.Duration, error) { // apply timeout - ctx := w.shutdownCtx var cancel context.CancelFunc if timeout > 0 { - ctx, cancel = context.WithTimeout(w.shutdownCtx, timeout) + ctx, cancel = context.WithTimeout(ctx, timeout) defer cancel() } // resolve hostIP. We don't want to scan hosts on private networks. @@ -1410,7 +1407,7 @@ func (w *worker) scanHost(timeout time.Duration, hostKey types.PublicKey, hostIP } for _, addr := range addrs { if isPrivateIP(addr.IP) { - return rhpv2.HostSettings{}, rhpv3.HostPriceTable{}, 0, errHostOnPrivateNetwork + return rhpv2.HostSettings{}, rhpv3.HostPriceTable{}, 0, api.ErrHostOnPrivateNetwork } } } @@ -1418,14 +1415,15 @@ func (w *worker) scanHost(timeout time.Duration, hostKey types.PublicKey, hostIP // fetch the host settings start := time.Now() var settings rhpv2.HostSettings - err := w.withTransportV2(ctx, hostKey, hostIP, func(t *rhpv2.Transport) (err error) { - if settings, err = RPCSettings(ctx, t); err == nil { - // NOTE: we overwrite the NetAddress with the host address here since we - // just used it to dial the host we know it's valid - settings.NetAddress = hostIP - return nil + err := w.withTransportV2(ctx, hostKey, hostIP, func(t *rhpv2.Transport) error { + var err error + if settings, err = RPCSettings(ctx, t); err != nil { + return fmt.Errorf("failed to fetch host settings: %w", err) } - return fmt.Errorf("failed to fetch host settings: %w", err) + // NOTE: we overwrite the NetAddress with the host address here + // since we just used it to dial the host we know it's valid + settings.NetAddress = hostIP + return nil }) elapsed := time.Since(start) if err != nil { @@ -1450,15 +1448,15 @@ func (w *worker) scanHost(timeout time.Duration, hostKey types.PublicKey, hostIP if err != nil { // scan: second try select { - case <-w.shutdownCtx.Done(): - return rhpv2.HostSettings{}, rhpv3.HostPriceTable{}, 0, w.shutdownCtx.Err() + case <-ctx.Done(): + return rhpv2.HostSettings{}, rhpv3.HostPriceTable{}, 0, ctx.Err() case <-time.After(time.Second): } settings, pt, duration, err = scan() logger = logger.With("elapsed", duration) if err == nil { - logger.Debugf("successfully scanned host on second try") + logger.Debug("successfully scanned host on second try") } else if !isErrHostUnreachable(err) { logger.Debugw("failed to scan host", zap.Error(err)) } @@ -1468,8 +1466,8 @@ func (w *worker) scanHost(timeout time.Duration, hostKey types.PublicKey, hostIP // just in case since recording a failed scan might have serious // repercussions select { - case <-w.shutdownCtx.Done(): - return rhpv2.HostSettings{}, rhpv3.HostPriceTable{}, 0, w.shutdownCtx.Err() + case <-ctx.Done(): + return rhpv2.HostSettings{}, rhpv3.HostPriceTable{}, 0, ctx.Err() default: } @@ -1506,24 +1504,14 @@ func discardTxnOnErr(ctx context.Context, bus Bus, l *zap.SugaredLogger, txn typ } func isErrHostUnreachable(err error) bool { - if isError(err, os.ErrDeadlineExceeded) { - return true - } else if isError(err, context.DeadlineExceeded) { - return true - } else if isError(err, errHostOnPrivateNetwork) { - return true - } else if isError(err, errors.New("no route to host")) { - return true - } else if isError(err, errors.New("no such host")) { - return true - } else if isError(err, errors.New("connection refused")) { - return true - } else if isError(err, errors.New("unknown port")) { - return true - } else if isError(err, errors.New("cannot assign requested address")) { - return true - } - return false + return isError(err, os.ErrDeadlineExceeded) || + isError(err, context.DeadlineExceeded) || + isError(err, api.ErrHostOnPrivateNetwork) || + isError(err, errors.New("no route to host")) || + isError(err, errors.New("no such host")) || + isError(err, errors.New("connection refused")) || + isError(err, errors.New("unknown port")) || + isError(err, errors.New("cannot assign requested address")) } func isErrDuplicateTransactionSet(err error) bool { From dd8cb8a2ccea8551ac1918377c224f89346f080d Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Wed, 6 Mar 2024 10:47:18 +0100 Subject: [PATCH 3/7] e2e: fix TestBlocklist --- worker/worker.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/worker/worker.go b/worker/worker.go index 2fe9d334a..fcf68387f 100644 --- a/worker/worker.go +++ b/worker/worker.go @@ -1390,9 +1390,10 @@ func (w *worker) scanHost(ctx context.Context, timeout time.Duration, hostKey ty // prepare a helper for scanning scan := func() (rhpv2.HostSettings, rhpv3.HostPriceTable, time.Duration, error) { // apply timeout + scanCtx := ctx var cancel context.CancelFunc if timeout > 0 { - ctx, cancel = context.WithTimeout(ctx, timeout) + scanCtx, cancel = context.WithTimeout(scanCtx, timeout) defer cancel() } // resolve hostIP. We don't want to scan hosts on private networks. @@ -1401,7 +1402,7 @@ func (w *worker) scanHost(ctx context.Context, timeout time.Duration, hostKey ty if err != nil { return rhpv2.HostSettings{}, rhpv3.HostPriceTable{}, 0, err } - addrs, err := (&net.Resolver{}).LookupIPAddr(ctx, host) + addrs, err := (&net.Resolver{}).LookupIPAddr(scanCtx, host) if err != nil { return rhpv2.HostSettings{}, rhpv3.HostPriceTable{}, 0, err } @@ -1415,9 +1416,9 @@ func (w *worker) scanHost(ctx context.Context, timeout time.Duration, hostKey ty // fetch the host settings start := time.Now() var settings rhpv2.HostSettings - err := w.withTransportV2(ctx, hostKey, hostIP, func(t *rhpv2.Transport) error { + err := w.withTransportV2(scanCtx, hostKey, hostIP, func(t *rhpv2.Transport) error { var err error - if settings, err = RPCSettings(ctx, t); err != nil { + if settings, err = RPCSettings(scanCtx, t); err != nil { return fmt.Errorf("failed to fetch host settings: %w", err) } // NOTE: we overwrite the NetAddress with the host address here @@ -1432,7 +1433,7 @@ func (w *worker) scanHost(ctx context.Context, timeout time.Duration, hostKey ty // fetch the host pricetable var pt rhpv3.HostPriceTable - err = w.transportPoolV3.withTransportV3(ctx, hostKey, settings.SiamuxAddr(), func(ctx context.Context, t *transportV3) error { + err = w.transportPoolV3.withTransportV3(scanCtx, hostKey, settings.SiamuxAddr(), func(ctx context.Context, t *transportV3) error { if hpt, err := RPCPriceTable(ctx, t, func(pt rhpv3.HostPriceTable) (rhpv3.PaymentMethod, error) { return nil, nil }); err != nil { return fmt.Errorf("failed to fetch host price table: %w", err) } else { From 1b0bd3bb02c9713174bf0814b7f3eee8499f2bc6 Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Wed, 6 Mar 2024 14:47:04 +0100 Subject: [PATCH 4/7] e2e: TestHostScan --- api/host.go | 2 +- autopilot/contractor.go | 13 +++-- internal/test/e2e/cluster.go | 13 +++-- internal/test/e2e/cluster_test.go | 96 +++++++++++++++++++++++++++++++ worker/worker.go | 1 - 5 files changed, 113 insertions(+), 12 deletions(-) diff --git a/api/host.go b/api/host.go index 0ad52e8ef..aea80a9fe 100644 --- a/api/host.go +++ b/api/host.go @@ -112,6 +112,6 @@ func (opts HostsForScanningOptions) Apply(values url.Values) { values.Set("limit", fmt.Sprint(opts.Limit)) } if !opts.MaxLastScan.IsZero() { - values.Set("maxLastScan", fmt.Sprint(TimeRFC3339(opts.MaxLastScan))) + values.Set("lastScan", fmt.Sprint(TimeRFC3339(opts.MaxLastScan))) } } diff --git a/autopilot/contractor.go b/autopilot/contractor.go index 188b55661..4e5e8c842 100644 --- a/autopilot/contractor.go +++ b/autopilot/contractor.go @@ -752,11 +752,14 @@ func (c *contractor) runContractChecks(ctx context.Context, w Worker, contracts continue } - // if the host doesn't have a valid pricetable, update it - var invalidPT bool - if err := refreshPriceTable(ctx, w, &host.Host); err != nil { - c.logger.Errorf("could not fetch price table for host %v: %v", host.PublicKey, err) - invalidPT = true + // if the host doesn't have a valid pricetable, update it if we were + // able to obtain a revision + invalidPT := contract.Revision == nil + if contract.Revision != nil { + if err := refreshPriceTable(ctx, w, &host.Host); err != nil { + c.logger.Errorf("could not fetch price table for host %v: %v", host.PublicKey, err) + invalidPT = true + } } // refresh the consensus state diff --git a/internal/test/e2e/cluster.go b/internal/test/e2e/cluster.go index b9776d598..16b3acbfd 100644 --- a/internal/test/e2e/cluster.go +++ b/internal/test/e2e/cluster.go @@ -154,6 +154,7 @@ type testClusterOptions struct { logger *zap.Logger uploadPacking bool skipSettingAutopilot bool + skipRunningAutopilot bool walletKey *types.PrivateKey autopilotCfg *node.AutopilotConfig @@ -393,11 +394,13 @@ func newTestCluster(t *testing.T, opts testClusterOptions) *TestCluster { _ = autopilotServer.Serve(autopilotListener) cluster.wg.Done() }() - cluster.wg.Add(1) - go func() { - _ = aStartFn() - cluster.wg.Done() - }() + if !opts.skipRunningAutopilot { + cluster.wg.Add(1) + go func() { + _ = aStartFn() + cluster.wg.Done() + }() + } // Set the test contract set to make sure we can add objects at the // beginning of a test right away. diff --git a/internal/test/e2e/cluster_test.go b/internal/test/e2e/cluster_test.go index c546937eb..f2ef53767 100644 --- a/internal/test/e2e/cluster_test.go +++ b/internal/test/e2e/cluster_test.go @@ -2448,3 +2448,99 @@ func TestMultipartUploadWrappedByPartialSlabs(t *testing.T) { t.Fatal("unexpected data") } } + +func TestHostScan(t *testing.T) { + // New cluster with autopilot disabled + cfg := clusterOptsDefault + cfg.skipRunningAutopilot = true + cluster := newTestCluster(t, cfg) + defer cluster.Shutdown() + + b := cluster.Bus + w := cluster.Worker + tt := cluster.tt + + // Add a host. + hosts := cluster.AddHosts(2) + host := hosts[0] + + settings, err := host.RHPv2Settings() + tt.OK(err) + + hk := host.PublicKey() + hostIP := settings.NetAddress + + assertHost := func(ls time.Time, lss, slss bool, ts uint64) { + t.Helper() + + hi, err := b.Host(context.Background(), host.PublicKey()) + tt.OK(err) + + if ls.IsZero() && !hi.Interactions.LastScan.IsZero() { + t.Fatal("expected last scan to be zero") + } else if !ls.IsZero() && !hi.Interactions.LastScan.After(ls) { + t.Fatal("expected last scan to be after", ls) + } else if hi.Interactions.LastScanSuccess != lss { + t.Fatalf("expected last scan success to be %v, got %v", lss, hi.Interactions.LastScanSuccess) + } else if hi.Interactions.SecondToLastScanSuccess != slss { + t.Fatalf("expected second to last scan success to be %v, got %v", slss, hi.Interactions.SecondToLastScanSuccess) + } else if hi.Interactions.TotalScans != ts { + t.Fatalf("expected total scans to be %v, got %v", ts, hi.Interactions.TotalScans) + } + } + + scanHost := func() error { + resp, err := w.RHPScan(context.Background(), hk, hostIP, 10*time.Second) + tt.OK(err) + if resp.ScanError != "" { + return errors.New(resp.ScanError) + } + return nil + } + + assertHost(time.Time{}, false, false, 0) + + // scan the host the first time + ls := time.Now() + if err := scanHost(); err != nil { + t.Fatal(err) + } + assertHost(ls, true, false, 1) + + // scan the host the second time + ls = time.Now() + if err := scanHost(); err != nil { + t.Fatal(err) + } + assertHost(ls, true, true, 2) + + // close the host to make scans fail + tt.OK(host.Close()) + + // scan the host a third time + ls = time.Now() + if err := scanHost(); err == nil { + t.Fatal("expected scan error") + } + assertHost(ls, false, true, 3) + + // fetch hosts for scanning with maxLastScan set to now which should return + // all hosts + toScan, err := b.HostsForScanning(context.Background(), api.HostsForScanningOptions{ + MaxLastScan: api.TimeRFC3339(time.Now()), + }) + tt.OK(err) + if len(toScan) != 2 { + t.Fatalf("expected 2 hosts, got %v", len(toScan)) + } + + // fetch hosts again with the unix epoch timestamp which should only return + // 1 host since that one hasn't been scanned yet + toScan, err = b.HostsForScanning(context.Background(), api.HostsForScanningOptions{ + MaxLastScan: api.TimeRFC3339(time.Unix(0, 1)), + }) + tt.OK(err) + if len(toScan) != 1 { + t.Fatalf("expected 1 hosts, got %v", len(toScan)) + } +} diff --git a/worker/worker.go b/worker/worker.go index fcf68387f..9f4ef90ed 100644 --- a/worker/worker.go +++ b/worker/worker.go @@ -1386,7 +1386,6 @@ func (w *worker) Shutdown(ctx context.Context) error { func (w *worker) scanHost(ctx context.Context, timeout time.Duration, hostKey types.PublicKey, hostIP string) (rhpv2.HostSettings, rhpv3.HostPriceTable, time.Duration, error) { logger := w.logger.With("host", hostKey).With("hostIP", hostIP).With("timeout", timeout) - // prepare a helper for scanning scan := func() (rhpv2.HostSettings, rhpv3.HostPriceTable, time.Duration, error) { // apply timeout From ee4d925a3055219a5850eadd4c3cdae080a4cd75 Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Wed, 6 Mar 2024 15:19:34 +0100 Subject: [PATCH 5/7] e2e: update comment in TestHostScan --- internal/test/e2e/cluster_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/test/e2e/cluster_test.go b/internal/test/e2e/cluster_test.go index f2ef53767..25426848b 100644 --- a/internal/test/e2e/cluster_test.go +++ b/internal/test/e2e/cluster_test.go @@ -2460,7 +2460,8 @@ func TestHostScan(t *testing.T) { w := cluster.Worker tt := cluster.tt - // Add a host. + // add 2 hosts to the cluster, 1 to scan and 1 to make sure we always have 1 + // peer and consider ourselves connected to the internet hosts := cluster.AddHosts(2) host := hosts[0] From 78769d232dcfaa790662b0af1110b8471875ee44 Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Wed, 6 Mar 2024 15:35:12 +0100 Subject: [PATCH 6/7] e2e: run check in retry --- internal/test/e2e/cluster_test.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/internal/test/e2e/cluster_test.go b/internal/test/e2e/cluster_test.go index 25426848b..9f9699b7d 100644 --- a/internal/test/e2e/cluster_test.go +++ b/internal/test/e2e/cluster_test.go @@ -2527,17 +2527,20 @@ func TestHostScan(t *testing.T) { // fetch hosts for scanning with maxLastScan set to now which should return // all hosts - toScan, err := b.HostsForScanning(context.Background(), api.HostsForScanningOptions{ - MaxLastScan: api.TimeRFC3339(time.Now()), + tt.Retry(100, 100*time.Millisecond, func() error { + toScan, err := b.HostsForScanning(context.Background(), api.HostsForScanningOptions{ + MaxLastScan: api.TimeRFC3339(time.Now()), + }) + tt.OK(err) + if len(toScan) != 2 { + return fmt.Errorf("expected 2 hosts, got %v", len(toScan)) + } + return nil }) - tt.OK(err) - if len(toScan) != 2 { - t.Fatalf("expected 2 hosts, got %v", len(toScan)) - } // fetch hosts again with the unix epoch timestamp which should only return // 1 host since that one hasn't been scanned yet - toScan, err = b.HostsForScanning(context.Background(), api.HostsForScanningOptions{ + toScan, err := b.HostsForScanning(context.Background(), api.HostsForScanningOptions{ MaxLastScan: api.TimeRFC3339(time.Unix(0, 1)), }) tt.OK(err) From e4ab18af4f3f55ff5d32e3e9bbfff02d48a8ed63 Mon Sep 17 00:00:00 2001 From: Chris Schinnerl Date: Wed, 6 Mar 2024 16:00:55 +0100 Subject: [PATCH 7/7] e2e: sleep before scan --- internal/test/e2e/cluster_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/test/e2e/cluster_test.go b/internal/test/e2e/cluster_test.go index 9f9699b7d..4c96cc989 100644 --- a/internal/test/e2e/cluster_test.go +++ b/internal/test/e2e/cluster_test.go @@ -2491,6 +2491,10 @@ func TestHostScan(t *testing.T) { } scanHost := func() error { + // timing on the CI can be weird, wait a bit to make sure time passes + // between scans + time.Sleep(time.Millisecond) + resp, err := w.RHPScan(context.Background(), hk, hostIP, 10*time.Second) tt.OK(err) if resp.ScanError != "" {