From 92bc5e5ff6adad6f8072df1930d63b93112ca531 Mon Sep 17 00:00:00 2001 From: Dimitar Dimitrov Date: Fri, 15 Dec 2023 17:44:03 +0200 Subject: [PATCH] Provide unit tests covering signed images verification (#220) * [#91] Provide unit tests covering signed images verification --------- Signed-off-by: Dimitar Dimitrov --- containerm/ctr/ctr_client_opts_test.go | 45 +++++---- containerm/ctr/ctr_verifier_mock.go | 62 ++++++++++++ containerm/ctr/ctr_verifier_test.go | 57 +++++++++++ containerm/ctr/ctrd_client_internal_test.go | 40 +++++--- containerm/daemon/daemon_test.go | 84 +++++++++++++--- .../config/daemon-config-image-verifier.json | 95 +++++++++++++++++++ 6 files changed, 344 insertions(+), 39 deletions(-) create mode 100644 containerm/ctr/ctr_verifier_mock.go create mode 100644 containerm/ctr/ctr_verifier_test.go create mode 100644 containerm/pkg/testutil/config/daemon-config-image-verifier.json diff --git a/containerm/ctr/ctr_client_opts_test.go b/containerm/ctr/ctr_client_opts_test.go index f5c76c8..61d4d37 100644 --- a/containerm/ctr/ctr_client_opts_test.go +++ b/containerm/ctr/ctr_client_opts_test.go @@ -13,11 +13,12 @@ package ctr import ( - "github.com/eclipse-kanto/container-management/containerm/containers/types" - "github.com/eclipse-kanto/container-management/containerm/log" "testing" "time" + "github.com/eclipse-kanto/container-management/containerm/containers/types" + "github.com/eclipse-kanto/container-management/containerm/log" + "github.com/eclipse-kanto/container-management/containerm/pkg/testutil" ) @@ -35,7 +36,7 @@ const ( ) var ( - regConfig = &RegistryConfig{ + testRegConfig = &RegistryConfig{ IsInsecure: false, Credentials: &AuthCredentials{ UserID: testUser, @@ -43,19 +44,22 @@ var ( }, Transport: nil, } + testVerifierConfig = map[string]string{"testKey": "testValue", "testAnotherKey": "testAnotherValue"} testOpt = &ctrOpts{ - namespace: testNamespace, - connectionPath: testConnectionPath, - registryConfigs: map[string]*RegistryConfig{testHost: regConfig}, - rootExec: testRootExec, - metaPath: testMetaPath, - imageDecKeys: testDecKeys, - imageDecRecipients: testDecRecipients, - runcRuntime: types.RuntimeTypeV2runcV2, - imageExpiry: testImageExpiry, - imageExpiryDisable: testImageExpiryDisable, - leaseID: testLeaseID, + namespace: testNamespace, + connectionPath: testConnectionPath, + registryConfigs: map[string]*RegistryConfig{testHost: testRegConfig}, + rootExec: testRootExec, + metaPath: testMetaPath, + imageDecKeys: testDecKeys, + imageDecRecipients: testDecRecipients, + runcRuntime: types.RuntimeTypeV2runcV2, + imageExpiry: testImageExpiry, + imageExpiryDisable: testImageExpiryDisable, + leaseID: testLeaseID, + imageVerifierType: VerifierNotation, + imageVerifierConfig: testVerifierConfig, } ) @@ -72,18 +76,27 @@ func TestCtrOpts(t *testing.T) { expectedOpts: &ctrOpts{}, expectedErr: log.NewErrorf("unexpected runc runtime = unknown"), }, + "test_ctr_opts_unexpected_image_verifier_type_error": { + opts: []ContainerOpts{ + WithCtrImageVerifierType("unknown"), + }, + expectedOpts: &ctrOpts{}, + expectedErr: log.NewErrorf("unexpected image verifier type = unknown"), + }, "test_ctr_opts_no_error": { opts: []ContainerOpts{WithCtrdConnectionPath(testConnectionPath), WithCtrdNamespace(testNamespace), WithCtrdRootExec(testRootExec), WithCtrdMetaPath(testMetaPath), - WithCtrdRegistryConfigs(map[string]*RegistryConfig{testHost: regConfig}), + WithCtrdRegistryConfigs(map[string]*RegistryConfig{testHost: testRegConfig}), WithCtrdImageDecryptKeys(testDecKeys...), WithCtrdImageDecryptRecipients(testDecRecipients...), WithCtrdRuncRuntime(string(types.RuntimeTypeV2runcV2)), WithCtrdImageExpiry(testImageExpiry), WithCtrdImageExpiryDisable(testImageExpiryDisable), - WithCtrdLeaseID(testLeaseID)}, + WithCtrdLeaseID(testLeaseID), + WithCtrImageVerifierType(string(VerifierNotation)), + WithCtrImageVerifierConfig(testVerifierConfig)}, expectedOpts: testOpt, }, } diff --git a/containerm/ctr/ctr_verifier_mock.go b/containerm/ctr/ctr_verifier_mock.go new file mode 100644 index 0000000..3f12a8a --- /dev/null +++ b/containerm/ctr/ctr_verifier_mock.go @@ -0,0 +1,62 @@ +// Copyright (c) 2023 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + +// Code generated by MockGen. DO NOT EDIT. +// Source: ./containerm/ctr/ctr_verifier.go + +// Package mocks is a generated GoMock package. +package ctr + +import ( + context "context" + reflect "reflect" + + types "github.com/eclipse-kanto/container-management/containerm/containers/types" + gomock "github.com/golang/mock/gomock" +) + +// MockcontainerVerifier is a mock of containerVerifier interface. +type MockcontainerVerifier struct { + ctrl *gomock.Controller + recorder *MockcontainerVerifierMockRecorder +} + +// MockcontainerVerifierMockRecorder is the mock recorder for MockcontainerVerifier. +type MockcontainerVerifierMockRecorder struct { + mock *MockcontainerVerifier +} + +// NewMockcontainerVerifier creates a new mock instance. +func NewMockcontainerVerifier(ctrl *gomock.Controller) *MockcontainerVerifier { + mock := &MockcontainerVerifier{ctrl: ctrl} + mock.recorder = &MockcontainerVerifierMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockcontainerVerifier) EXPECT() *MockcontainerVerifierMockRecorder { + return m.recorder +} + +// Verify mocks base method. +func (m *MockcontainerVerifier) Verify(arg0 context.Context, arg1 types.Image) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Verify", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Verify indicates an expected call of Verify. +func (mr *MockcontainerVerifierMockRecorder) Verify(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Verify", reflect.TypeOf((*MockcontainerVerifier)(nil).Verify), arg0, arg1) +} diff --git a/containerm/ctr/ctr_verifier_test.go b/containerm/ctr/ctr_verifier_test.go new file mode 100644 index 0000000..2ffb87d --- /dev/null +++ b/containerm/ctr/ctr_verifier_test.go @@ -0,0 +1,57 @@ +// Copyright (c) 2023 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + +package ctr + +import ( + "context" + "testing" + + "github.com/eclipse-kanto/container-management/containerm/containers/types" + "github.com/eclipse-kanto/container-management/containerm/pkg/testutil" + "github.com/notaryproject/notation-go/dir" +) + +func TestContainerVerifier(t *testing.T) { + t.Run("unknown_verifier", func(t *testing.T) { + v, err := newContainerVerifier("unknown", nil, nil) + testutil.AssertNotNil(t, err) + testutil.AssertNil(t, v) + }) + t.Run("skip_verifier", func(t *testing.T) { + v, err := newContainerVerifier(VerifierNone, nil, nil) + testutil.AssertNil(t, err) + testutil.AssertNotNil(t, v) + testutil.AssertNil(t, v.Verify(context.Background(), types.Image{})) + }) + t.Run("notation_verifier", func(t *testing.T) { + config := map[string]string{ + notationKeyConfigDir: "testConfigDir", + notationKeyLibexecDir: "testLibexecDir", + } + registryConfig := map[string]*RegistryConfig{ + testHost: testRegConfig, + } + + v, err := newContainerVerifier(VerifierNotation, config, registryConfig) + testutil.AssertNil(t, err) + testutil.AssertNotNil(t, v) + testutil.AssertNotNil(t, v.Verify(context.Background(), types.Image{})) // expected fail due to invalid config dir + + nv := v.(*notationVerifier) + testutil.AssertEqual(t, registryConfig, nv.registryConfig) + testutil.AssertEqual(t, config[notationKeyConfigDir], dir.UserConfigDir) + testutil.AssertEqual(t, config[notationKeyLibexecDir], dir.UserLibexecDir) + + }) + +} diff --git a/containerm/ctr/ctrd_client_internal_test.go b/containerm/ctr/ctrd_client_internal_test.go index aff3cb6..86f6a4a 100644 --- a/containerm/ctr/ctrd_client_internal_test.go +++ b/containerm/ctr/ctrd_client_internal_test.go @@ -325,17 +325,17 @@ func TestClientInternalPullImage(t *testing.T) { DecryptConfig: &types.DecryptConfig{}, } testCases := map[string]struct { - mockExec func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error) + mockExec func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) }{ "test_get_decrypt_cfg_error": { - mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error) { + mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) { err := log.NewError("test error") decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(nil, err) return nil, err }, }, "test_spi_get_image_error": { - mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error) { + mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) { dc := &config.DecryptConfig{} decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(dc, nil) err := log.NewError("test error") @@ -344,7 +344,7 @@ func TestClientInternalPullImage(t *testing.T) { }, }, "test_spi_get_image_available_check_auth_error": { - mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error) { + mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) { dc := &config.DecryptConfig{} decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(dc, nil) imageMock := mocksContainerd.NewMockImage(ctrl) @@ -355,7 +355,7 @@ func TestClientInternalPullImage(t *testing.T) { }, }, "test_spi_get_image_available_no_error": { - mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error) { + mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) { dc := &config.DecryptConfig{} decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(dc, nil) imageMock := mocksContainerd.NewMockImage(ctrl) @@ -364,11 +364,22 @@ func TestClientInternalPullImage(t *testing.T) { return imageMock, nil }, }, + "test_spi_get_image_not_available_verifier_error": { + mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) { + dc := &config.DecryptConfig{} + decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(dc, nil) + spiMock.EXPECT().GetImage(gomock.Any(), testImageInfo.Name).Return(nil, errdefs.ErrNotFound) + err := log.NewError("test error") + verifierMock.EXPECT().Verify(gomock.Any(), testImageInfo).Return(err) + return nil, err + }, + }, "test_spi_get_image_not_available_pull_error": { - mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error) { + mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) { dc := &config.DecryptConfig{} decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(dc, nil) spiMock.EXPECT().GetImage(gomock.Any(), testImageInfo.Name).Return(nil, errdefs.ErrNotFound) + verifierMock.EXPECT().Verify(gomock.Any(), testImageInfo).Return(nil) regsResolverMock.EXPECT().ResolveImageRegistry(util.GetImageHost(testImageInfo.Name)).Return(nil) err := log.NewError("test error") spiMock.EXPECT().PullImage(gomock.Any(), testImageInfo.Name, matchers.MatchesResolverOpts(containerd.WithSchema1Conversion)).Return(nil, err) @@ -376,10 +387,11 @@ func TestClientInternalPullImage(t *testing.T) { }, }, "test_spi_get_image_not_available_check_auth_error": { - mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error) { + mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) { dc := &config.DecryptConfig{} decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(dc, nil) spiMock.EXPECT().GetImage(gomock.Any(), testImageInfo.Name).Return(nil, errdefs.ErrNotFound) + verifierMock.EXPECT().Verify(gomock.Any(), testImageInfo).Return(nil) regsResolverMock.EXPECT().ResolveImageRegistry(util.GetImageHost(testImageInfo.Name)).Return(nil) imageMock := mocksContainerd.NewMockImage(ctrl) spiMock.EXPECT().PullImage(gomock.Any(), testImageInfo.Name, matchers.MatchesResolverOpts(containerd.WithSchema1Conversion)).Return(imageMock, nil) @@ -389,10 +401,11 @@ func TestClientInternalPullImage(t *testing.T) { }, }, "test_spi_get_image_not_available_gen_unpack_opts_error": { - mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error) { + mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) { dc := &config.DecryptConfig{} decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(dc, nil) spiMock.EXPECT().GetImage(gomock.Any(), testImageInfo.Name).Return(nil, errdefs.ErrNotFound) + verifierMock.EXPECT().Verify(gomock.Any(), testImageInfo).Return(nil) regsResolverMock.EXPECT().ResolveImageRegistry(util.GetImageHost(testImageInfo.Name)).Return(nil) imageMock := mocksContainerd.NewMockImage(ctrl) spiMock.EXPECT().PullImage(gomock.Any(), testImageInfo.Name, matchers.MatchesResolverOpts(containerd.WithSchema1Conversion)).Return(imageMock, nil) @@ -403,10 +416,11 @@ func TestClientInternalPullImage(t *testing.T) { }, }, "test_spi_get_image_not_available_unpack_error": { - mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error) { + mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) { dc := &config.DecryptConfig{} decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(dc, nil).Times(2) spiMock.EXPECT().GetImage(gomock.Any(), testImageInfo.Name).Return(nil, errdefs.ErrNotFound) + verifierMock.EXPECT().Verify(gomock.Any(), testImageInfo).Return(nil) regsResolverMock.EXPECT().ResolveImageRegistry(util.GetImageHost(testImageInfo.Name)).Return(nil) imageMock := mocksContainerd.NewMockImage(ctrl) spiMock.EXPECT().PullImage(gomock.Any(), testImageInfo.Name, matchers.MatchesResolverOpts(containerd.WithSchema1Conversion)).Return(imageMock, nil) @@ -417,10 +431,11 @@ func TestClientInternalPullImage(t *testing.T) { }, }, "test_spi_get_image_not_available_no_error": { - mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, ctrl *gomock.Controller) (containerd.Image, error) { + mockExec: func(decryptMgrMock *mocksCtrd.MockcontainerDecryptMgr, spiMock *mocksCtrd.MockcontainerdSpi, regsResolverMock *mocksCtrd.MockcontainerImageRegistriesResolver, verifierMock *MockcontainerVerifier, ctrl *gomock.Controller) (containerd.Image, error) { dc := &config.DecryptConfig{} decryptMgrMock.EXPECT().GetDecryptConfig(testImageInfo.DecryptConfig).Return(dc, nil).Times(2) spiMock.EXPECT().GetImage(gomock.Any(), testImageInfo.Name).Return(nil, errdefs.ErrNotFound) + verifierMock.EXPECT().Verify(gomock.Any(), testImageInfo).Return(nil) regsResolverMock.EXPECT().ResolveImageRegistry(util.GetImageHost(testImageInfo.Name)).Return(nil) imageMock := mocksContainerd.NewMockImage(ctrl) spiMock.EXPECT().PullImage(gomock.Any(), testImageInfo.Name, matchers.MatchesResolverOpts(containerd.WithSchema1Conversion)).Return(imageMock, nil) @@ -439,13 +454,14 @@ func TestClientInternalPullImage(t *testing.T) { decryptMgrMock := mocksCtrd.NewMockcontainerDecryptMgr(ctrl) spiMock := mocksCtrd.NewMockcontainerdSpi(ctrl) registriesResolverMock := mocksCtrd.NewMockcontainerImageRegistriesResolver(ctrl) + verifierMock := NewMockcontainerVerifier(ctrl) ctrdClient := &containerdClient{ decMgr: decryptMgrMock, spi: spiMock, registriesResolver: registriesResolverMock, - verifier: &skipVerifier{}, + verifier: verifierMock, } - expectedImage, expectedErr := testCaseData.mockExec(decryptMgrMock, spiMock, registriesResolverMock, ctrl) + expectedImage, expectedErr := testCaseData.mockExec(decryptMgrMock, spiMock, registriesResolverMock, verifierMock, ctrl) actualImage, actualErr := ctrdClient.pullImage(context.TODO(), testImageInfo) testutil.AssertError(t, expectedErr, actualErr) testutil.AssertEqual(t, expectedImage, actualImage) diff --git a/containerm/daemon/daemon_test.go b/containerm/daemon/daemon_test.go index 937d270..4610571 100644 --- a/containerm/daemon/daemon_test.go +++ b/containerm/daemon/daemon_test.go @@ -16,6 +16,7 @@ import ( "fmt" "os" "reflect" + "strings" "testing" "time" @@ -83,13 +84,13 @@ func TestDefaultConfig(t *testing.T) { func TestThingsServiceFeaturesConfig(t *testing.T) { local := &config{} _ = loadLocalConfig("../pkg/testutil/config/daemon-things-features-config.json", local) - testutil.AssertEqual(t, local.ThingsConfig.Features, []string{things.ContainerFactoryFeatureID}) + testutil.AssertEqual(t, []string{things.ContainerFactoryFeatureID}, local.ThingsConfig.Features) } func TestThingsTLSConfig(t *testing.T) { local := &config{} _ = loadLocalConfig("../pkg/testutil/config/daemon-things-tls-config.json", local) - testutil.AssertEqual(t, local.ThingsConfig.ThingsConnectionConfig.Transport, &tlsConfig{RootCA: "ca.crt", ClientCert: "client.crt", ClientKey: "client.key"}) + testutil.AssertEqual(t, &tlsConfig{RootCA: "ca.crt", ClientCert: "client.crt", ClientKey: "client.key"}, local.ThingsConfig.ThingsConnectionConfig.Transport) } func TestExtractOpts(t *testing.T) { @@ -261,6 +262,14 @@ func TestSetCommandFlags(t *testing.T) { flag: "ccl-lease-id", expectedType: reflect.String.String(), }, + "test_flags_ccl-image-verifier-type": { + flag: "ccl-image-verifier-type", + expectedType: reflect.String.String(), + }, + "test_flags_ccl-image-verifier-config": { + flag: "ccl-image-verifier-config", + expectedType: "stringSlice", + }, "test_flags_net-type": { flag: "net-type", expectedType: reflect.String.String(), @@ -508,15 +517,6 @@ func TestParseRegistryConfigs(t *testing.T) { t.Errorf("error while parsing registry configs, with nil insecure registries") } }) - t.Run("test_parse_registry_configs_null_insecure", func(t *testing.T) { - cfg := getDefaultInstance() - _ = loadLocalConfig(registriesDaemonConfig, cfg) - cfg.ContainerClientConfig.CtrInsecureRegistries = nil - registryConfigs := parseRegistryConfigs(cfg.ContainerClientConfig.CtrRegistryConfigs, cfg.ContainerClientConfig.CtrInsecureRegistries) - if registryConfigs == nil || len(registryConfigs) == 0 { - t.Errorf("error while parsing registry configs, with nil insecure registries") - } - }) t.Run("test_parse_registry_configs_null_registry_config", func(t *testing.T) { cfg := getDefaultInstance() _ = loadLocalConfig(registriesDaemonConfig, cfg) @@ -661,4 +661,66 @@ func TestRunLock(t *testing.T) { }) } +func TestImageVerifierConfig(t *testing.T) { + local := &config{} + _ = loadLocalConfig("../pkg/testutil/config/daemon-config-image-verifier.json", local) + testutil.AssertEqual(t, "notation", local.ContainerClientConfig.CtrImageVerifierType) + expected := map[string]string{"configDir": "/path/notation/config", "libexecDir": "/path/notation/libexec"} + assertStringMap(t, expected, local.ContainerClientConfig.CtrImageVerifierConfig) +} + +func TestImageVerifierFlag(t *testing.T) { + const ( + testSinglePair = "key=value" + testMultiplePairs = "key0=value0,key1=value1,key2=value2" + ) + + tests := map[string]struct { + value string + expectedValue map[string]string + expectedStringPairs []string + expectedErr error + }{ + "test_empty_error": { + expectedErr: log.NewError("the image verifier config could not be empty"), + }, + "test_parse_error": { + value: "invalid", + expectedErr: log.NewError("could not parse image verification config, invalid key-value pair - invalid"), + }, + "test_single_config_no_error": { + value: testSinglePair, + expectedStringPairs: []string{testSinglePair}, + expectedValue: map[string]string{"key": "value"}, + }, + "test_multiple_configs_no_error": { + value: testMultiplePairs, + expectedStringPairs: strings.Split(testMultiplePairs, ","), + expectedValue: map[string]string{"key0": "value0", "key1": "value1", "key2": "value2"}, + }, + } + for testName, test := range tests { + t.Run(testName, func(t *testing.T) { + verifierConfig := &verifierConfig{} + testutil.AssertError(t, test.expectedErr, verifierConfig.Set(test.value)) + for _, expectedPair := range test.expectedStringPairs { + testutil.AssertTrue(t, strings.Contains(verifierConfig.String(), expectedPair)) + } + testutil.AssertEqual(t, len(test.expectedValue), len(*verifierConfig)) + assertStringMap(t, test.expectedValue, *verifierConfig) + + }) + + } +} + +func assertStringMap(t *testing.T, expected, actual map[string]string) { + testutil.AssertEqual(t, len(expected), len(actual)) + for key, expectedValue := range expected { + value, ok := actual[key] + testutil.AssertTrue(t, ok) + testutil.AssertEqual(t, expectedValue, value) + } +} + // TODO test the behavior of the daemon towards its services (start, stop), with mocked instanced of GRPC service etc. diff --git a/containerm/pkg/testutil/config/daemon-config-image-verifier.json b/containerm/pkg/testutil/config/daemon-config-image-verifier.json new file mode 100644 index 0000000..04d24b9 --- /dev/null +++ b/containerm/pkg/testutil/config/daemon-config-image-verifier.json @@ -0,0 +1,95 @@ +{ + "log": { + "log_file": "log/container-management.log", + "log_level": "INFO", + "log_file_count": 5, + "log_file_size": 2, + "log_file_max_age": 28, + "syslog": false + }, + "manager": { + "home_dir": "/var/lib/container-management", + "exec_root_dir": "/var/run/container-management", + "container_client_sid": "container-management.service.local.v1.service-containerd-client", + "network_manager_sid": "container-management.service.local.v1.service-libnetwork-manager", + "default_ctrs_stop_timeout": 30 + }, + "containers": { + "default_ns": "kanto-cm", + "address_path": "/run/containerd/containerd.sock", + "insecure_registries": [ + "localhost" + ], + "exec_root_dir": "/var/run/container-management", + "home_dir": "/var/lib/container-management", + "runc_runtime": "io.containerd.runc.v2", + "image_expiry": "744h", + "image_expiry_disable": false, + "lease_id": "kanto-cm.lease", + "image_verifier_type": "notation", + "image_verifier_config": { + "configDir": "/path/notation/config", + "libexecDir": "/path/notation/libexec" + } + }, + "network": { + "type": "bridge", + "home_dir": "/var/lib/container-management", + "exec_root_dir": "/var/run/container-management", + "default_bridge": { + "name": "kanto-cm0", + "mtu": 1500, + "icc": true, + "ip_tables": true, + "ip_forward": true, + "ip_masq": true + } + }, + "grpc_server": { + "protocol": "unix", + "address_path": "/run/container-management/container-management.sock" + }, + "things": { + "enable": true, + "home_dir": "/var/lib/container-management", + "features": [ + "ContainerFactory", + "SoftwareUpdatable", + "Metrics" + ], + "connection": { + "broker_url": "tcp://localhost:1883", + "keep_alive": 20000, + "disconnect_timeout": 250, + "client_username": "", + "client_password": "", + "connect_timeout": 30000, + "acknowledge_timeout": 15000, + "subscribe_timeout": 15000, + "unsubscribe_timeout": 5000 + } + }, + "deployment": { + "enable": true, + "mode": "update", + "home_dir": "/var/lib/container-management", + "ctr_dir": "/etc/container-management/containers" + }, + "update_agent": { + "enable": false, + "domain": "containers", + "system_containers": [], + "verbose_inventory": false + }, + "connection": { + "broker_url": "tcp://localhost:1883", + "keep_alive": "20s", + "disconnect_timeout": "250ms", + "client_username": "", + "client_password": "", + "connect_timeout": "30s", + "acknowledge_timeout": "15s", + "subscribe_timeout": "15s", + "unsubscribe_timeout": "5s" + } +}