mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-27 10:45:08 -06:00
0b7a2c38af
This commit introduces the idea of manual triggers to trigger plugins. Currently plugins such as discovery, bundle, decision log etc. perform their functions in a timer-based loop. For example, the bundle plugin periodically checks for new bundles by polling a remote server. This change adds the ability to trigger a plugin thereby allowing callers to control when a bundle download happens, when a decision log is uploaded etc. The periodic mode is still the default for the plugins. This feature allows callers to trigger individual plugins. Plugins perform their functions and then report back to the caller when done. Co-authored-by: Torin Sandall <torinsandall@gmail.com> Signed-off-by: Ashutosh Narkar <anarkar4387@gmail.com>
781 lines
18 KiB
Go
781 lines
18 KiB
Go
// Copyright 2018 The OPA Authors. All rights reserved.
|
|
// Use of this source code is governed by an Apache2
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package status
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/open-policy-agent/opa/metrics"
|
|
"github.com/open-policy-agent/opa/plugins"
|
|
"github.com/open-policy-agent/opa/plugins/bundle"
|
|
"github.com/open-policy-agent/opa/storage/inmem"
|
|
"github.com/open-policy-agent/opa/util"
|
|
"github.com/open-policy-agent/opa/version"
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
if version.Version == "" {
|
|
version.Version = "unit-test"
|
|
}
|
|
os.Exit(m.Run())
|
|
}
|
|
|
|
func TestPluginStart(t *testing.T) {
|
|
|
|
fixture := newTestFixture(t, nil)
|
|
fixture.server.ch = make(chan UpdateRequestV1)
|
|
defer fixture.server.stop()
|
|
|
|
ctx := context.Background()
|
|
|
|
err := fixture.plugin.Start(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer fixture.plugin.Stop(ctx)
|
|
|
|
// Start will trigger a status update when the plugin state switches
|
|
// from "not ready" to "ok".
|
|
result := <-fixture.server.ch
|
|
|
|
exp := UpdateRequestV1{
|
|
Labels: map[string]string{
|
|
"id": "test-instance-id",
|
|
"app": "example-app",
|
|
"version": version.Version,
|
|
},
|
|
Plugins: map[string]*plugins.Status{
|
|
"status": {State: plugins.StateOK},
|
|
},
|
|
}
|
|
|
|
if !reflect.DeepEqual(result, exp) {
|
|
t.Fatalf("Expected: %v but got: %v", exp, result)
|
|
}
|
|
|
|
status := testStatus()
|
|
|
|
fixture.plugin.BulkUpdateBundleStatus(map[string]*bundle.Status{"test": status})
|
|
result = <-fixture.server.ch
|
|
|
|
exp.Bundles = map[string]*bundle.Status{"test": status}
|
|
|
|
if !reflect.DeepEqual(result, exp) {
|
|
t.Fatalf("Expected: %v but got: %v", exp, result)
|
|
}
|
|
}
|
|
|
|
func TestPluginStartTriggerManual(t *testing.T) {
|
|
|
|
fixture := newTestFixture(t, nil)
|
|
fixture.server.ch = make(chan UpdateRequestV1)
|
|
defer fixture.server.stop()
|
|
|
|
ctx := context.Background()
|
|
tr := plugins.TriggerManual
|
|
fixture.plugin.config.Trigger = &tr
|
|
|
|
// Start will trigger a status update when the plugin state switches
|
|
// from "not ready" to "ok". This status update will be sent only after a manual trigger
|
|
err := fixture.plugin.Start(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer fixture.plugin.Stop(ctx)
|
|
|
|
// trigger the status update
|
|
go func() {
|
|
_ = fixture.plugin.Trigger(ctx)
|
|
}()
|
|
|
|
result := <-fixture.server.ch
|
|
|
|
exp := UpdateRequestV1{
|
|
Labels: map[string]string{
|
|
"id": "test-instance-id",
|
|
"app": "example-app",
|
|
"version": version.Version,
|
|
},
|
|
Plugins: map[string]*plugins.Status{
|
|
"status": {State: plugins.StateOK},
|
|
},
|
|
}
|
|
|
|
if !reflect.DeepEqual(result, exp) {
|
|
t.Fatalf("Expected: %v but got: %v", exp, result)
|
|
}
|
|
|
|
status := testStatus()
|
|
|
|
fixture.plugin.BulkUpdateBundleStatus(map[string]*bundle.Status{"test": status})
|
|
|
|
// trigger the status update
|
|
go func() {
|
|
_ = fixture.plugin.Trigger(ctx)
|
|
}()
|
|
|
|
result = <-fixture.server.ch
|
|
|
|
exp.Bundles = map[string]*bundle.Status{"test": status}
|
|
|
|
if !reflect.DeepEqual(result, exp) {
|
|
t.Fatalf("Expected: %v but got: %v", exp, result)
|
|
}
|
|
}
|
|
|
|
func TestPluginStartTriggerManualMultiple(t *testing.T) {
|
|
|
|
fixture := newTestFixture(t, nil)
|
|
fixture.server.ch = make(chan UpdateRequestV1)
|
|
defer fixture.server.stop()
|
|
|
|
ctx := context.Background()
|
|
tr := plugins.TriggerManual
|
|
fixture.plugin.config.Trigger = &tr
|
|
|
|
err := fixture.plugin.Start(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer fixture.plugin.Stop(ctx)
|
|
|
|
status := testStatus()
|
|
fixture.plugin.BulkUpdateBundleStatus(map[string]*bundle.Status{"test": status})
|
|
fixture.plugin.UpdateDiscoveryStatus(*status)
|
|
|
|
exp := UpdateRequestV1{
|
|
Labels: map[string]string{
|
|
"id": "test-instance-id",
|
|
"app": "example-app",
|
|
"version": version.Version,
|
|
},
|
|
Plugins: map[string]*plugins.Status{
|
|
"status": {State: plugins.StateOK},
|
|
},
|
|
}
|
|
|
|
// trigger the status update
|
|
go func() {
|
|
_ = fixture.plugin.Trigger(ctx)
|
|
}()
|
|
|
|
result := <-fixture.server.ch
|
|
|
|
exp.Bundles = map[string]*bundle.Status{"test": status}
|
|
exp.Discovery = status
|
|
|
|
if !reflect.DeepEqual(result, exp) {
|
|
t.Fatalf("Expected: %v but got: %v", exp, result)
|
|
}
|
|
}
|
|
|
|
func TestPluginStartTriggerManualWithTimeout(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
|
defer cancel()
|
|
|
|
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
time.Sleep(3 * time.Second) // this should cause the context deadline to exceed
|
|
}))
|
|
|
|
managerConfig := []byte(fmt.Sprintf(`{
|
|
"labels": {
|
|
"app": "example-app"
|
|
},
|
|
"services": [
|
|
{
|
|
"name": "example",
|
|
"url": %q
|
|
}
|
|
]}`, s.URL))
|
|
|
|
manager, err := plugins.New(managerConfig, "test-instance-id", inmem.New())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
pluginConfig := []byte(`{
|
|
"service": "example",
|
|
}`)
|
|
|
|
config, _ := ParseConfig(pluginConfig, manager.Services(), nil)
|
|
tr := plugins.TriggerManual
|
|
config.Trigger = &tr
|
|
|
|
p := New(config, manager)
|
|
|
|
// Start will trigger a status update when the plugin state switches
|
|
// from "not ready" to "ok". This status update will be sent only after a manual trigger
|
|
err = p.Start(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer p.Stop(ctx)
|
|
|
|
// trigger the status update
|
|
done := make(chan struct{})
|
|
go func() {
|
|
// this call should block till the context deadline exceeds
|
|
_ = p.Trigger(ctx)
|
|
close(done)
|
|
}()
|
|
<-done
|
|
|
|
if ctx.Err() == nil {
|
|
t.Fatal("Expected error but got nil")
|
|
}
|
|
|
|
exp := "context deadline exceeded"
|
|
if ctx.Err().Error() != exp {
|
|
t.Fatalf("Expected error %v but got %v", exp, ctx.Err().Error())
|
|
}
|
|
}
|
|
|
|
func TestPluginStartTriggerManualWithError(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
managerConfig := []byte(`{
|
|
"labels": {
|
|
"app": "example-app"
|
|
},
|
|
"services": [
|
|
{
|
|
"name": "example",
|
|
"url": "http://localhost:12345"
|
|
}
|
|
]}`)
|
|
|
|
manager, err := plugins.New(managerConfig, "test-instance-id", inmem.New())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
pluginConfig := []byte(`{
|
|
"service": "example",
|
|
}`)
|
|
|
|
config, _ := ParseConfig(pluginConfig, manager.Services(), nil)
|
|
tr := plugins.TriggerManual
|
|
config.Trigger = &tr
|
|
|
|
p := New(config, manager)
|
|
|
|
// Start will trigger a status update when the plugin state switches
|
|
// from "not ready" to "ok". This status update will be sent only after a manual trigger
|
|
err = p.Start(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer p.Stop(ctx)
|
|
|
|
// trigger the status update
|
|
// this call should result in an error from the bad service config
|
|
err = p.Trigger(ctx)
|
|
if err == nil {
|
|
t.Fatal("Expected error but got nil")
|
|
}
|
|
|
|
exp := "connection refused"
|
|
if !strings.Contains(err.Error(), exp) {
|
|
t.Fatalf("Unexpected error message %v", err.Error())
|
|
}
|
|
}
|
|
|
|
func TestPluginStartBulkUpdate(t *testing.T) {
|
|
|
|
fixture := newTestFixture(t, nil)
|
|
fixture.server.ch = make(chan UpdateRequestV1)
|
|
defer fixture.server.stop()
|
|
|
|
ctx := context.Background()
|
|
|
|
err := fixture.plugin.Start(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer fixture.plugin.Stop(ctx)
|
|
|
|
// Start will trigger a status update when the plugin state switches
|
|
// from "not ready" to "ok".
|
|
<-fixture.server.ch // Discard first request.
|
|
|
|
exp := UpdateRequestV1{
|
|
Labels: map[string]string{
|
|
"id": "test-instance-id",
|
|
"app": "example-app",
|
|
"version": version.Version,
|
|
},
|
|
Plugins: map[string]*plugins.Status{
|
|
"status": {State: plugins.StateOK},
|
|
},
|
|
}
|
|
|
|
status := testStatus()
|
|
|
|
fixture.plugin.BulkUpdateBundleStatus(map[string]*bundle.Status{status.Name: status})
|
|
result := <-fixture.server.ch
|
|
|
|
exp.Bundles = map[string]*bundle.Status{status.Name: status}
|
|
|
|
if !reflect.DeepEqual(result, exp) {
|
|
t.Fatalf("Expected: %v but got: %v", exp, result)
|
|
}
|
|
}
|
|
|
|
func TestPluginStartBulkUpdateMultiple(t *testing.T) {
|
|
|
|
fixture := newTestFixture(t, nil)
|
|
fixture.server.ch = make(chan UpdateRequestV1)
|
|
defer fixture.server.stop()
|
|
|
|
ctx := context.Background()
|
|
|
|
err := fixture.plugin.Start(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer fixture.plugin.Stop(ctx)
|
|
|
|
// Ignore the plugin updating its status (tested elsewhere)
|
|
<-fixture.server.ch
|
|
|
|
statuses := map[string]*bundle.Status{}
|
|
tDownload, _ := time.Parse(time.RFC3339Nano, "2018-01-01T00:00:00.0000000Z")
|
|
tActivate, _ := time.Parse(time.RFC3339Nano, "2018-01-01T00:00:01.0000000Z")
|
|
for i := 0; i < 20; i++ {
|
|
name := fmt.Sprintf("test-bundle-%d", i)
|
|
statuses[name] = &bundle.Status{
|
|
Name: name,
|
|
ActiveRevision: fmt.Sprintf("v%d", i),
|
|
LastSuccessfulDownload: tDownload,
|
|
LastSuccessfulActivation: tActivate,
|
|
}
|
|
}
|
|
|
|
fixture.plugin.BulkUpdateBundleStatus(statuses)
|
|
result := <-fixture.server.ch
|
|
|
|
expLabels := map[string]string{
|
|
"id": "test-instance-id",
|
|
"app": "example-app",
|
|
"version": version.Version,
|
|
}
|
|
|
|
if !reflect.DeepEqual(result.Labels, expLabels) {
|
|
t.Fatalf("Unexpected status labels: %+v", result.Labels)
|
|
}
|
|
|
|
if len(result.Bundles) != len(statuses) {
|
|
t.Fatalf("Expected %d statuses, got %d", len(statuses), len(result.Bundles))
|
|
}
|
|
|
|
for name, s := range statuses {
|
|
actualStatus := result.Bundles[name]
|
|
if actualStatus.Name != s.Name ||
|
|
actualStatus.LastSuccessfulActivation != s.LastSuccessfulActivation ||
|
|
actualStatus.LastSuccessfulDownload != s.LastSuccessfulDownload ||
|
|
actualStatus.ActiveRevision != s.ActiveRevision {
|
|
t.Errorf("Bundle %s has unexpected status:\n\n %v\n\nExpected:\n%v\n\n", name, actualStatus, s)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPluginStartDiscovery(t *testing.T) {
|
|
|
|
fixture := newTestFixture(t, nil)
|
|
fixture.server.ch = make(chan UpdateRequestV1)
|
|
defer fixture.server.stop()
|
|
|
|
ctx := context.Background()
|
|
|
|
err := fixture.plugin.Start(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer fixture.plugin.Stop(ctx)
|
|
|
|
// Ignore the plugin updating its status (tested elsewhere)
|
|
<-fixture.server.ch
|
|
|
|
status := testStatus()
|
|
|
|
fixture.plugin.UpdateDiscoveryStatus(*status)
|
|
result := <-fixture.server.ch
|
|
|
|
exp := UpdateRequestV1{
|
|
Labels: map[string]string{
|
|
"id": "test-instance-id",
|
|
"app": "example-app",
|
|
"version": version.Version,
|
|
},
|
|
Discovery: status,
|
|
Plugins: map[string]*plugins.Status{
|
|
"status": {State: plugins.StateOK},
|
|
},
|
|
}
|
|
|
|
if !reflect.DeepEqual(result, exp) {
|
|
t.Fatalf("Expected: %+v but got: %+v", exp, result)
|
|
}
|
|
}
|
|
|
|
func TestPluginBadAuth(t *testing.T) {
|
|
fixture := newTestFixture(t, nil)
|
|
ctx := context.Background()
|
|
fixture.server.expCode = 401
|
|
defer fixture.server.stop()
|
|
fixture.plugin.lastBundleStatuses = map[string]*bundle.Status{}
|
|
err := fixture.plugin.oneShot(ctx)
|
|
if err == nil {
|
|
t.Fatal("Expected error")
|
|
}
|
|
}
|
|
|
|
func TestPluginBadPath(t *testing.T) {
|
|
fixture := newTestFixture(t, nil)
|
|
ctx := context.Background()
|
|
fixture.server.expCode = 404
|
|
defer fixture.server.stop()
|
|
fixture.plugin.lastBundleStatuses = map[string]*bundle.Status{}
|
|
err := fixture.plugin.oneShot(ctx)
|
|
if err == nil {
|
|
t.Fatal("Expected error")
|
|
}
|
|
}
|
|
|
|
func TestPluginBadStatus(t *testing.T) {
|
|
fixture := newTestFixture(t, nil)
|
|
ctx := context.Background()
|
|
fixture.server.expCode = 500
|
|
defer fixture.server.stop()
|
|
fixture.plugin.lastBundleStatuses = map[string]*bundle.Status{}
|
|
err := fixture.plugin.oneShot(ctx)
|
|
if err == nil {
|
|
t.Fatal("Expected error")
|
|
}
|
|
}
|
|
|
|
func TestPluginReconfigure(t *testing.T) {
|
|
ctx := context.Background()
|
|
fixture := newTestFixture(t, nil)
|
|
defer fixture.server.stop()
|
|
|
|
if err := fixture.plugin.Start(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
pluginConfig := []byte(`{
|
|
"service": "example",
|
|
"partition_name": "test"
|
|
}`)
|
|
|
|
config, _ := ParseConfig(pluginConfig, fixture.manager.Services(), nil)
|
|
|
|
fixture.plugin.Reconfigure(ctx, config)
|
|
fixture.plugin.Stop(ctx)
|
|
|
|
if fixture.plugin.config.PartitionName != "test" {
|
|
t.Fatalf("Expected partition name: test but got %v", fixture.plugin.config.PartitionName)
|
|
}
|
|
}
|
|
|
|
func TestMetrics(t *testing.T) {
|
|
fixture := newTestFixture(t, metrics.New())
|
|
fixture.server.ch = make(chan UpdateRequestV1)
|
|
defer fixture.server.stop()
|
|
|
|
ctx := context.Background()
|
|
|
|
err := fixture.plugin.Start(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer fixture.plugin.Stop(ctx)
|
|
|
|
// Ignore the plugin updating its status (tested elsewhere)
|
|
<-fixture.server.ch
|
|
|
|
status := testStatus()
|
|
|
|
fixture.plugin.BulkUpdateBundleStatus(map[string]*bundle.Status{"bundle": status})
|
|
result := <-fixture.server.ch
|
|
|
|
exp := map[string]interface{}{"<built-in>": map[string]interface{}{}}
|
|
|
|
if !reflect.DeepEqual(result.Metrics, exp) {
|
|
t.Fatalf("Expected %v but got %v", exp, result.Metrics)
|
|
}
|
|
}
|
|
|
|
func TestParseConfigUseDefaultServiceNoConsole(t *testing.T) {
|
|
services := []string{
|
|
"s0",
|
|
"s1",
|
|
"s3",
|
|
}
|
|
|
|
loggerConfig := []byte(`{
|
|
"console": false
|
|
}`)
|
|
|
|
config, err := ParseConfig(loggerConfig, services, nil)
|
|
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if config.Service != services[0] {
|
|
t.Errorf("Expected %s service in config, actual = '%s'", services[0], config.Service)
|
|
}
|
|
}
|
|
|
|
func TestParseConfigDefaultServiceWithConsole(t *testing.T) {
|
|
services := []string{
|
|
"s0",
|
|
"s1",
|
|
"s3",
|
|
}
|
|
|
|
loggerConfig := []byte(`{
|
|
"console": true
|
|
}`)
|
|
|
|
config, err := ParseConfig(loggerConfig, services, nil)
|
|
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if config.Service != "" {
|
|
t.Errorf("Expected no service in config, actual = '%s'", config.Service)
|
|
}
|
|
}
|
|
|
|
func TestParseConfigDefaultServiceWithNoServiceOrConsole(t *testing.T) {
|
|
loggerConfig := []byte(`{}`)
|
|
|
|
_, err := ParseConfig(loggerConfig, []string{}, nil)
|
|
|
|
if err == nil {
|
|
t.Error("Expected an error but err==nil")
|
|
}
|
|
}
|
|
|
|
func TestParseConfigTriggerMode(t *testing.T) {
|
|
cases := []struct {
|
|
note string
|
|
config []byte
|
|
expected plugins.TriggerMode
|
|
wantErr bool
|
|
err error
|
|
}{
|
|
{
|
|
note: "default trigger mode",
|
|
config: []byte(`{}`),
|
|
expected: plugins.DefaultTriggerMode,
|
|
},
|
|
{
|
|
note: "manual trigger mode",
|
|
config: []byte(`{"trigger": "manual"}`),
|
|
expected: plugins.TriggerManual,
|
|
},
|
|
{
|
|
note: "trigger mode mismatch",
|
|
config: []byte(`{"trigger": "manual"}`),
|
|
expected: plugins.TriggerPeriodic,
|
|
wantErr: true,
|
|
err: fmt.Errorf("invalid status config, discovery has trigger mode periodic, status has manual"),
|
|
},
|
|
{
|
|
note: "bad trigger mode",
|
|
config: []byte(`{"trigger": "foo"}`),
|
|
expected: "foo",
|
|
wantErr: true,
|
|
err: fmt.Errorf("invalid trigger mode \"foo\" (want \"periodic\" or \"manual\")"),
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.note, func(t *testing.T) {
|
|
|
|
c, err := NewConfigBuilder().WithBytes(tc.config).WithServices([]string{"s0"}).WithTriggerMode(&tc.expected).Parse()
|
|
|
|
if tc.wantErr {
|
|
if err == nil {
|
|
t.Fatal("Expected error but got nil")
|
|
}
|
|
|
|
if tc.err != nil && tc.err.Error() != err.Error() {
|
|
t.Fatalf("Expected error message %v but got %v", tc.err.Error(), err.Error())
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error %v", err)
|
|
}
|
|
|
|
if *c.Trigger != tc.expected {
|
|
t.Fatalf("Expected trigger mode %v but got %v", tc.expected, *c.Trigger)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type testFixture struct {
|
|
manager *plugins.Manager
|
|
plugin *Plugin
|
|
server *testServer
|
|
}
|
|
|
|
type testPluginCustomizer func(c *Config)
|
|
|
|
func newTestFixture(t *testing.T, m metrics.Metrics, options ...testPluginCustomizer) testFixture {
|
|
|
|
ts := testServer{
|
|
t: t,
|
|
expCode: 200,
|
|
}
|
|
|
|
ts.start()
|
|
|
|
managerConfig := []byte(fmt.Sprintf(`{
|
|
"labels": {
|
|
"app": "example-app"
|
|
},
|
|
"services": [
|
|
{
|
|
"name": "example",
|
|
"url": %q,
|
|
"credentials": {
|
|
"bearer": {
|
|
"scheme": "Bearer",
|
|
"token": "secret"
|
|
}
|
|
}
|
|
}
|
|
]}`, ts.server.URL))
|
|
|
|
manager, err := plugins.New(managerConfig, "test-instance-id", inmem.New())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
pluginConfig := []byte(`{
|
|
"service": "example",
|
|
}`)
|
|
|
|
config, _ := ParseConfig(pluginConfig, manager.Services(), nil)
|
|
for _, option := range options {
|
|
option(config)
|
|
}
|
|
|
|
p := New(config, manager).WithMetrics(m)
|
|
|
|
return testFixture{
|
|
manager: manager,
|
|
plugin: p,
|
|
server: &ts,
|
|
}
|
|
|
|
}
|
|
|
|
type testServer struct {
|
|
t *testing.T
|
|
expCode int
|
|
server *httptest.Server
|
|
ch chan UpdateRequestV1
|
|
}
|
|
|
|
func (t *testServer) handle(w http.ResponseWriter, r *http.Request) {
|
|
|
|
status := UpdateRequestV1{}
|
|
|
|
if err := util.NewJSONDecoder(r.Body).Decode(&status); err != nil {
|
|
t.t.Fatal(err)
|
|
}
|
|
|
|
if t.ch != nil {
|
|
t.ch <- status
|
|
}
|
|
|
|
w.WriteHeader(t.expCode)
|
|
}
|
|
|
|
func (t *testServer) start() {
|
|
t.server = httptest.NewServer(http.HandlerFunc(t.handle))
|
|
}
|
|
|
|
func (t *testServer) stop() {
|
|
t.server.Close()
|
|
}
|
|
|
|
func testStatus() *bundle.Status {
|
|
|
|
tDownload, _ := time.Parse(time.RFC3339Nano, "2018-01-01T00:00:00.0000000Z")
|
|
tActivate, _ := time.Parse(time.RFC3339Nano, "2018-01-01T00:00:01.0000000Z")
|
|
|
|
status := bundle.Status{
|
|
Name: "example/authz",
|
|
ActiveRevision: "quickbrawnfaux",
|
|
LastSuccessfulDownload: tDownload,
|
|
LastSuccessfulActivation: tActivate,
|
|
}
|
|
|
|
return &status
|
|
}
|
|
|
|
type testPlugin struct {
|
|
reqs []UpdateRequestV1
|
|
}
|
|
|
|
func (*testPlugin) Start(context.Context) error {
|
|
return nil
|
|
}
|
|
|
|
func (p *testPlugin) Stop(context.Context) {
|
|
}
|
|
|
|
func (p *testPlugin) Reconfigure(context.Context, interface{}) {
|
|
}
|
|
|
|
func (p *testPlugin) Log(_ context.Context, req *UpdateRequestV1) error {
|
|
p.reqs = append(p.reqs, *req)
|
|
return nil
|
|
}
|
|
|
|
func TestPluginCustomBackend(t *testing.T) {
|
|
ctx := context.Background()
|
|
manager, _ := plugins.New(nil, "test-instance-id", inmem.New())
|
|
|
|
backend := &testPlugin{}
|
|
manager.Register("test_plugin", backend)
|
|
|
|
config, err := ParseConfig([]byte(`{"plugin": "test_plugin"}`), nil, []string{"test_plugin"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
plugin := New(config, manager)
|
|
err = plugin.oneShot(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = plugin.oneShot(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(backend.reqs) != 2 {
|
|
t.Fatalf("Unexpected number of reqs: expected 2, got %d: %v", len(backend.reqs), backend.reqs)
|
|
}
|
|
}
|