pkg/localvolume/localvolume.go (74 lines of code) (raw):
// Copyright 2024 Google LLC
//
// 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 localvolume
import (
"fmt"
"os"
"path/filepath"
"strings"
"k8s.io/klog/v2"
"k8s.io/mount-utils"
"k8s.io/utils/exec"
)
const (
fsType = "ext4"
procMounts = "/proc/mounts"
)
// LocalVolume represents a local volume to the CSI node driver. It should have a
// path that locates the volume in the local filesystem. This must be bind-mountable.
type LocalVolume interface {
Path() string
}
// deviceVolume is a local volume from a device.
type deviceVolume struct {
devicePath string
mountPath string
}
var _ LocalVolume = &deviceVolume{}
// NewDeviceVolume creates a local volume from a device. The device will be
// formatted if necessary and mounted at the specified location. If the device
// is already mounted to mountPath, the existing mount is returned.
func NewFromDevice(devicePath, mountPath string) (LocalVolume, error) {
actualDevice, err := filepath.EvalSymlinks(devicePath)
if err != nil {
return nil, fmt.Errorf("Cannot resolve %s: %w", devicePath, err)
}
mounts, err := os.ReadFile(procMounts)
if err != nil {
return nil, fmt.Errorf("Cannot read %s: %w", procMounts, err)
}
for _, line := range strings.Split(string(mounts), "\n") {
if strings.Contains(line, mountPath) {
if !strings.Contains(line, actualDevice) {
return nil, fmt.Errorf("Already mounted, but not to expected device %s: %s", actualDevice, line)
}
klog.Infof("Found %s already mounted at %s", devicePath, mountPath)
return &deviceVolume{
devicePath,
mountPath,
}, nil
}
}
if err := os.MkdirAll(mountPath, 0750); err != nil {
return nil, fmt.Errorf("Couldn't create mount point: %w", err)
}
mounter := &mount.SafeFormatAndMount{
Interface: mount.New(""),
Exec: exec.New(),
}
if err := mounter.FormatAndMount(devicePath, mountPath, fsType, nil); err != nil {
return nil, fmt.Errorf("cannot format %s to %s: %w", devicePath, mountPath, err)
}
return &deviceVolume{
devicePath,
mountPath,
}, nil
}
func (v *deviceVolume) Path() string {
return v.mountPath
}
// pathVolume is a local volume from a path.
type pathVolume struct {
path string
}
var _ LocalVolume = &pathVolume{}
// NewFromPath creates a local volume at a path.
func NewFromPath(path string) (LocalVolume, error) {
if _, err := os.Stat(path); err != nil {
return nil, err
}
return &pathVolume{path: path}, nil
}
func (v *pathVolume) Path() string {
return v.path
}