diff --git a/bundle/manifests/compliance.openshift.io_compliancescans.yaml b/bundle/manifests/compliance.openshift.io_compliancescans.yaml index 725e8feb2c..fd0dc0f937 100644 --- a/bundle/manifests/compliance.openshift.io_compliancescans.yaml +++ b/bundle/manifests/compliance.openshift.io_compliancescans.yaml @@ -106,6 +106,13 @@ spec: rawResultStorage: description: Specifies settings that pertain to raw result storage. properties: + enabled: + default: true + description: |- + Specifies if raw scan results should be saved to persistent volumes. + This is useful for enabling compliance scans in environments that + don't have storage. Defaults to true. + type: boolean nodeSelector: additionalProperties: type: string diff --git a/bundle/manifests/compliance.openshift.io_compliancesuites.yaml b/bundle/manifests/compliance.openshift.io_compliancesuites.yaml index 35c9617042..7982ffe7ad 100644 --- a/bundle/manifests/compliance.openshift.io_compliancesuites.yaml +++ b/bundle/manifests/compliance.openshift.io_compliancesuites.yaml @@ -125,6 +125,13 @@ spec: rawResultStorage: description: Specifies settings that pertain to raw result storage. properties: + enabled: + default: true + description: |- + Specifies if raw scan results should be saved to persistent volumes. + This is useful for enabling compliance scans in environments that + don't have storage. Defaults to true. + type: boolean nodeSelector: additionalProperties: type: string diff --git a/bundle/manifests/compliance.openshift.io_scansettings.yaml b/bundle/manifests/compliance.openshift.io_scansettings.yaml index aafe34e6b4..69bf3d951f 100644 --- a/bundle/manifests/compliance.openshift.io_scansettings.yaml +++ b/bundle/manifests/compliance.openshift.io_scansettings.yaml @@ -76,6 +76,13 @@ spec: rawResultStorage: description: Specifies settings that pertain to raw result storage. properties: + enabled: + default: true + description: |- + Specifies if raw scan results should be saved to persistent volumes. + This is useful for enabling compliance scans in environments that + don't have storage. Defaults to true. + type: boolean nodeSelector: additionalProperties: type: string diff --git a/cmd/manager/resultcollector.go b/cmd/manager/resultcollector.go index 54b69b081a..24c73fd709 100644 --- a/cmd/manager/resultcollector.go +++ b/cmd/manager/resultcollector.go @@ -78,6 +78,7 @@ type scapresultsConfig struct { Cert string Key string CA string + RawResultsOutput bool } func defineResultcollectorFlags(cmd *cobra.Command) { @@ -95,7 +96,7 @@ func defineResultcollectorFlags(cmd *cobra.Command) { cmd.Flags().String("tls-client-cert", "", "The path to the client and CA PEM cert bundle.") cmd.Flags().String("tls-client-key", "", "The path to the client PEM key.") cmd.Flags().String("tls-ca", "", "The path to the CA certificate.") - + cmd.Flags().Bool("raw-results-output", true, "Setting to true to upload raw arf result") flags := cmd.Flags() // Add flags registered by imported packages (e.g. glog and @@ -117,6 +118,7 @@ func parseConfig(cmd *cobra.Command) *scapresultsConfig { conf.CA = getValidStringArg(cmd, "tls-ca") conf.Timeout, _ = cmd.Flags().GetInt64("timeout") conf.ResultServerURI, _ = cmd.Flags().GetString("resultserveruri") + conf.RawResultsOutput, _ = cmd.Flags().GetBool("raw-results-output") // Set default if needed if conf.ResultServerURI == "" { conf.ResultServerURI = "http://" + conf.ScanName + "-rs:8080/" @@ -370,31 +372,36 @@ func uploadErrorConfigMap(errorMsg *resultFileContents, exitcode string, } func handleCompleteSCAPResults(exitcode string, scapresultsconf *scapresultsConfig, client *complianceCrClient) { - arfContents, err := readResultsFile(scapresultsconf.ArfFile, scapresultsconf.Timeout) - if err != nil { - cmdLog.Error(err, "Failed to read ARF file") - os.Exit(1) - } - defer arfContents.close() - xccdfContents, err := readResultsFile(scapresultsconf.XccdfFile, scapresultsconf.Timeout) if err != nil { cmdLog.Error(err, "Failed to read XCCDF file") os.Exit(1) } defer xccdfContents.close() - var wg sync.WaitGroup - wg.Add(2) - go func() { - serverUploadErr := uploadToResultServer(arfContents, scapresultsconf) - if serverUploadErr != nil { - cmdLog.Error(serverUploadErr, "Failed to upload results to server") + numWG := 1 + if scapresultsconf.RawResultsOutput { + numWG++ + } + wg.Add(numWG) + + if scapresultsconf.RawResultsOutput { + arfContents, err := readResultsFile(scapresultsconf.ArfFile, scapresultsconf.Timeout) + if err != nil { + cmdLog.Error(err, "Failed to read ARF file") os.Exit(1) } - cmdLog.Info("Uploaded to resultserver") - wg.Done() - }() + defer arfContents.close() + go func() { + serverUploadErr := uploadToResultServer(arfContents, scapresultsconf) + if serverUploadErr != nil { + cmdLog.Error(serverUploadErr, "Failed to upload results to server") + os.Exit(1) + } + cmdLog.Info("Uploaded to resultserver") + wg.Done() + }() + } go func() { cmUploadErr := uploadResultConfigMap(xccdfContents, exitcode, scapresultsconf, client) diff --git a/config/crd/bases/compliance.openshift.io_compliancescans.yaml b/config/crd/bases/compliance.openshift.io_compliancescans.yaml index 815a274268..2113435443 100644 --- a/config/crd/bases/compliance.openshift.io_compliancescans.yaml +++ b/config/crd/bases/compliance.openshift.io_compliancescans.yaml @@ -106,6 +106,13 @@ spec: rawResultStorage: description: Specifies settings that pertain to raw result storage. properties: + enabled: + default: true + description: |- + Specifies if raw scan results should be saved to persistent volumes. + This is useful for enabling compliance scans in environments that + don't have storage. Defaults to true. + type: boolean nodeSelector: additionalProperties: type: string diff --git a/config/crd/bases/compliance.openshift.io_compliancesuites.yaml b/config/crd/bases/compliance.openshift.io_compliancesuites.yaml index 19239096e5..8ac4d84432 100644 --- a/config/crd/bases/compliance.openshift.io_compliancesuites.yaml +++ b/config/crd/bases/compliance.openshift.io_compliancesuites.yaml @@ -125,6 +125,13 @@ spec: rawResultStorage: description: Specifies settings that pertain to raw result storage. properties: + enabled: + default: true + description: |- + Specifies if raw scan results should be saved to persistent volumes. + This is useful for enabling compliance scans in environments that + don't have storage. Defaults to true. + type: boolean nodeSelector: additionalProperties: type: string diff --git a/config/crd/bases/compliance.openshift.io_scansettings.yaml b/config/crd/bases/compliance.openshift.io_scansettings.yaml index 9ee475baa9..b0a84d80b7 100644 --- a/config/crd/bases/compliance.openshift.io_scansettings.yaml +++ b/config/crd/bases/compliance.openshift.io_scansettings.yaml @@ -76,6 +76,13 @@ spec: rawResultStorage: description: Specifies settings that pertain to raw result storage. properties: + enabled: + default: true + description: |- + Specifies if raw scan results should be saved to persistent volumes. + This is useful for enabling compliance scans in environments that + don't have storage. Defaults to true. + type: boolean nodeSelector: additionalProperties: type: string diff --git a/pkg/apis/compliance/v1alpha1/complianceremediation_types_test.go b/pkg/apis/compliance/v1alpha1/complianceremediation_types_test.go index c0320ab7ce..c9fbd5b708 100644 --- a/pkg/apis/compliance/v1alpha1/complianceremediation_types_test.go +++ b/pkg/apis/compliance/v1alpha1/complianceremediation_types_test.go @@ -1,13 +1,14 @@ package v1alpha1 import ( + "reflect" + . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" - "reflect" ) var _ = Describe("Testing ComplianceRemediation API", func() { diff --git a/pkg/apis/compliance/v1alpha1/compliancescan_types.go b/pkg/apis/compliance/v1alpha1/compliancescan_types.go index 17b3e437ac..634fc91081 100644 --- a/pkg/apis/compliance/v1alpha1/compliancescan_types.go +++ b/pkg/apis/compliance/v1alpha1/compliancescan_types.go @@ -136,6 +136,12 @@ type ComplianceScanType string // When changing the defaults, remember to change also the DefaultRawStorageSize and // DefaultStorageRotation constants type RawResultStorageSettings struct { + // Specifies if raw scan results should be saved to persistent volumes. + // This is useful for enabling compliance scans in environments that + // don't have storage. Defaults to true. + // +kubebuilder:validation:Default=true + // +kubebuilder:default=true + Enabled *bool `json:"enabled,omitempty"` // Specifies the amount of storage to ask for storing the raw results. Note that // if re-scans happen, the new results will also need to be stored. Defaults to 1Gi. // +kubebuilder:validation:Default=1Gi diff --git a/pkg/apis/compliance/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/compliance/v1alpha1/zz_generated.deepcopy.go index 2af88722d6..8b9deeff56 100644 --- a/pkg/apis/compliance/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/compliance/v1alpha1/zz_generated.deepcopy.go @@ -792,6 +792,11 @@ func (in *ProfilePayload) DeepCopy() *ProfilePayload { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RawResultStorageSettings) DeepCopyInto(out *RawResultStorageSettings) { *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } if in.StorageClassName != nil { in, out := &in.StorageClassName, &out.StorageClassName *out = new(string) diff --git a/pkg/controller/compliancescan/compliancescan_controller.go b/pkg/controller/compliancescan/compliancescan_controller.go index 971ee40d78..a8b5fc7fdd 100644 --- a/pkg/controller/compliancescan/compliancescan_controller.go +++ b/pkg/controller/compliancescan/compliancescan_controller.go @@ -257,6 +257,14 @@ func (r *ReconcileComplianceScan) validate(instance *compv1alpha1.ComplianceScan return false, err } + if instance.Spec.RawResultStorage.Enabled == nil { + instanceCopy := instance.DeepCopy() + trueValue := true + instanceCopy.Spec.RawResultStorage.Enabled = &trueValue + err := r.Client.Update(context.TODO(), instanceCopy) + return false, err + } + if len(instance.Spec.RawResultStorage.PVAccessModes) == 0 { instanceCopy := instance.DeepCopy() instanceCopy.Spec.RawResultStorage.PVAccessModes = defaultAccessMode @@ -436,26 +444,29 @@ func (r *ReconcileComplianceScan) phaseLaunchingHandler(h scanTypeHandler, logge return reconcile.Result{}, err } - if err = r.handleResultServerSecret(scan, logger); err != nil { - logger.Error(err, "Cannot create result server cert secret") - return reconcile.Result{}, err - } + if scan.Spec.RawResultStorage.Enabled != nil && *scan.Spec.RawResultStorage.Enabled { + if err = r.handleResultServerSecret(scan, logger); err != nil { + logger.Error(err, "Cannot create result server cert secret") + return reconcile.Result{}, err + } - if err = r.handleResultClientSecret(scan, logger); err != nil { - logger.Error(err, "Cannot create result Client cert secret") - return reconcile.Result{}, err - } + if err = r.handleResultClientSecret(scan, logger); err != nil { + logger.Error(err, "Cannot create result Client cert secret") + return reconcile.Result{}, err + } - if resume, err := r.handleRawResultsForScan(scan, logger); err != nil || !resume { - if err != nil { - logger.Error(err, "Cannot create the PersistentVolumeClaims") + if resume, err := r.handleRawResultsForScan(scan, logger); err != nil || !resume { + if err != nil { + logger.Error(err, "Cannot create the PersistentVolumeClaims") + } + return reconcile.Result{}, err + } + + if err = r.createResultServer(scan, logger); err != nil { + logger.Error(err, "Cannot create result server") + return reconcile.Result{}, err } - return reconcile.Result{}, err - } - if err = r.createResultServer(scan, logger); err != nil { - logger.Error(err, "Cannot create result server") - return reconcile.Result{}, err } if err = r.handleRuntimeKubeletConfig(scan, logger); err != nil { @@ -880,11 +891,12 @@ func (r *ReconcileComplianceScan) phaseDoneHandler(h scanTypeHandler, instance * } } else { // If we're done with the scan but we're not cleaning up just yet. - - // scale down resultserver so it's not still listening for requests. - if err := r.scaleDownResultServer(instance, logger); err != nil { - logger.Error(err, "Cannot scale down result server") - return reconcile.Result{}, err + if instance.Spec.RawResultStorage.Enabled != nil && *instance.Spec.RawResultStorage.Enabled { + // scale down resultserver so it's not still listening for requests. + if err := r.scaleDownResultServer(instance, logger); err != nil { + logger.Error(err, "Cannot scale down result server") + return reconcile.Result{}, err + } } } diff --git a/pkg/controller/compliancescan/compliancescan_controller_test.go b/pkg/controller/compliancescan/compliancescan_controller_test.go index f9ee89ede7..f9b11e2b5b 100644 --- a/pkg/controller/compliancescan/compliancescan_controller_test.go +++ b/pkg/controller/compliancescan/compliancescan_controller_test.go @@ -123,7 +123,7 @@ var _ = Describe("Testing compliancescan controller phases", func() { // logger = zapr.NewLogger(dev) logger = zapr.NewLogger(zap.NewNop()) objs := []runtime.Object{} - + trueValue := true // test instance compliancescaninstance = &compv1alpha1.ComplianceScan{ ObjectMeta: metav1.ObjectMeta{ @@ -133,6 +133,7 @@ var _ = Describe("Testing compliancescan controller phases", func() { ScanType: compv1alpha1.ScanTypeNode, ComplianceScanSettings: compv1alpha1.ComplianceScanSettings{ RawResultStorage: compv1alpha1.RawResultStorageSettings{ + Enabled: &trueValue, PVAccessModes: defaultAccessMode, Size: compv1alpha1.DefaultRawStorageSize, }, @@ -150,6 +151,7 @@ var _ = Describe("Testing compliancescan controller phases", func() { ScanType: compv1alpha1.ScanTypePlatform, ComplianceScanSettings: compv1alpha1.ComplianceScanSettings{ RawResultStorage: compv1alpha1.RawResultStorageSettings{ + Enabled: &trueValue, PVAccessModes: defaultAccessMode, Size: compv1alpha1.DefaultRawStorageSize, }, diff --git a/pkg/controller/compliancescan/resultserver.go b/pkg/controller/compliancescan/resultserver.go index e7b18aa1dc..fac8915051 100644 --- a/pkg/controller/compliancescan/resultserver.go +++ b/pkg/controller/compliancescan/resultserver.go @@ -282,3 +282,157 @@ func getResultServerName(instance *compv1alpha1.ComplianceScan) string { func getResultServerURI(instance *compv1alpha1.ComplianceScan) string { return "https://" + getResultServerName(instance) + fmt.Sprintf(":%d/", ResultServerPort) } + +func getRawResultsOutputValue(instance *compv1alpha1.ComplianceScan) string { + if instance.Spec.RawResultStorage.Enabled != nil && *instance.Spec.RawResultStorage.Enabled { + return "true" + } else { + return "false" + } +} + +func getLogCollectorVolumeMounts(instance *compv1alpha1.ComplianceScan) []corev1.VolumeMount { + if instance.Spec.RawResultStorage.Enabled != nil && *instance.Spec.RawResultStorage.Enabled { + return []corev1.VolumeMount{ + { + Name: "report-dir", + MountPath: "/reports", + ReadOnly: true, + }, + { + Name: "tls", + MountPath: "/etc/pki/tls", + ReadOnly: true, + }, + } + } else { + return []corev1.VolumeMount{ + { + Name: "report-dir", + MountPath: "/reports", + ReadOnly: true, + }, + } + } +} + +func getPlatformScannerPodVolumes(instance *compv1alpha1.ComplianceScan) []corev1.Volume { + mode := int32(0755) + volumeList := []corev1.Volume{ + { + Name: "report-dir", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + { + Name: "content-dir", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + { + Name: "tmp-dir", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + { + Name: "fetch-results", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + { + Name: scriptCmForScan(instance), + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: scriptCmForScan(instance), + }, + DefaultMode: &mode, + }, + }, + }, + } + if instance.Spec.RawResultStorage.Enabled != nil && *instance.Spec.RawResultStorage.Enabled { + volumeList = append(volumeList, corev1.Volume{ + Name: "tls", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: ClientCertPrefix + instance.Name, + }, + }, + }, + ) + } + return volumeList +} + +func getNodeScannerPodVolumes(instance *compv1alpha1.ComplianceScan, node *corev1.Node) []corev1.Volume { + mode := int32(0744) + kubeMode := int32(0600) + volumesList := []corev1.Volume{ + { + Name: "host", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/", + Type: &hostPathDir, + }, + }, + }, + { + Name: "report-dir", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + { + Name: "content-dir", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + { + Name: "tmp-dir", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + { + Name: scriptCmForScan(instance), + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: scriptCmForScan(instance), + }, + DefaultMode: &mode, + }, + }, + }, + { + Name: "kubeletconfig", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: getKubeletCMNameForScan(instance, node), + }, + DefaultMode: &kubeMode, + }, + }, + }, + } + if instance.Spec.RawResultStorage.Enabled != nil && *instance.Spec.RawResultStorage.Enabled { + volumesList = append(volumesList, corev1.Volume{ + Name: "tls", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: ClientCertPrefix + instance.Name, + }, + }, + }, + ) + } + return volumesList +} diff --git a/pkg/controller/compliancescan/scan.go b/pkg/controller/compliancescan/scan.go index 7a14dc88e9..b2b68f4896 100644 --- a/pkg/controller/compliancescan/scan.go +++ b/pkg/controller/compliancescan/scan.go @@ -67,9 +67,6 @@ func scanLimits(scanInstance *compv1alpha1.ComplianceScan, defaultMem, defaultCp } func newScanPodForNode(scanInstance *compv1alpha1.ComplianceScan, node *corev1.Node, logger logr.Logger) *corev1.Pod { - mode := int32(0744) - - kubeMode := int32(0600) podName := getPodForNodeName(scanInstance.Name, node.Name) cmName := getConfigMapForNodeName(scanInstance.Name, node.Name) @@ -182,6 +179,7 @@ func newScanPodForNode(scanInstance *compv1alpha1.ComplianceScan, node *corev1.N "--tls-client-cert=/etc/pki/tls/tls.crt", "--tls-client-key=/etc/pki/tls/tls.key", "--tls-ca=/etc/pki/tls/ca.crt", + "--raw-results-output=" + getRawResultsOutputValue(scanInstance), }, ImagePullPolicy: corev1.PullAlways, SecurityContext: &corev1.SecurityContext{ @@ -201,18 +199,7 @@ func newScanPodForNode(scanInstance *compv1alpha1.ComplianceScan, node *corev1.N corev1.ResourceCPU: resource.MustParse("100m"), }, }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "report-dir", - MountPath: "/reports", - ReadOnly: true, - }, - { - Name: "tls", - MountPath: "/etc/pki/tls", - ReadOnly: true, - }, - }, + VolumeMounts: getLogCollectorVolumeMounts(scanInstance), }, { Name: OpenSCAPScanContainerName, @@ -297,71 +284,12 @@ func newScanPodForNode(scanInstance *compv1alpha1.ComplianceScan, node *corev1.N HostNetwork: true, DNSPolicy: "ClusterFirstWithHostNet", RestartPolicy: corev1.RestartPolicyOnFailure, - Volumes: []corev1.Volume{ - { - Name: "host", - VolumeSource: corev1.VolumeSource{ - HostPath: &corev1.HostPathVolumeSource{ - Path: "/", - Type: &hostPathDir, - }, - }, - }, - { - Name: "report-dir", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }, - { - Name: "content-dir", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }, - { - Name: "tmp-dir", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }, - { - Name: scriptCmForScan(scanInstance), - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: scriptCmForScan(scanInstance), - }, - DefaultMode: &mode, - }, - }, - }, - { - Name: "kubeletconfig", - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: getKubeletCMNameForScan(scanInstance, node), - }, - DefaultMode: &kubeMode, - }, - }, - }, - { - Name: "tls", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: ClientCertPrefix + scanInstance.Name, - }, - }, - }, - }, + Volumes: getNodeScannerPodVolumes(scanInstance, node), }, } } func (r *ReconcileComplianceScan) newPlatformScanPod(scanInstance *compv1alpha1.ComplianceScan, logger logr.Logger) *corev1.Pod { - mode := int32(0755) podName := getPodForNodeName(scanInstance.Name, PlatformScanName) cmName := getConfigMapForNodeName(scanInstance.Name, PlatformScanName) podLabels := map[string]string{ @@ -513,6 +441,7 @@ func (r *ReconcileComplianceScan) newPlatformScanPod(scanInstance *compv1alpha1. "--tls-client-cert=/etc/pki/tls/tls.crt", "--tls-client-key=/etc/pki/tls/tls.key", "--tls-ca=/etc/pki/tls/ca.crt", + "--raw-results-output=" + getRawResultsOutputValue(scanInstance), }, ImagePullPolicy: corev1.PullAlways, SecurityContext: &corev1.SecurityContext{ @@ -532,18 +461,7 @@ func (r *ReconcileComplianceScan) newPlatformScanPod(scanInstance *compv1alpha1. corev1.ResourceCPU: resource.MustParse("100m"), }, }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "report-dir", - MountPath: "/reports", - ReadOnly: true, - }, - { - Name: "tls", - MountPath: "/etc/pki/tls", - ReadOnly: true, - }, - }, + VolumeMounts: getLogCollectorVolumeMounts(scanInstance), }, { Name: OpenSCAPScanContainerName, @@ -603,51 +521,7 @@ func (r *ReconcileComplianceScan) newPlatformScanPod(scanInstance *compv1alpha1. NodeSelector: r.schedulingInfo.Selector, Tolerations: r.schedulingInfo.Tolerations, RestartPolicy: corev1.RestartPolicyOnFailure, - Volumes: []corev1.Volume{ - { - Name: "report-dir", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }, - { - Name: "content-dir", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }, - { - Name: "tmp-dir", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }, - { - Name: "fetch-results", - VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, - }, - }, - { - Name: scriptCmForScan(scanInstance), - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: scriptCmForScan(scanInstance), - }, - DefaultMode: &mode, - }, - }, - }, - { - Name: "tls", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: ClientCertPrefix + scanInstance.Name, - }, - }, - }, - }, + Volumes: getPlatformScannerPodVolumes(scanInstance), }, } } diff --git a/tests/e2e/parallel/main_test.go b/tests/e2e/parallel/main_test.go index b05d0130a2..5da3c4b99f 100644 --- a/tests/e2e/parallel/main_test.go +++ b/tests/e2e/parallel/main_test.go @@ -19,6 +19,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" + "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -2131,6 +2132,73 @@ func TestScheduledSuitePriorityClass(t *testing.T) { } } +func TestScheduledSuiteNoStorage(t *testing.T) { + t.Parallel() + f := framework.Global + suiteName := "test-scheduled-suite-no-storage" + workerScanName := fmt.Sprintf("%s-workers-scan", suiteName) + selectWorkers := map[string]string{ + "node-role.kubernetes.io/worker": "", + } + + falseValue := false + testSuite := &compv1alpha1.ComplianceSuite{ + ObjectMeta: metav1.ObjectMeta{ + Name: suiteName, + Namespace: f.OperatorNamespace, + }, + Spec: compv1alpha1.ComplianceSuiteSpec{ + ComplianceSuiteSettings: compv1alpha1.ComplianceSuiteSettings{ + AutoApplyRemediations: false, + }, + Scans: []compv1alpha1.ComplianceScanSpecWrapper{ + { + Name: workerScanName, + ComplianceScanSpec: compv1alpha1.ComplianceScanSpec{ + ContentImage: contentImagePath, + Profile: "xccdf_org.ssgproject.content_profile_moderate", + Content: framework.RhcosContentFile, + Rule: "xccdf_org.ssgproject.content_rule_no_netrc_files", + NodeSelector: selectWorkers, + ComplianceScanSettings: compv1alpha1.ComplianceScanSettings{ + RawResultStorage: compv1alpha1.RawResultStorageSettings{ + Enabled: &falseValue, + }, + Debug: true, + }, + }, + }, + }, + }, + } + + err := f.Client.Create(context.TODO(), testSuite, nil) + if err != nil { + t.Fatal(err) + } + defer f.Client.Delete(context.TODO(), testSuite) + + pvcList := &corev1.PersistentVolumeClaimList{} + err = f.Client.List(context.TODO(), pvcList, client.InNamespace(f.OperatorNamespace), client.MatchingLabels(map[string]string{ + compv1alpha1.ComplianceScanLabel: workerScanName, + })) + if err != nil { + t.Fatal(err) + } + if len(pvcList.Items) > 0 { + for _, pvc := range pvcList.Items { + t.Fatalf("Found unexpected PVC %s", pvc.Name) + } + t.Fatal("Expected not to find PVC associated with the scan.") + } + + // Ensure that all the scans in the suite have finished and are marked as Done + err = f.WaitForSuiteScansStatus(f.OperatorNamespace, suiteName, compv1alpha1.PhaseDone, compv1alpha1.ResultCompliant) + if err != nil { + t.Fatal(err) + } +} + func TestScheduledSuiteInvalidPriorityClass(t *testing.T) { t.Parallel() f := framework.Global @@ -2461,6 +2529,267 @@ func TestScanSettingBinding(t *testing.T) { } } +} +func TestScanSettingBindingNoStorage(t *testing.T) { + t.Parallel() + f := framework.Global + objName := framework.GetObjNameFromTest(t) + var baselineImage = fmt.Sprintf("%s:%s", brokenContentImagePath, "kubeletconfig") + const requiredRule = "kubelet-eviction-thresholds-set-soft-imagefs-available" + pbName := framework.GetObjNameFromTest(t) + prefixName := func(profName, ruleBaseName string) string { return profName + "-" + ruleBaseName } + + ocpPb, err := f.CreateProfileBundle(pbName, baselineImage, framework.OcpContentFile) + if err != nil { + t.Fatal(err) + } + defer f.Client.Delete(context.TODO(), ocpPb) + if err := f.WaitForProfileBundleStatus(pbName, compv1alpha1.DataStreamValid); err != nil { + t.Fatal(err) + } + + // Check that if the rule we are going to test is there + requiredRuleName := prefixName(pbName, requiredRule) + err, found := framework.Global.DoesRuleExist(f.OperatorNamespace, requiredRuleName) + if err != nil { + t.Fatal(err) + } else if !found { + t.Fatalf("Expected rule %s not found", requiredRuleName) + } + + suiteName := "storage-test-node" + scanSettingBindingName := suiteName + + tp := &compv1alpha1.TailoredProfile{ + ObjectMeta: metav1.ObjectMeta{ + Name: suiteName, + Namespace: f.OperatorNamespace, + Annotations: map[string]string{ + compv1alpha1.DisableOutdatedReferenceValidation: "true", + }, + }, + Spec: compv1alpha1.TailoredProfileSpec{ + Title: "storage-test", + Description: "A test tailored profile to test storage settings", + ManualRules: []compv1alpha1.RuleReferenceSpec{ + { + Name: prefixName(pbName, requiredRule), + Rationale: "To be tested", + }, + }, + }, + } + + createTPErr := f.Client.Create(context.TODO(), tp, nil) + if createTPErr != nil { + t.Fatal(createTPErr) + } + defer f.Client.Delete(context.TODO(), tp) + scanSettingNoStorageName := objName + "-setting-no-storage" + falseValue := false + trueValue := true + scanSettingWithStorageName := objName + "-setting-with-storage" + scanSettingNoStorage := compv1alpha1.ScanSetting{ + ObjectMeta: metav1.ObjectMeta{ + Name: scanSettingNoStorageName, + Namespace: f.OperatorNamespace, + }, + ComplianceSuiteSettings: compv1alpha1.ComplianceSuiteSettings{ + AutoApplyRemediations: false, + }, + ComplianceScanSettings: compv1alpha1.ComplianceScanSettings{ + Debug: true, + RawResultStorage: compv1alpha1.RawResultStorageSettings{ + Enabled: &falseValue, + }, + }, + Roles: []string{"master", "worker"}, + } + + scanSettingWithStorage := compv1alpha1.ScanSetting{ + ObjectMeta: metav1.ObjectMeta{ + Name: scanSettingWithStorageName, + Namespace: f.OperatorNamespace, + }, + ComplianceSuiteSettings: compv1alpha1.ComplianceSuiteSettings{ + AutoApplyRemediations: false, + }, + ComplianceScanSettings: compv1alpha1.ComplianceScanSettings{ + Debug: true, + RawResultStorage: compv1alpha1.RawResultStorageSettings{ + Enabled: &trueValue, + }, + }, + Roles: []string{"master", "worker"}, + } + + if err := f.Client.Create(context.TODO(), &scanSettingNoStorage, nil); err != nil { + t.Fatal(err) + } + defer f.Client.Delete(context.TODO(), &scanSettingNoStorage) + + if err := f.Client.Create(context.TODO(), &scanSettingWithStorage, nil); err != nil { + t.Fatal(err) + } + defer f.Client.Delete(context.TODO(), &scanSettingWithStorage) + + scanSettingBinding := compv1alpha1.ScanSettingBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: scanSettingBindingName, + Namespace: f.OperatorNamespace, + }, + Profiles: []compv1alpha1.NamedObjectReference{ + { + APIGroup: "compliance.openshift.io/v1alpha1", + Kind: "TailoredProfile", + Name: suiteName, + }, + }, + SettingsRef: &compv1alpha1.NamedObjectReference{ + Name: scanSettingNoStorage.Name, + Kind: "ScanSetting", + APIGroup: "compliance.openshift.io/v1alpha1", + }, + } + + if err := f.Client.Create(context.TODO(), &scanSettingBinding, nil); err != nil { + t.Fatal(err) + } + defer f.Client.Delete(context.TODO(), &scanSettingBinding) + + // Wait until the suite finishes, thus verifying the suite exists + err = f.WaitForSuiteScansStatus(f.OperatorNamespace, scanSettingBindingName, compv1alpha1.PhaseDone, compv1alpha1.ResultNonCompliant) + if err != nil { + t.Fatal(err) + } + + scanKey := types.NamespacedName{Namespace: f.OperatorNamespace, Name: suiteName + "-master"} + scan := &compv1alpha1.ComplianceScan{} + if err := f.Client.Get(context.TODO(), scanKey, scan); err != nil { + t.Fatal(err) + } + + if scan.Spec.RawResultStorage.Enabled != nil && *scan.Spec.RawResultStorage.Enabled { + t.Fatal("Expected that the scan does not have raw result storage enabled") + } + + pvcList := &corev1.PersistentVolumeClaimList{} + err = f.Client.List(context.TODO(), pvcList, client.InNamespace(f.OperatorNamespace), client.MatchingLabels(map[string]string{ + compv1alpha1.ComplianceScanLabel: scan.Name, + })) + if err != nil { + t.Fatal(err) + } + if len(pvcList.Items) > 0 { + for _, pvc := range pvcList.Items { + t.Fatalf("Found unexpected PVC %s", pvc.Name) + } + t.Fatal("Expected not to find PVC associated with the scan.") + } + // let's delete the scan setting binding + if err := f.Client.Delete(context.TODO(), &scanSettingBinding); err != nil { + t.Fatal(err) + } + + // let's create a new scan setting binding with the with storage setting + scanSettingBinding = compv1alpha1.ScanSettingBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: scanSettingBindingName, + Namespace: f.OperatorNamespace, + }, + Profiles: []compv1alpha1.NamedObjectReference{ + { + APIGroup: "compliance.openshift.io/v1alpha1", + Kind: "TailoredProfile", + Name: suiteName, + }, + }, + SettingsRef: &compv1alpha1.NamedObjectReference{ + Name: scanSettingWithStorage.Name, + Kind: "ScanSetting", + APIGroup: "compliance.openshift.io/v1alpha1", + }, + } + + if err := f.Client.Create(context.TODO(), &scanSettingBinding, nil); err != nil { + t.Fatal(err) + } + defer f.Client.Delete(context.TODO(), &scanSettingBinding) + + // Wait until the suite finishes, thus verifying the suite exists + err = f.WaitForSuiteScansStatus(f.OperatorNamespace, scanSettingBindingName, compv1alpha1.PhaseDone, compv1alpha1.ResultNonCompliant) + if err != nil { + t.Fatal(err) + } + + scan = &compv1alpha1.ComplianceScan{} + if err := f.Client.Get(context.TODO(), scanKey, scan); err != nil { + t.Fatal(err) + } + + if scan.Spec.RawResultStorage.Enabled != nil && *scan.Spec.RawResultStorage.Enabled == false { + t.Fatal("Expected that the scan has raw result storage enabled") + } + + pvcList = &corev1.PersistentVolumeClaimList{} + err = f.Client.List(context.TODO(), pvcList, client.InNamespace(f.OperatorNamespace), client.MatchingLabels(map[string]string{ + compv1alpha1.ComplianceScanLabel: scan.Name, + })) + if err != nil { + t.Fatal(err) + } + if len(pvcList.Items) == 0 { + t.Fatal("Expected to find PVC associated with the scan.") + } + t.Logf("Found PVC %s", pvcList.Items[0].Name) + t.Logf("Succeeded to create PVC associated with the scan.") + + // let's update the scan setting binding to use the no storage setting + ssb := &compv1alpha1.ScanSettingBinding{} + if err := f.Client.Get(context.TODO(), types.NamespacedName{Namespace: f.OperatorNamespace, Name: scanSettingBindingName}, ssb); err != nil { + t.Fatal("Expected to find the scan setting binding, but got error: ", err) + } + ssb.SettingsRef.Name = scanSettingNoStorage.Name + if err := f.Client.Update(context.TODO(), ssb); err != nil { + t.Fatal(err) + } + + // let's rerun the scan + err = f.ReRunScan(scan.Name, f.OperatorNamespace) + if err != nil { + t.Fatal(err) + } + // wait for scan to finish + err = f.WaitForSuiteScansStatus(f.OperatorNamespace, scanSettingBindingName, compv1alpha1.PhaseDone, compv1alpha1.ResultNonCompliant) + if err != nil { + t.Fatal(err) + } + + // get the scan again + scan = &compv1alpha1.ComplianceScan{} + if err := f.Client.Get(context.TODO(), scanKey, scan); err != nil { + t.Fatal(err) + } + + // make sure enabled is false + if scan.Spec.RawResultStorage.Enabled != nil && *scan.Spec.RawResultStorage.Enabled == true { + t.Fatal("Expected that the scan does not have raw result storage enabled") + } + + // let's check that the PVC should be there and still associated with the scan + pvcList = &corev1.PersistentVolumeClaimList{} + err = f.Client.List(context.TODO(), pvcList, client.InNamespace(f.OperatorNamespace), client.MatchingLabels(map[string]string{ + compv1alpha1.ComplianceScanLabel: scan.Name, + })) + if err != nil { + t.Fatal(err) + } + if len(pvcList.Items) == 0 { + t.Fatal("Expected to find PVC associated with the scan.") + } + t.Logf("Found PVC %s", pvcList.Items[0].Name) + t.Logf("Succeeded to check that the PVC is still there.") + } func TestScanSettingBindingTailoringManyEnablingRulePass(t *testing.T) {