Skip to content
This repository has been archived by the owner on Oct 14, 2024. It is now read-only.

Mount attached volume from CLI #90

Merged
merged 10 commits into from
Mar 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Dockerfile.cli
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ RUN --mount=type=cache,target=/go/pkg/mod \

FROM ${VMCLARITY_TOOLS_BASE}

RUN apk add util-linux

WORKDIR /app

COPY --from=builder /build/cli/cli ./vmclarity-cli
Expand Down
79 changes: 79 additions & 0 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ import (
"os"

"github.com/ghodss/yaml"
"github.com/openclarity/kubeclarity/shared/pkg/utils"
uuid "github.com/satori/go.uuid"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/openclarity/vmclarity/cli/pkg"
"github.com/openclarity/vmclarity/cli/pkg/mount"
"github.com/openclarity/vmclarity/shared/pkg/families"
"github.com/openclarity/vmclarity/shared/pkg/families/exploits"
"github.com/openclarity/vmclarity/shared/pkg/families/results"
Expand All @@ -42,6 +45,12 @@ var (

server string
scanResultID string
mountVolume bool
)

const (
fsTypeExt4 = "ext4"
fsTypeXFS = "xfs"
)

// rootCmd represents the base command when called without any subcommands.
Expand All @@ -53,6 +62,15 @@ var rootCmd = &cobra.Command{
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
logger.Infof("Running...")

if mountVolume {
mountPoints, err := mountAttachedVolume()
if err != nil {
return fmt.Errorf("failed to mount attached volume: %v", err)
}
setMountPointsForFamiliesInput(mountPoints, config)
}

var exporter *Exporter
if server != "" {
exp, err := CreateExporter()
Expand Down Expand Up @@ -193,6 +211,7 @@ func init() {
rootCmd.PersistentFlags().StringVar(&output, "output", "", "set file path output (default: stdout)")
rootCmd.PersistentFlags().StringVar(&server, "server", "", "VMClarity server to export scan results to, for example: http://localhost:9999/api")
rootCmd.PersistentFlags().StringVar(&scanResultID, "scan-result-id", "", "the ScanResult ID to export the scan results to")
rootCmd.PersistentFlags().BoolVar(&mountVolume, "mount-attached-volume", false, "discover for an attached volume and mount it before the scan")

// TODO(sambetts) we may have to change this to our own validation when
// we add the CI/CD scenario and there isn't an existing scan-result-id
Expand Down Expand Up @@ -266,3 +285,63 @@ func Output(bytes []byte, outputPrefix string) error {

return nil
}

func isSupportedFS(fs string) bool {
akpsgit marked this conversation as resolved.
Show resolved Hide resolved
switch fs {
case fsTypeExt4, fsTypeXFS:
return true
}
return false
}

func setMountPointsForFamiliesInput(mountPoints []string, familiesConfig *families.Config) *families.Config {
// update families inputs with the mount point as rootfs
for _, mountDir := range mountPoints {
if familiesConfig.SBOM.Enabled {
familiesConfig.SBOM.Inputs = append(familiesConfig.SBOM.Inputs, sbom.Input{
Input: mountDir,
InputType: string(utils.ROOTFS),
})
}
if familiesConfig.Vulnerabilities.Enabled {
if familiesConfig.SBOM.Enabled {
familiesConfig.Vulnerabilities.InputFromSbom = true
} else {
familiesConfig.Vulnerabilities.Inputs = append(familiesConfig.Vulnerabilities.Inputs, vulnerabilities.Input{
Input: mountDir,
InputType: string(utils.ROOTFS),
})
}
}
if familiesConfig.Secrets.Enabled {
familiesConfig.Secrets.Inputs = append(familiesConfig.Secrets.Inputs, secrets.Input{
Input: mountDir,
InputType: string(utils.ROOTFS),
})
}
}
return familiesConfig
}

func mountAttachedVolume() ([]string, error) {
var mountPoints []string

devices, err := mount.ListBlockDevices()
if err != nil {
return nil, fmt.Errorf("failed to list block devices: %v", err)
}
for _, device := range devices {
// if the device is not mounted and of a supported filesystem type,
// we assume it belongs to the attached volume, so we mount it.
if device.MountPoint == "" && isSupportedFS(device.FilesystemType) {
akpsgit marked this conversation as resolved.
Show resolved Hide resolved
mountDir := "/mnt/snapshot" + uuid.NewV4().String()

if err := device.Mount(mountDir); err != nil {
return nil, fmt.Errorf("failed to mount device: %v", err)
}
logger.Infof("Mounted device %v on %v", device.DeviceName, mountDir)
mountPoints = append(mountPoints, mountDir)
}
}
return mountPoints, nil
}
158 changes: 158 additions & 0 deletions cli/cmd/root_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// Copyright © 2022 Cisco Systems, Inc. and its affiliates.
// All rights reserved.
//
// 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.

package cmd

import (
"reflect"
"testing"

"github.com/openclarity/kubeclarity/shared/pkg/utils"

"github.com/openclarity/vmclarity/shared/pkg/families"
"github.com/openclarity/vmclarity/shared/pkg/families/sbom"
"github.com/openclarity/vmclarity/shared/pkg/families/secrets"
"github.com/openclarity/vmclarity/shared/pkg/families/vulnerabilities"
)

func Test_isSupportedFS(t *testing.T) {
type args struct {
fs string
}
tests := []struct {
name string
args args
want bool
}{
{
name: "supported ext4",
args: args{
fs: fsTypeExt4,
},
want: true,
},
{
name: "supported xfs",
args: args{
fs: fsTypeXFS,
},
want: true,
},
{
name: "not supported btrfs",
args: args{
fs: "btrfs",
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := isSupportedFS(tt.args.fs); got != tt.want {
t.Errorf("isSupportedFS() = %v, want %v", got, tt.want)
}
})
}
}

func Test_setMountPointsForFamiliesInput(t *testing.T) {
type args struct {
mountPoints []string
familiesConfig *families.Config
}
tests := []struct {
name string
args args
want *families.Config
}{
{
name: "sbom, vuls and secrets are enabled",
args: args{
mountPoints: []string{"/mnt/snapshot1"},
familiesConfig: &families.Config{
SBOM: sbom.Config{
Enabled: true,
Inputs: nil,
},
Vulnerabilities: vulnerabilities.Config{
Enabled: true,
Inputs: nil,
InputFromSbom: false,
},
Secrets: secrets.Config{
Enabled: true,
Inputs: nil,
},
},
},
want: &families.Config{
SBOM: sbom.Config{
Enabled: true,
Inputs: []sbom.Input{
{
Input: "/mnt/snapshot1",
InputType: string(utils.ROOTFS),
},
},
},
Vulnerabilities: vulnerabilities.Config{
Enabled: true,
InputFromSbom: true,
},
Secrets: secrets.Config{
Enabled: true,
Inputs: []secrets.Input{
{
Input: "/mnt/snapshot1",
InputType: string(utils.ROOTFS),
},
},
},
},
},
{
name: "only vuls enabled",
args: args{
mountPoints: []string{"/mnt/snapshot1"},
familiesConfig: &families.Config{
Vulnerabilities: vulnerabilities.Config{
Enabled: true,
Inputs: nil,
InputFromSbom: false,
},
},
},
want: &families.Config{
Vulnerabilities: vulnerabilities.Config{
Enabled: true,
Inputs: []vulnerabilities.Input{
{
Input: "/mnt/snapshot1",
InputType: string(utils.ROOTFS),
},
},
InputFromSbom: false,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := setMountPointsForFamiliesInput(tt.args.mountPoints, tt.args.familiesConfig); !reflect.DeepEqual(got, tt.want) {
t.Errorf("setMountPointsForFamiliesInput() = %v, want %v", got, tt.want)
}
})
}
}
Loading