Repository: clouditor/clouditor
Pull request #694 (2022-05-19 13:27:56)

Make Sending Evidences to Evidence Store Optional

codecov[bot]: # [Codecov](https://codecov.io/gh/clouditor/clouditor/pull/694?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) Report
> Merging [#694](https://codecov.io/gh/clouditor/clouditor/pull/694?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) (69d037a) into [main](https://codecov.io/gh/clouditor/clouditor/commit/2b658cc76dab92ac08e5e892f322e51931712e83?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) (2b658cc) will **decrease** coverage by `0.04%`.
> The diff coverage is `95.83%`.

> :exclamation: Current head 69d037a differs from pull request most recent head 6cc2d83. Consider uploading reports for the commit 6cc2d83 to get more accurate results

```diff
@@            Coverage Diff             @@
##             main     #694      +/-   ##
==========================================
- Coverage   81.58%   81.53%   -0.05%     
==========================================
  Files          65       65              
  Lines        6032     6038       +6     
==========================================
+ Hits         4921     4923       +2     
- Misses        856      859       +3     
- Partials      255      256       +1     
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `81.53% <95.83%> (-0.05%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/clouditor/clouditor/pull/694?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor) | Coverage Δ | |
|---|---|---|
| [service/assessment/assessment.go](https://codecov.io/gh/clouditor/clouditor/pull/694/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-c2VydmljZS9hc3Nlc3NtZW50L2Fzc2Vzc21lbnQuZ28=) | `83.91% <95.83%> (+0.26%)` | :arrow_up: |
| [service/discovery/azure/azure.go](https://codecov.io/gh/clouditor/clouditor/pull/694/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor#diff-c2VydmljZS9kaXNjb3ZlcnkvYXp1cmUvYXp1cmUuZ28=) | `79.22% <0.00%> (-5.20%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/clouditor/clouditor/pull/694?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/clouditor/clouditor/pull/694?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor). Last update [2b658cc...6cc2d83](https://codecov.io/gh/clouditor/clouditor/pull/694?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clouditor).


Patch:
diff --git a/service/assessment/assessment.go b/service/assessment/assessment.go
index e49221d5..e6de905e 100644
--- a/service/assessment/assessment.go
+++ b/service/assessment/assessment.go
@@ -81,6 +81,8 @@ type Service struct {
 	// Embedded for FWD compatibility
 	assessment.UnimplementedAssessmentServer
 
+	// isEvidenceStoreDisabled specifies if evidences shall be discarded (when true).
+	isEvidenceStoreDisabled bool
 	// evidenceStoreStream sends evidences to the Evidence Store
 	evidenceStoreStreams *api.StreamsOf[evidence.EvidenceStore_StoreEvidencesClient, *evidence.StoreEvidenceRequest]
 	evidenceStoreAddress grpcTarget
@@ -124,17 +126,21 @@ const (
 	DefaultOrchestratorAddress = "localhost:9090"
 )
 
-// ServiceOption is a functional option type to configure the assessment service.
-type ServiceOption func(*Service)
+// WithoutEvidenceStore is a service option to discard evidences and don't send them to an evidence store
+func WithoutEvidenceStore() service.Option[Service] {
+	return func(svc *Service) {
+		svc.isEvidenceStoreDisabled = true
+	}
+}
 
 // WithEvidenceStoreAddress is an option to configure the evidence store gRPC address.
-func WithEvidenceStoreAddress(address string, opts ...grpc.DialOption) ServiceOption {
-	return func(s *Service) {
+func WithEvidenceStoreAddress(address string, opts ...grpc.DialOption) service.Option[Service] {
+	return func(svc *Service) {
 		if address == "" {
 			address = DefaultEvidenceStoreAddress
 		}
 
-		s.evidenceStoreAddress = grpcTarget{
+		svc.evidenceStoreAddress = grpcTarget{
 			target: address,
 			opts:   opts,
 		}
@@ -142,13 +148,13 @@ func WithEvidenceStoreAddress(address string, opts ...grpc.DialOption) ServiceOp
 }
 
 // WithOrchestratorAddress is an option to configure the orchestrator gRPC address.
-func WithOrchestratorAddress(address string, opts ...grpc.DialOption) ServiceOption {
-	return func(s *Service) {
+func WithOrchestratorAddress(address string, opts ...grpc.DialOption) service.Option[Service] {
+	return func(svc *Service) {
 		if address == "" {
 			address = DefaultOrchestratorAddress
 		}
 
-		s.orchestratorAddress = grpcTarget{
+		svc.orchestratorAddress = grpcTarget{
 			target: address,
 			opts:   opts,
 		}
@@ -156,21 +162,21 @@ func WithOrchestratorAddress(address string, opts ...grpc.DialOption) ServiceOpt
 }
 
 // WithOAuth2Authorizer is an option to use an OAuth 2.0 authorizer
-func WithOAuth2Authorizer(config *clientcredentials.Config) ServiceOption {
+func WithOAuth2Authorizer(config *clientcredentials.Config) service.Option[Service] {
 	return func(s *Service) {
 		s.SetAuthorizer(api.NewOAuthAuthorizerFromClientCredentials(config))
 	}
 }
 
 // WithRegoPackageName is an option to configure the Rego package name
-func WithRegoPackageName(pkg string) ServiceOption {
+func WithRegoPackageName(pkg string) service.Option[Service] {
 	return func(s *Service) {
 		s.evalPkg = pkg
 	}
 }
 
 // NewService creates a new assessment service with default values.
-func NewService(opts ...ServiceOption) *Service {
+func NewService(opts ...service.Option[Service]) *Service {
 	s := &Service{
 		results:              make(map[string]*assessment.AssessmentResult),
 		evidenceStoreStreams: api.NewStreamsOf(api.WithLogger[evidence.EvidenceStore_StoreEvidencesClient, *evidence.StoreEvidenceRequest](log)),
@@ -318,15 +324,19 @@ func (svc *Service) handleEvidence(ev *evidence.Evidence, resourceId string) (er
 		return newError
 	}
 
-	// Get Evidence Store stream
-	channelEvidenceStore, err := svc.evidenceStoreStreams.GetStream(svc.evidenceStoreAddress.target, "Evidence Store", svc.initEvidenceStoreStream, svc.evidenceStoreAddress.opts...)
-	if err != nil {
-		err = fmt.Errorf("could not get stream to evidence store (%s): %w", svc.evidenceStoreAddress.target, err)
-		log.Error(err)
+	// Send evidence via Evidence Store stream if sending evidences is not disabled
+	if !svc.isEvidenceStoreDisabled {
+		// Get Evidence Store stream
+		channelEvidenceStore, err := svc.evidenceStoreStreams.GetStream(svc.evidenceStoreAddress.target, "Evidence Store", svc.initEvidenceStoreStream, svc.evidenceStoreAddress.opts...)
+		if err != nil {
+			err = fmt.Errorf("could not get stream to evidence store (%s): %w", svc.evidenceStoreAddress.target, err)
+			log.Error(err)
 
-		go svc.informHooks(nil, err)
+			go svc.informHooks(nil, err)
 
-		return err
+			return err
+		}
+		channelEvidenceStore.Send(&evidence.StoreEvidenceRequest{Evidence: ev})
 	}
 
 	// Get Orchestrator stream
@@ -340,9 +350,6 @@ func (svc *Service) handleEvidence(ev *evidence.Evidence, resourceId string) (er
 		return err
 	}
 
-	// Send evidence in evidenceStoreChannel
-	channelEvidenceStore.Send(&evidence.StoreEvidenceRequest{Evidence: ev})
-
 	for i, data := range evaluations {
 		metricId := data.MetricId
 
diff --git a/service/option.go b/service/option.go
new file mode 100644
index 00000000..be0b769e
--- /dev/null
+++ b/service/option.go
@@ -0,0 +1,29 @@
+// Copyright 2022 Fraunhofer AISEC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//           $$\                           $$\ $$\   $$\
+//           $$ |                          $$ |\__|  $$ |
+//  $$$$$$$\ $$ | $$$$$$\  $$\   $$\  $$$$$$$ |$$\ $$$$$$\    $$$$$$\   $$$$$$\
+// $$  _____|$$ |$$  __$$\ $$ |  $$ |$$  __$$ |$$ |\_$$  _|  $$  __$$\ $$  __$$\
+// $$ /      $$ |$$ /  $$ |$$ |  $$ |$$ /  $$ |$$ |  $$ |    $$ /  $$ |$$ | \__|
+// $$ |      $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$\ $$ |  $$ |$$ |
+// \$$$$$$\  $$ |\$$$$$   |\$$$$$   |\$$$$$$  |$$ |  \$$$   |\$$$$$   |$$ |
+//  \_______|\__| \______/  \______/  \_______|\__|   \____/  \______/ \__|
+//
+// This file is part of Clouditor Community Edition.
+
+package service
+
+// Option is a functional option type to configure services.
+type Option[T any] func(*T)


Test patch:
diff --git a/service/assessment/assessment_test.go b/service/assessment/assessment_test.go
index e230fed2..5bb06c5c 100644
--- a/service/assessment/assessment_test.go
+++ b/service/assessment/assessment_test.go
@@ -26,6 +26,7 @@
 package assessment
 
 import (
+	"clouditor.io/clouditor/service"
 	"context"
 	"encoding/json"
 	"errors"
@@ -73,7 +74,7 @@ func TestMain(m *testing.M) {
 // TestNewService is a simply test for NewService
 func TestNewService(t *testing.T) {
 	type args struct {
-		opts []ServiceOption
+		opts []service.Option[Service]
 	}
 	tests := []struct {
 		name string
@@ -99,7 +100,7 @@ func TestNewService(t *testing.T) {
 		{
 			name: "AssessmentServer created with options",
 			args: args{
-				opts: []ServiceOption{
+				opts: []service.Option[Service]{
 					WithEvidenceStoreAddress("localhost:9091"),
 					WithOrchestratorAddress("localhost:9092"),
 				},
@@ -118,6 +119,27 @@ func TestNewService(t *testing.T) {
 				evalPkg:              policies.DefaultRegoPackage,
 			},
 		},
+		{
+			name: "AssessmentServer without EvidenceStore",
+			args: args{
+				opts: []service.Option[Service]{
+					WithoutEvidenceStore(),
+				},
+			},
+			want: &Service{
+				results:                 make(map[string]*assessment.AssessmentResult),
+				isEvidenceStoreDisabled: true,
+				evidenceStoreAddress: grpcTarget{
+					target: DefaultEvidenceStoreAddress,
+				},
+				orchestratorAddress: grpcTarget{
+					target: DefaultOrchestratorAddress,
+				},
+				orchestratorStreams:  nil,
+				cachedConfigurations: make(map[string]cachedConfiguration),
+				evalPkg:              policies.DefaultRegoPackage,
+			},
+		},
 	}
 
 	for _, tt := range tests {
@@ -1048,7 +1070,7 @@ func TestHandleEvidence(t *testing.T) {
 
 func TestService_initOrchestratorStoreStream(t *testing.T) {
 	type fields struct {
-		opts []ServiceOption
+		opts []service.Option[Service]
 	}
 	type args struct {
 		url string
@@ -1065,7 +1087,7 @@ func TestService_initOrchestratorStoreStream(t *testing.T) {
 				url: "localhost:1",
 			},
 			fields: fields{
-				opts: []ServiceOption{
+				opts: []service.Option[Service]{
 					WithOrchestratorAddress("localhost:1"),
 				},
 			},
@@ -1094,7 +1116,7 @@ func TestService_initOrchestratorStoreStream(t *testing.T) {
 				url: "bufnet",
 			},
 			fields: fields{
-				opts: []ServiceOption{
+				opts: []service.Option[Service]{
 					WithOrchestratorAddress("bufnet", grpc.WithContextDialer(bufConnDialer)),
 					WithOAuth2Authorizer(testutil.AuthClientConfig(authPort)),
 				},