/* Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ package certcache import ( "context" "crypto/x509" "testing" "github.com/edgelesssys/constellation/v2/internal/attestation/variant" "github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/crypto" "github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/joinservice/internal/certcache/testdata" "github.com/google/go-sev-guest/abi" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" k8serrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime/schema" ) func TestCreateCertChainCache(t *testing.T) { notFoundErr := k8serrors.NewNotFound(schema.GroupResource{}, "test") testCases := map[string]struct { kubeClient *stubKubeClient kdsClient *stubKdsClient expectedArk *x509.Certificate expectedAsk *x509.Certificate wantErr bool }{ "available in configmap": { kubeClient: &stubKubeClient{ askResponse: string(testdata.Ask), arkResponse: string(testdata.Ark), }, kdsClient: &stubKdsClient{}, expectedArk: mustParsePEM(testdata.Ark), expectedAsk: mustParsePEM(testdata.Ask), }, "query from kds": { kubeClient: &stubKubeClient{ getConfigMapDataErr: notFoundErr, }, kdsClient: &stubKdsClient{ askResponse: testdata.Ask, arkResponse: testdata.Ark, }, expectedArk: mustParsePEM(testdata.Ark), expectedAsk: mustParsePEM(testdata.Ask), }, "only replace uncached cert": { kubeClient: &stubKubeClient{ askResponse: string(testdata.Ark), // on purpose }, kdsClient: &stubKdsClient{ arkResponse: testdata.Ark, askResponse: testdata.Ask, }, expectedArk: mustParsePEM(testdata.Ark), expectedAsk: mustParsePEM(testdata.Ark), // on purpose }, "only ask available in configmap": { kubeClient: &stubKubeClient{ askResponse: string(testdata.Ask), }, kdsClient: &stubKdsClient{ arkResponse: testdata.Ark, }, expectedArk: mustParsePEM(testdata.Ark), expectedAsk: mustParsePEM(testdata.Ask), }, "only ark available in configmap": { kubeClient: &stubKubeClient{ arkResponse: string(testdata.Ark), }, kdsClient: &stubKdsClient{ askResponse: testdata.Ask, }, expectedArk: mustParsePEM(testdata.Ark), expectedAsk: mustParsePEM(testdata.Ask), }, "get config map data err": { kubeClient: &stubKubeClient{ getConfigMapDataErr: assert.AnError, }, wantErr: true, }, "update configmap err": { kubeClient: &stubKubeClient{ askResponse: string(testdata.Ask), updateConfigMapErr: assert.AnError, }, kdsClient: &stubKdsClient{ arkResponse: testdata.Ark, }, wantErr: true, }, "kds cert chain err": { kubeClient: &stubKubeClient{ getConfigMapDataErr: notFoundErr, }, kdsClient: &stubKdsClient{ certChainErr: assert.AnError, }, wantErr: true, }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { assert := assert.New(t) require := require.New(t) ctx := context.Background() c := &Client{ attVariant: variant.Dummy{}, log: logger.NewTest(t), kubeClient: tc.kubeClient, kdsClient: tc.kdsClient, } ask, ark, err := c.createCertChainCache(ctx, abi.NoneReportSigner) if tc.wantErr { assert.Error(err) } else { require.NoError(err) assert.Equal(tc.expectedArk, ark) assert.Equal(tc.expectedAsk, ask) } }) } } type stubKdsClient struct { askResponse []byte arkResponse []byte certChainErr error } func (c *stubKdsClient) CertChain(abi.ReportSigner) (ask, ark *x509.Certificate, err error) { if c.askResponse != nil { ask = mustParsePEM(c.askResponse) } if c.arkResponse != nil { ark = mustParsePEM(c.arkResponse) } return ask, ark, c.certChainErr } func mustParsePEM(pemBytes []byte) *x509.Certificate { cert, err := crypto.PemToX509Cert(pemBytes) if err != nil { panic(err) } return cert } func TestGetCertChainCache(t *testing.T) { testCases := map[string]struct { kubeClient *stubKubeClient expectedAsk *x509.Certificate expectedArk *x509.Certificate wantErr bool }{ "success": { kubeClient: &stubKubeClient{ askResponse: string(testdata.Ask), arkResponse: string(testdata.Ark), }, expectedAsk: mustParsePEM(testdata.Ask), expectedArk: mustParsePEM(testdata.Ark), }, "empty ask": { kubeClient: &stubKubeClient{ askResponse: "", arkResponse: string(testdata.Ark), }, expectedAsk: nil, expectedArk: mustParsePEM(testdata.Ark), }, "empty ark": { kubeClient: &stubKubeClient{ askResponse: string(testdata.Ask), arkResponse: "", }, expectedAsk: mustParsePEM(testdata.Ask), expectedArk: nil, }, "error getting config map data": { kubeClient: &stubKubeClient{ getConfigMapDataErr: assert.AnError, }, wantErr: true, }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { assert := assert.New(t) ctx := context.Background() c := NewClient(logger.NewTest(t), tc.kubeClient, variant.Dummy{}) ask, ark, err := c.getCertChainCache(ctx) if tc.wantErr { assert.Error(err) } else { assert.NoError(err) assert.Equal(tc.expectedArk, ark) assert.Equal(tc.expectedAsk, ask) } }) } } type stubKubeClient struct { askResponse string arkResponse string createConfigMapErr error updateConfigMapErr error getConfigMapDataErr error } func (s *stubKubeClient) CreateConfigMap(context.Context, string, map[string]string) error { return s.createConfigMapErr } func (s *stubKubeClient) GetConfigMapData(_ context.Context, _ string, key string) (string, error) { if key == constants.CertCacheAskKey { return s.askResponse, s.getConfigMapDataErr } if key == constants.CertCacheArkKey { return s.arkResponse, s.getConfigMapDataErr } return "", s.getConfigMapDataErr } func (s *stubKubeClient) UpdateConfigMap(context.Context, string, string, string) error { return s.updateConfigMapErr }