diff --git a/docs/content/management.md b/docs/content/management.md index 1428ecfbd6..8ba5439752 100644 --- a/docs/content/management.md +++ b/docs/content/management.md @@ -834,6 +834,8 @@ Status updates contain the following fields: | `bundles[_].metrics` | `object` | Metrics from the last update of the bundle. | | `discovery.name` | `string` | Name of discovery bundle that the OPA instance is configured to download. | | `discovery.active_revision` | `string` | Opaque revision identifier of the last successful discovery activation. | +| `discovery.last_request` | `string` | RFC3339 timestamp of last discovery bundle request. This timestamp should be >= to the successful request timestamp in normal operation. | +| `discovery.last_successful_request` | `string` | RFC3339 timestamp of last successful discovery bundle request. This timestamp should be >= to the successful download timestamp in normal operation. | | `discovery.last_successful_download` | `string` | RFC3339 timestamp of last successful discovery bundle download. | | `discovery.last_successful_activation` | `string` | RFC3339 timestamp of last successful discovery bundle activation. | | `plugins` | `object` | A set of objects describing the state of configured plugins in OPA's runtime. | diff --git a/plugins/discovery/discovery.go b/plugins/discovery/discovery.go index a451a8c249..beb1dc88af 100644 --- a/plugins/discovery/discovery.go +++ b/plugins/discovery/discovery.go @@ -133,6 +133,7 @@ func (c *Discovery) oneShot(ctx context.Context, u download.Update) { } func (c *Discovery) processUpdate(ctx context.Context, u download.Update) { + c.status.SetRequest() if u.Error != nil { c.logError("Discovery download failed: %v", u.Error) @@ -141,8 +142,10 @@ func (c *Discovery) processUpdate(ctx context.Context, u download.Update) { return } + c.status.LastSuccessfulRequest = c.status.LastRequest + if u.Bundle != nil { - c.status.SetDownloadSuccess() + c.status.LastSuccessfulDownload = c.status.LastSuccessfulRequest if err := c.reconfigure(ctx, u); err != nil { c.logError("Discovery reconfiguration error occurred: %v", err) diff --git a/plugins/discovery/discovery_test.go b/plugins/discovery/discovery_test.go index 8d46b6af15..b2a4d03f50 100644 --- a/plugins/discovery/discovery_test.go +++ b/plugins/discovery/discovery_test.go @@ -751,6 +751,78 @@ func TestStatusUpdates(t *testing.T) { } } +func TestStatusUpdatesTimestamp(t *testing.T) { + + ts := testServer{t: t} + ts.Start() + defer ts.Stop() + + manager, err := plugins.New([]byte(fmt.Sprintf(`{ + "labels": {"x": "y"}, + "services": { + "localhost": { + "url": %q + } + }, + "discovery": {"name": "config"}, + }`, ts.server.URL)), "test-id", inmem.New()) + if err != nil { + t.Fatal(err) + } + + disco, err := New(manager) + if err != nil { + t.Fatal(err) + } + + ctx := context.Background() + + // simulate HTTP 200 response from downloader + disco.oneShot(ctx, download.Update{ETag: "etag-1", Bundle: makeDataBundle(1, `{ + "config": { + "status": {} + } + }`)}) + + if disco.status.LastSuccessfulDownload != disco.status.LastSuccessfulRequest || disco.status.LastSuccessfulDownload != disco.status.LastRequest { + t.Fatal("expected last successful request to be same as download and request") + } + + if disco.status.LastSuccessfulActivation.IsZero() { + t.Fatal("expected last successful activation to be non-zero") + } + + time.Sleep(time.Millisecond) + + // simulate HTTP 304 response from downloader + disco.oneShot(ctx, download.Update{ETag: "etag-1", Bundle: nil}) + if disco.status.LastSuccessfulDownload == disco.status.LastSuccessfulRequest || disco.status.LastSuccessfulDownload == disco.status.LastRequest { + t.Fatal("expected last successful download to differ from request and last request") + } + + // simulate HTTP 200 response from downloader + disco.oneShot(ctx, download.Update{ETag: "etag-2", Bundle: makeDataBundle(2, `{ + "config": { + "status": {} + } + }`)}) + + if disco.status.LastSuccessfulDownload != disco.status.LastSuccessfulRequest || disco.status.LastSuccessfulDownload != disco.status.LastRequest { + t.Fatal("expected last successful request to be same as download and request") + } + + if disco.status.LastSuccessfulActivation.IsZero() { + t.Fatal("expected last successful activation to be non-zero") + } + + // simulate error response from downloader + disco.oneShot(ctx, download.Update{Error: fmt.Errorf("unknown error")}) + + if disco.status.LastSuccessfulDownload != disco.status.LastSuccessfulRequest || disco.status.LastSuccessfulDownload == disco.status.LastRequest { + t.Fatal("expected last successful request to be same as download but different from request") + } +} + func makeDataBundle(n int, s string) *bundleApi.Bundle { return &bundleApi.Bundle{ Manifest: bundleApi.Manifest{Revision: fmt.Sprintf("test-revision-%v", n)},