...

Source file src/github.com/emissary-ingress/emissary/v3/cmd/entrypoint/istiocert_test.go

Documentation: github.com/emissary-ingress/emissary/v3/cmd/entrypoint

     1  package entrypoint_test
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"reflect"
     7  	"sync"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/datawire/dlib/dlog"
    12  	"github.com/datawire/dlib/dtime"
    13  	"github.com/emissary-ingress/emissary/v3/cmd/entrypoint"
    14  	"github.com/emissary-ingress/emissary/v3/pkg/kates"
    15  )
    16  
    17  const (
    18  	PUBLIC_KEY  string = "fake-public-key"
    19  	PRIVATE_KEY string = "fake-private-key"
    20  )
    21  
    22  func fakeReadPEM(ctx context.Context, dir string, name string) ([]byte, bool) {
    23  	if name == "key.pem" {
    24  		return []byte(PRIVATE_KEY), true
    25  	} else if name == "cert-chain.pem" {
    26  		return []byte(PUBLIC_KEY), true
    27  	} else {
    28  		return nil, false
    29  	}
    30  }
    31  
    32  type icertMetadata struct {
    33  	t      *testing.T
    34  	ft     *dtime.FakeTime
    35  	icert  *entrypoint.IstioCert
    36  	events []entrypoint.IstioCertUpdate
    37  	mutex  sync.Mutex
    38  }
    39  
    40  func newICertMetadata(t *testing.T) (context.Context, *icertMetadata) {
    41  	ctx := dlog.NewTestContext(t, false)
    42  	ft := dtime.NewFakeTime()
    43  
    44  	updates := make(chan entrypoint.IstioCertUpdate)
    45  
    46  	icert := entrypoint.NewIstioCert("/tmp", "istio-test", "ambassador", updates)
    47  	icert.SetFetchTime(ft.Now)
    48  	icert.SetReadPEM(fakeReadPEM)
    49  
    50  	m := &icertMetadata{t: t, ft: ft, icert: icert}
    51  	m.events = make([]entrypoint.IstioCertUpdate, 0, 5)
    52  
    53  	go func() {
    54  		for {
    55  			evt := <-updates
    56  
    57  			m.mutex.Lock()
    58  			m.events = append(m.events, evt)
    59  			m.mutex.Unlock()
    60  
    61  			if evt.Op == "update" {
    62  				dlog.Infof(ctx, "Event handler: got update of %s", evt.Secret.ObjectMeta.Name)
    63  			} else {
    64  				dlog.Infof(ctx, "Event handler: got deletion")
    65  			}
    66  		}
    67  	}()
    68  
    69  	return ctx, m
    70  }
    71  
    72  func (m *icertMetadata) stepSec(sec int) {
    73  	m.ft.StepSec(sec)
    74  }
    75  
    76  func (m *icertMetadata) check(ctx context.Context, what string, name string, deleted bool, count int) {
    77  	m.icert.HandleEvent(ctx, name, deleted)
    78  	time.Sleep(250 * time.Millisecond)
    79  
    80  	m.mutex.Lock()
    81  	defer m.mutex.Unlock()
    82  	if len(m.events) != count {
    83  		m.t.Errorf("%s: wanted event count %d, got %d", what, count, len(m.events))
    84  	}
    85  }
    86  
    87  func (m *icertMetadata) checkNoSecret() {
    88  	m.mutex.Lock()
    89  	count := len(m.events)
    90  	m.mutex.Unlock()
    91  
    92  	if count > 0 {
    93  		m.mutex.Lock()
    94  		evt := m.events[count-1]
    95  		m.mutex.Unlock()
    96  
    97  		if evt.Op != "delete" {
    98  			m.t.Errorf("wanted no live secret, got %s op?", evt.Op)
    99  		}
   100  
   101  		if evt.Secret != nil {
   102  			m.t.Errorf("wanted no live secret, got %s", evt.Secret.ObjectMeta.Name)
   103  		}
   104  	}
   105  }
   106  
   107  func (m *icertMetadata) checkSecret(namespace string, publicPEM string, privatePEM string) {
   108  	wantedSecret := &kates.Secret{
   109  		TypeMeta: kates.TypeMeta{
   110  			APIVersion: "v1",
   111  			Kind:       "Secret",
   112  		},
   113  		ObjectMeta: kates.ObjectMeta{
   114  			Name:      "istio-test",
   115  			Namespace: namespace,
   116  		},
   117  		Type: kates.SecretTypeTLS,
   118  		Data: map[string][]byte{
   119  			"tls.key": []byte(privatePEM),
   120  			"tls.crt": []byte(publicPEM),
   121  		},
   122  	}
   123  
   124  	count := len(m.events)
   125  
   126  	if count == 0 {
   127  		m.t.Errorf("wanted live secret, have none")
   128  		return
   129  	}
   130  
   131  	m.mutex.Lock()
   132  	evt := m.events[count-1]
   133  	m.mutex.Unlock()
   134  
   135  	if evt.Op != "update" {
   136  		m.t.Errorf("wanted live secret, got %s op?", evt.Op)
   137  	}
   138  
   139  	if evt.Name != wantedSecret.ObjectMeta.Name {
   140  		m.t.Errorf("wanted name %s in update, got %s", wantedSecret.ObjectMeta.Name, evt.Name)
   141  	}
   142  
   143  	if evt.Namespace != wantedSecret.ObjectMeta.Namespace {
   144  		m.t.Errorf("wanted namespace %s in update, got %s", wantedSecret.ObjectMeta.Namespace, evt.Namespace)
   145  	}
   146  
   147  	if !reflect.DeepEqual(wantedSecret, evt.Secret) {
   148  		// We know a priori that marshaling the secrets cannot ever fail.
   149  		wantedSecretJSON, err1 := json.MarshalIndent(wantedSecret, "", "  ")
   150  		secretJSON, err2 := json.MarshalIndent(evt.Secret, "", "  ")
   151  
   152  		if (err1 != nil) || (err2 != nil) {
   153  			// WTFO.
   154  			m.t.Errorf("secret mismatch AND impossible errors:\n-- wanted %#v\n--- (err %s)\n-- got %#v\n--- (err %s)",
   155  				wantedSecret, err1, evt.Secret, err2)
   156  		} else {
   157  			// Oh good, the thing that can't fail didn't fail.
   158  			m.t.Errorf("secret mismatch:\n-- wanted %s\n-- got %s", wantedSecretJSON, secretJSON)
   159  		}
   160  	}
   161  }
   162  
   163  func TestIstioCertHappyBoot1(t *testing.T) {
   164  	ctx, m := newICertMetadata(t)
   165  
   166  	m.check(ctx, "boot foo", "/tmp/foo", false, 0)
   167  	m.check(ctx, "boot bar", "/tmp/bar", false, 0)
   168  	m.check(ctx, "boot root-cert.pem", "/tmp/root-cert.pem", false, 0)
   169  	m.check(ctx, "boot cert-chain.pem", "/tmp/cert-chain.pem", false, 0)
   170  	m.check(ctx, "boot key.pem", "/tmp/key.pem", false, 1)
   171  
   172  	m.checkSecret("ambassador", PUBLIC_KEY, PRIVATE_KEY)
   173  }
   174  
   175  func TestIstioCertHappyBoot2(t *testing.T) {
   176  	ctx, m := newICertMetadata(t)
   177  
   178  	m.check(ctx, "boot key.pem", "/tmp/key.pem", false, 0)
   179  	m.check(ctx, "boot foo", "/tmp/foo", false, 0)
   180  	m.check(ctx, "boot bar", "/tmp/bar", false, 0)
   181  	m.check(ctx, "boot root-cert.pem", "/tmp/root-cert.pem", false, 0)
   182  	m.check(ctx, "boot cert-chain.pem", "/tmp/cert-chain.pem", false, 1)
   183  
   184  	m.checkSecret("ambassador", PUBLIC_KEY, PRIVATE_KEY)
   185  }
   186  
   187  func TestIstioCertHappyNoBoot(t *testing.T) {
   188  	ctx, m := newICertMetadata(t)
   189  
   190  	m.stepSec(5)
   191  	m.check(ctx, "key.pem", "/tmp/key.pem", false, 0)
   192  	m.stepSec(1)
   193  	m.check(ctx, "root-cert.pem", "/tmp/root-cert.pem", false, 0)
   194  	m.stepSec(2)
   195  	m.check(ctx, "cert-chain.pem", "/tmp/cert-chain.pem", false, 1)
   196  
   197  	m.checkSecret("ambassador", PUBLIC_KEY, PRIVATE_KEY)
   198  }
   199  
   200  func TestIstioCertTooSlow1(t *testing.T) {
   201  	ctx, m := newICertMetadata(t)
   202  
   203  	m.stepSec(5)
   204  	m.check(ctx, "key.pem", "/tmp/key.pem", false, 0)
   205  	m.stepSec(5)
   206  	m.check(ctx, "root-cert.pem", "/tmp/root-cert.pem", false, 0)
   207  	m.stepSec(5)
   208  	m.check(ctx, "cert-chain.pem", "/tmp/cert-chain.pem", false, 0)
   209  }
   210  
   211  func TestIstioCertTooSlow2(t *testing.T) {
   212  	ctx, m := newICertMetadata(t)
   213  
   214  	m.stepSec(5)
   215  	m.check(ctx, "key.pem", "/tmp/key.pem", false, 0)
   216  	m.stepSec(1)
   217  	m.check(ctx, "root-cert.pem", "/tmp/root-cert.pem", false, 0)
   218  	m.stepSec(5)
   219  	m.check(ctx, "cert-chain.pem", "/tmp/cert-chain.pem", false, 0)
   220  }
   221  
   222  func TestIstioCertEventually(t *testing.T) {
   223  	ctx, m := newICertMetadata(t)
   224  
   225  	m.stepSec(5)
   226  	m.check(ctx, "key.pem", "/tmp/key.pem", false, 0)
   227  	m.stepSec(5)
   228  	m.check(ctx, "root-cert.pem", "/tmp/root-cert.pem", false, 0)
   229  	m.stepSec(1)
   230  	m.check(ctx, "cert-chain.pem", "/tmp/cert-chain.pem", false, 0)
   231  	m.stepSec(1)
   232  	m.check(ctx, "root-cert.pem", "/tmp/root-cert.pem", false, 0)
   233  	m.stepSec(2)
   234  	m.check(ctx, "key.pem", "/tmp/key.pem", false, 1)
   235  
   236  	m.checkSecret("ambassador", PUBLIC_KEY, PRIVATE_KEY)
   237  }
   238  
   239  func TestIstioCertDeletion(t *testing.T) {
   240  	ctx, m := newICertMetadata(t)
   241  
   242  	m.stepSec(5)
   243  	m.check(ctx, "key.pem", "/tmp/key.pem", false, 0)
   244  	m.checkNoSecret()
   245  
   246  	m.stepSec(1)
   247  	m.check(ctx, "root-cert.pem", "/tmp/root-cert.pem", false, 0)
   248  	m.stepSec(1)
   249  	m.check(ctx, "cert-chain.pem", "/tmp/cert-chain.pem", false, 1)
   250  	m.checkSecret("ambassador", PUBLIC_KEY, PRIVATE_KEY)
   251  
   252  	m.stepSec(1)
   253  	m.check(ctx, "root-cert.pem", "/tmp/root-cert.pem", true, 1)
   254  	m.checkSecret("ambassador", PUBLIC_KEY, PRIVATE_KEY)
   255  
   256  	m.stepSec(1)
   257  	m.check(ctx, "key.pem", "/tmp/key.pem", true, 2)
   258  	m.checkNoSecret()
   259  }
   260  

View as plain text