...

Source file src/github.com/datawire/ambassador/v2/cmd/entrypoint/consul_test.go

Documentation: github.com/datawire/ambassador/v2/cmd/entrypoint

     1  package entrypoint
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	amb "github.com/datawire/ambassador/v2/pkg/api/getambassador.io/v3alpha1"
    12  	"github.com/datawire/ambassador/v2/pkg/consulwatch"
    13  	"github.com/datawire/ambassador/v2/pkg/kates"
    14  	snapshotTypes "github.com/datawire/ambassador/v2/pkg/snapshot/v1"
    15  	"github.com/datawire/dlib/dgroup"
    16  	"github.com/datawire/dlib/dlog"
    17  )
    18  
    19  const manifests = `
    20  ---
    21  apiVersion: getambassador.io/v3alpha1
    22  kind: ConsulResolver
    23  metadata:
    24    name: consultest-resolver
    25  spec:
    26    ambassador_id:
    27     - consultest
    28    address: consultest-consul:8500
    29    datacenter: dc1
    30  ---
    31  apiVersion: getambassador.io/v3alpha1
    32  kind: Mapping
    33  name:  consultest_k8s_mapping
    34  prefix: /consultest_k8s/
    35  service: consultest-http-k8s
    36  ---
    37  apiVersion: getambassador.io/v3alpha1
    38  kind: TCPMapping
    39  name:  consultest_k8s_mapping_tcp
    40  port: 3099
    41  service: consultest-http-k8s
    42  ---
    43  apiVersion: getambassador.io/v2
    44  kind: KubernetesServiceResolver
    45  name: kubernetes-service
    46  ---
    47  apiVersion: getambassador.io/v2
    48  kind: KubernetesEndpointResolver
    49  name: endpoint
    50  ---
    51  apiVersion: getambassador.io/v3alpha1
    52  kind: Mapping
    53  name:  consultest_consul_mapping
    54  prefix: /consultest_consul/
    55  service: consultest-consul-service
    56  # tls: consultest-client-context # this doesn't seem to work... ambassador complains with "no private key in secret ..."
    57  resolver: consultest-resolver
    58  load_balancer:
    59    policy: round_robin
    60  ---
    61  apiVersion: getambassador.io/v3alpha1
    62  kind: TCPMapping
    63  name:  consultest_consul_mapping_tcp
    64  port: 3090
    65  service: consultest-consul-service-tcp
    66  resolver: consultest-resolver
    67  ---
    68  apiVersion: getambassador.io/v3alpha1
    69  kind:  TLSContext
    70  name:  consultest-client-context
    71  secret: consultest-client-cert-secret
    72  `
    73  
    74  func TestReconcile(t *testing.T) {
    75  	ctx, resolvers, mappings, c, tw := setup(t)
    76  	require.NoError(t, c.reconcile(ctx, resolvers, mappings))
    77  	tw.Assert(
    78  		"consultest-resolver.default:consultest-consul-service:watch",
    79  		"consultest-resolver.default:consultest-consul-service-tcp:watch",
    80  	)
    81  	extra := consulMapping{
    82  		Service:  "foo",
    83  		Resolver: "consultest-resolver",
    84  	}
    85  	require.NoError(t, c.reconcile(ctx, resolvers, append(mappings, extra)))
    86  	tw.Assert(
    87  		"consultest-resolver.default:foo:watch",
    88  	)
    89  	require.NoError(t, c.reconcile(ctx, resolvers, nil))
    90  	tw.Assert(
    91  		"consultest-resolver.default:consultest-consul-service-tcp:stop",
    92  		"consultest-resolver.default:consultest-consul-service:stop",
    93  		"consultest-resolver.default:foo:stop",
    94  	)
    95  }
    96  
    97  func TestCleanup(t *testing.T) {
    98  	ctx, resolvers, mappings, c, tw := setup(t)
    99  	require.NoError(t, c.reconcile(ctx, resolvers, mappings))
   100  	tw.Assert(
   101  		"consultest-resolver.default:consultest-consul-service:watch",
   102  		"consultest-resolver.default:consultest-consul-service-tcp:watch",
   103  	)
   104  	require.NoError(t, c.cleanup(ctx))
   105  	tw.Assert(
   106  		"consultest-resolver.default:consultest-consul-service:stop",
   107  		"consultest-resolver.default:consultest-consul-service-tcp:stop",
   108  	)
   109  }
   110  
   111  func TestBootstrap(t *testing.T) {
   112  	ctx, resolvers, mappings, c, _ := setup(t)
   113  	assert.False(t, c.isBootstrapped())
   114  	require.NoError(t, c.reconcile(ctx, resolvers, mappings))
   115  	assert.False(t, c.isBootstrapped())
   116  	// XXX: break this (maybe use a chan to replace uncoalesced dirties and passing con around?)
   117  	//
   118  	// In order for consul to be considered bootstrapped, both the service referenced by
   119  	// a Mapping and the one refereced by a TCPMapping should have Endpoints{
   120  	c.endpoints["consultest-consul-service"] = consulwatch.Endpoints{}
   121  	c.endpoints["consultest-consul-service-tcp"] = consulwatch.Endpoints{}
   122  	assert.True(t, c.isBootstrapped())
   123  }
   124  
   125  func setup(t *testing.T) (ctx context.Context, resolvers []*amb.ConsulResolver, mappings []consulMapping, c *consulWatcher, tw *testWatcher) {
   126  	var cancel context.CancelFunc
   127  	ctx, cancel = context.WithCancel(dlog.NewTestContext(t, false))
   128  	grp := dgroup.NewGroup(ctx, dgroup.GroupConfig{})
   129  	t.Cleanup(func() {
   130  		cancel()
   131  		assert.NoError(t, grp.Wait())
   132  	})
   133  
   134  	parent := &kates.Unstructured{
   135  		Object: map[string]interface{}{
   136  			"metadata": map[string]interface{}{
   137  				"namespace": "default",
   138  				"annotations": map[string]interface{}{
   139  					"getambassador.io/config": manifests,
   140  				},
   141  			},
   142  		},
   143  	}
   144  
   145  	objs, err := snapshotTypes.ParseAnnotationResources(parent)
   146  	require.NoError(t, err)
   147  
   148  	for _, obj := range objs {
   149  		newobj, err := snapshotTypes.ValidateAndConvertObject(ctx, obj)
   150  		if !assert.NoError(t, err) {
   151  			continue
   152  		}
   153  		switch o := newobj.(type) {
   154  		case *amb.ConsulResolver:
   155  			resolvers = append(resolvers, o)
   156  		case *amb.Mapping:
   157  			mappings = append(mappings, consulMapping{Service: o.Spec.Service, Resolver: o.Spec.Resolver})
   158  		case *amb.TCPMapping:
   159  			mappings = append(mappings, consulMapping{Service: o.Spec.Service, Resolver: o.Spec.Resolver})
   160  		}
   161  	}
   162  
   163  	assert.Equal(t, 1, len(resolvers))
   164  	assert.Equal(t, 4, len(mappings))
   165  
   166  	tw = &testWatcher{t: t, events: make(map[string]bool)}
   167  	c = newConsulWatcher(tw.Watch)
   168  	grp.Go("consul", c.run)
   169  	tw.Assert()
   170  
   171  	return
   172  }
   173  
   174  type testWatcher struct {
   175  	t      *testing.T
   176  	events map[string]bool
   177  }
   178  
   179  func (tw *testWatcher) Log(event string) {
   180  	tw.events[event] = true
   181  }
   182  
   183  func (tw *testWatcher) Logf(format string, args ...interface{}) {
   184  	tw.Log(fmt.Sprintf(format, args...))
   185  }
   186  
   187  func (tw *testWatcher) Assert(events ...string) {
   188  	eventsMap := make(map[string]bool)
   189  	for _, e := range events {
   190  		eventsMap[e] = true
   191  	}
   192  	assert.Equal(tw.t, eventsMap, tw.events)
   193  	tw.events = make(map[string]bool)
   194  }
   195  
   196  func (tw *testWatcher) Watch(ctx context.Context, resolver *amb.ConsulResolver, svc string, _ chan consulwatch.Endpoints) (Stopper, error) {
   197  	rname := fmt.Sprintf("%s.%s", resolver.GetName(), resolver.GetNamespace())
   198  	tw.Logf("%s:%s:watch", rname, svc)
   199  	return &testStopper{watcher: tw, resolver: rname, service: svc}, nil
   200  }
   201  
   202  type testStopper struct {
   203  	watcher  *testWatcher
   204  	resolver string
   205  	service  string
   206  }
   207  
   208  func (ts *testStopper) Stop() {
   209  	ts.watcher.Logf("%s:%s:stop", ts.resolver, ts.service)
   210  }
   211  

View as plain text