Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CON-9040 driver can accidentally format existing volume #478

Merged
Merged
Show file tree
Hide file tree
Changes from 6 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: 1 addition & 1 deletion cmd/do-csi-plugin/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

FROM alpine:3.16
FROM amd64/alpine:3.16

# e2fsprogs-extra is required for resize2fs used for the resize operation
# blkid: block device identification tool from util-linux
Expand Down
2 changes: 2 additions & 0 deletions cmd/do-csi-plugin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func main() {
defaultVolumesPageSize = flag.Uint("default-volumes-page-size", 0, "The default page size used when paging through volumes results (default: do not specify and let the DO API choose)")
doAPIRateLimitQPS = flag.Float64("do-api-rate-limit", 0, "Impose QPS rate limit on DigitalOcean API usage (default: do not rate limit)")
version = flag.Bool("version", false, "Print the version and exit.")
timoreimann marked this conversation as resolved.
Show resolved Hide resolved
validateAttachment = flag.Bool("validate-attachment", false, "Validate if the attachment is in a running state.")
llDrLove marked this conversation as resolved.
Show resolved Hide resolved
)
flag.Parse()

Expand All @@ -62,6 +63,7 @@ func main() {
DebugAddr: *debugAddr,
DefaultVolumesPageSize: *defaultVolumesPageSize,
DOAPIRateLimitQPS: *doAPIRateLimitQPS,
ValidateAttachment: *validateAttachment,
})
if err != nil {
log.Fatalln(err)
Expand Down
2 changes: 2 additions & 0 deletions driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ type Driver struct {
doTag string
isController bool
defaultVolumesPageSize uint
validateAttachment bool

srv *grpc.Server
httpSrv *http.Server
Expand Down Expand Up @@ -100,6 +101,7 @@ type NewDriverParams struct {
DebugAddr string
DefaultVolumesPageSize uint
DOAPIRateLimitQPS float64
ValidateAttachment bool
}

// NewDriver returns a CSI plugin that contains the necessary gRPC
Expand Down
4 changes: 4 additions & 0 deletions driver/driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,10 @@ func (f *fakeMounter) GetDeviceName(_ mount.Interface, mountPath string) (string
return "", nil
}

func (f *fakeMounter) IsRunning(av iAttachmentValidator, source string) bool {
return true
}

func (f *fakeMounter) IsFormatted(source string) (bool, error) {
return true, nil
}
Expand Down
3 changes: 3 additions & 0 deletions driver/mockgen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package driver

//go:generate mockgen -source=mounter.go -destination=mounter_mock.go -package=driver
60 changes: 60 additions & 0 deletions driver/mounter.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,25 @@ import (
kexec "k8s.io/utils/exec"
)

const (
runningState = "running"
)

type attachmentValidator struct{}

func (av *attachmentValidator) readFile(name string) ([]byte, error) {
return os.ReadFile(name)
}

func (av *attachmentValidator) evalSymlinks(path string) (string, error) {
return filepath.EvalSymlinks(path)
}

type iAttachmentValidator interface {
llDrLove marked this conversation as resolved.
Show resolved Hide resolved
readFile(name string) ([]byte, error)
evalSymlinks(path string) (string, error)
}

type findmntResponse struct {
FileSystems []fileSystem `json:"filesystems"`
}
Expand Down Expand Up @@ -67,6 +86,9 @@ type Mounter interface {
// Unmount unmounts the given target
Unmount(target string) error

// IsRunning checks whether the source device is in the running state.
IsRunning(av iAttachmentValidator, source string) bool
llDrLove marked this conversation as resolved.
Show resolved Hide resolved

// IsFormatted checks whether the source device is formatted or not. It
// returns true if the source device is already formatted.
IsFormatted(source string) (bool, error)
Expand Down Expand Up @@ -208,6 +230,44 @@ func (m *mounter) Unmount(target string) error {
return mount.CleanupMountPoint(target, m.kMounter, true)
}

func (m *mounter) IsRunning(av iAttachmentValidator, source string) bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make iAttachmentValidator a member/field of mounter? This would hide the dependency injection more elegantly and keep the IsRunning signature simpler.

out, err := av.evalSymlinks(source)
if err != nil {
m.log.WithFields(logrus.Fields{
"source": source,
}).Errorf("error evaluating the sym link, err: %v", err)
llDrLove marked this conversation as resolved.
Show resolved Hide resolved

return false
}

_, file := filepath.Split(out)
llDrLove marked this conversation as resolved.
Show resolved Hide resolved
fileContent, err := av.readFile(fmt.Sprintf("/sys/class/block/%s/device/state", file))
if err != nil {
m.log.WithFields(logrus.Fields{
"source": source,
"file": file,
}).Errorf("error reading the device state file, err: %v", err)

return false
}

m.log.WithFields(logrus.Fields{
"source": source,
"fileContent": string(fileContent),
}).Info("state file content")

if string(fileContent) != fmt.Sprintf("%s\n", runningState) {
llDrLove marked this conversation as resolved.
Show resolved Hide resolved
m.log.WithFields(logrus.Fields{
"source": source,
"fileContent": string(fileContent),
}).Error("the state file does not specify the running state")

return false
}

return true
}

func (m *mounter) IsFormatted(source string) (bool, error) {
if source == "" {
return false, errors.New("source is not specified")
Expand Down
224 changes: 224 additions & 0 deletions driver/mounter_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading