Skip to content

Commit

Permalink
mount: (pre-)merge mounting code
Browse files Browse the repository at this point in the history
  • Loading branch information
jmxnzo committed Jan 22, 2025
1 parent ab984fb commit 0ca6bdf
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 113 deletions.
3 changes: 2 additions & 1 deletion coordinator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/edgelesssys/contrast/internal/grpc/atlscredentials"
"github.com/edgelesssys/contrast/internal/logger"
"github.com/edgelesssys/contrast/internal/meshapi"
"github.com/edgelesssys/contrast/internal/mount"
"github.com/edgelesssys/contrast/internal/userapi"
grpcprometheus "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
"github.com/prometheus/client_golang/prometheus"
Expand Down Expand Up @@ -56,7 +57,7 @@ func run() (retErr error) {

logger.Info("Coordinator started")

if err := setupMount(context.Background(), logger); err != nil {
if err := mount.SetupMount(context.Background(), logger, "/dev/csi0", "/mnt/state"); err != nil {
return fmt.Errorf("setting up mount: %w", err)
}

Expand Down
112 changes: 0 additions & 112 deletions coordinator/mount.go

This file was deleted.

118 changes: 118 additions & 0 deletions internal/mount/mount.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright 2024 Edgeless Systems GmbH
// SPDX-License-Identifier: AGPL-3.0-only

package mount

import (
"context"
"errors"
"fmt"
"log/slog"
"os"
"os/exec"
"strconv"
"strings"
)

// SetupMount formats the device to ext4 in case it is not ext4 formatted and mounts it to the provided mount point.
func SetupMount(ctx context.Context, logger *slog.Logger, devPath, mountPoint string) error {
blk, err := blkid(ctx, devPath)
if errors.Is(err, errNotIdentified) {
logger.Info("device not identified, formatting", "device", devPath)
if err := mkfsExt4(ctx, devPath); err != nil {
return err
}
} else if err != nil {
return err
} else if blk.Type != "ext4" {
logger.Info("device is not ext4, formatting", "device", devPath)
if err := mkfsExt4(ctx, devPath); err != nil {
return err
}
}

if err := mount(ctx, devPath, mountPoint); err != nil {
return err
}
logger.Info("device mounted successfully", "dev", devPath, "mountPoint", mountPoint)

return nil
}

// blk holds the main attributes of a block device used by blkid system executable command.
type blk struct {
DevName string
UUID string
BlockSize int
Type string
}

var errNotIdentified = errors.New("blkid did not identify the device")

// blkid creates a blk struct of the device located at the provided devPath.
func blkid(ctx context.Context, devPath string) (*blk, error) {
cmd := exec.CommandContext(ctx, "blkid", "-o", "export", devPath)
out, err := cmd.CombinedOutput()
var exitErr *exec.ExitError
if errors.As(err, &exitErr) && exitErr.ExitCode() == 2 {
// See man page, sec EXIT STATUS.
return nil, errNotIdentified
} else if err != nil {
return nil, fmt.Errorf("blkid: %w, output: %q", err, out)
}
return parseBlkidCommand(out)
}

// parseBlkidCommand parses the output of the blkid system command to rerpresentative Blkid struct.
func parseBlkidCommand(out []byte) (*blk, error) {
lines := strings.Split(string(out), "\n")
b := &blk{}

for _, line := range lines {
if line == "" {
continue
}
key, value, ok := strings.Cut(line, "=")
if !ok {
return nil, fmt.Errorf("parsing blkid output line %q", line)
}
switch key {
case "DEVNAME":
b.DevName = value
case "UUID":
b.UUID = value
case "TYPE":
b.Type = value
case "BLOCK_SIZE":
blockSize, err := strconv.Atoi(value)
if err != nil {
return nil, fmt.Errorf("parsing BLOCK_SIZE of blkid output %q: %w", value, err)
}
b.BlockSize = blockSize
}
}
return b, nil
}

// mkfsExt4 wraps the mkfs.ext4 command and creates an ext4 file system on the device.
func mkfsExt4(ctx context.Context, devPath string) error {
cmd := exec.CommandContext(ctx, "mkfs.ext4", devPath)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("mkfs.ext4: %w, output: %q", err, out)
}
return nil
}

// mount wraps the mount command and attaches the device to the specified mountPoint.
func mount(ctx context.Context, devPath, mountPoint string) error {
if err := os.MkdirAll(mountPoint, 0o755); err != nil {
return fmt.Errorf("mkdir: %w", err)
}
cmd := exec.CommandContext(ctx, "mount", devPath, mountPoint)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("mount: %w, output: %q", err, out)
}
return nil
}
55 changes: 55 additions & 0 deletions internal/mount/mount_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2024 Edgeless Systems GmbH
// SPDX-License-Identifier: AGPL-3.0-only

package mount

import (
"fmt"
"testing"

"github.com/stretchr/testify/require"
)

func TestParseBlkidCommand(t *testing.T) {
blk := &blk{
DevName: "/dev/nvme1n1p1",
UUID: "f3e74cbb-4b6c-442b-8cd1-d12e3fc7adcd",
BlockSize: 1,
Type: "ntfs",
}

blocks := map[string]struct {
outFormatString string
expError bool
}{
"valid output": {
outFormatString: "DEVNAME=%s\nUUID=%s\nBLOCK_SIZE=%d\nTYPE=%s\n",
expError: false,
},
"no equal sign": {
outFormatString: "No-Equal-Sign\n",
expError: true,
},
"block size is string": {
outFormatString: "BLOCK_SIZE=isString\n",
expError: true,
},
}

for name, block := range blocks {
t.Run(name, func(t *testing.T) {
parsedBlock, err := parseBlkidCommand(func(formatString string) []byte {
return []byte(fmt.Sprintf(formatString,
blk.DevName, blk.UUID, blk.BlockSize, blk.Type))
}(block.outFormatString))

if block.expError {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, blk, parsedBlock)

}
})
}
}

0 comments on commit 0ca6bdf

Please sign in to comment.