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

Commit

Permalink
Mount attached volume from CLI (#90)
Browse files Browse the repository at this point in the history
  • Loading branch information
fishkerez authored Mar 5, 2023
1 parent cc92e9e commit 94afffe
Show file tree
Hide file tree
Showing 13 changed files with 433 additions and 94 deletions.
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 {
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) {
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

0 comments on commit 94afffe

Please sign in to comment.