From b73b9e9be84bb4df2d3bd3da021c32969fd66f3f Mon Sep 17 00:00:00 2001 From: "J. Lowell Wofford" Date: Sun, 25 Jul 2021 22:02:06 -0600 Subject: [PATCH] Add local attach support (#40) * add local attach support * use os.Mode* for backwards compatability * fix incorrect driver naming for attach local * fix some incorrect pointer references in log messages * log message tweaks for attach-local --- go.mod | 4 +- go.sum | 2 - internal/api/attach_local.go | 62 +++++++++++++++++++++++++++ internal/api/attach_loopback.go | 10 ++--- internal/api/mount_nfs.go | 4 +- models/attach.go | 42 ++++++++++++++++++ models/attach_local.go | 76 +++++++++++++++++++++++++++++++++ restapi/embedded_spec.go | 32 ++++++++++++++ swagger.yaml | 17 ++++++++ 9 files changed, 237 insertions(+), 12 deletions(-) create mode 100644 internal/api/attach_local.go create mode 100644 models/attach_local.go diff --git a/go.mod b/go.mod index 8c4bfbf..4aa7755 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/kraken-hpc/imageapi go 1.15 require ( - github.com/u-root/u-root v7.0.0+incompatible github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/bensallen/rbd v0.0.0-20210224155049-baf486eceefa github.com/go-openapi/errors v0.20.0 @@ -19,6 +18,7 @@ require ( github.com/kraken-hpc/uinit v0.1.1 github.com/mailru/easyjson v0.7.7 // indirect github.com/sirupsen/logrus v1.8.1 + github.com/u-root/u-root v7.0.0+incompatible golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect golang.org/x/mod v0.4.2 // indirect golang.org/x/net v0.0.0-20210331060903-cb1fcc7394e5 @@ -26,4 +26,4 @@ require ( google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 // indirect ) -replace github.com/u-root/u-root v7.0.0+incompatible => github.com/u-root/u-root v1.0.1-0.20201119150355-04f343dd1922 \ No newline at end of file +replace github.com/u-root/u-root v7.0.0+incompatible => github.com/u-root/u-root v1.0.1-0.20201119150355-04f343dd1922 diff --git a/go.sum b/go.sum index 2efa80a..92c8600 100644 --- a/go.sum +++ b/go.sum @@ -352,8 +352,6 @@ github.com/u-root/iscsinl v0.1.0/go.mod h1:UGTrNqGTX3vAz6y/VmHC7pY8FLqoigybttGad github.com/u-root/u-root v1.0.1-0.20201119150355-04f343dd1922 h1:t9B62nb5RYgZNwrK+gaF//CDcJnYSt1HqE+MAROggM0= github.com/u-root/u-root v1.0.1-0.20201119150355-04f343dd1922/go.mod h1:c7G35Qvc1z58PWx/p1R9ROyiSuBc0ckqMU74klQ5g/s= github.com/u-root/u-root v6.0.1-0.20200118052101-6bcd1cda5996+incompatible/go.mod h1:RYkpo8pTHrNjW08opNd/U6p/RJE7K0D8fXO0d47+3YY= -github.com/u-root/u-root v7.0.0+incompatible h1:u+KSS04pSxJGI5E7WE4Bs9+Zd75QjFv+REkjy/aoAc8= -github.com/u-root/u-root v7.0.0+incompatible/go.mod h1:RYkpo8pTHrNjW08opNd/U6p/RJE7K0D8fXO0d47+3YY= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= diff --git a/internal/api/attach_local.go b/internal/api/attach_local.go new file mode 100644 index 0000000..f6820af --- /dev/null +++ b/internal/api/attach_local.go @@ -0,0 +1,62 @@ +package api + +import ( + "os" + + "github.com/kraken-hpc/imageapi/models" + "github.com/sirupsen/logrus" +) + +func init() { + AttachDrivers[models.AttachKindLocal] = &AttachDriverLocal{} +} + +type AttachDriverLocal struct { + log *logrus.Entry +} + +func (a *AttachDriverLocal) Init(log *logrus.Entry) { + a.log = log + a.log.Trace("initialized") +} + +func (a *AttachDriverLocal) Attach(att *Attach) (ret *Attach, err error) { + // sanity check + l := a.log.WithField("operation", "attach") + if att.Local == nil { + l.Trace("attempted to attach local with no local definition") + return nil, ERRINVALDAT + } + l = l.WithField("path", *att.Local.Path) + + finfo, err := os.Stat(*att.Local.Path) + if err != nil { + l.WithError(err).Debug("failed to stat device file") + return nil, ERRFAIL + } + + if finfo.Mode()&os.ModeDevice == 0 { + l.Trace("path is not a device file") + return nil, ERRINVALDAT + } + + if finfo.Mode()&os.ModeCharDevice != 0 { + l.Trace("path points to character device") + return nil, ERRINVALDAT + } + att.DeviceFile = *att.Local.Path + + l.Info("successfully attached") + return att, nil +} + +func (a *AttachDriverLocal) Detach(att *Attach) (ret *Attach, err error) { + // this is a dummy operation + l := a.log.WithFields(logrus.Fields{ + "operation": "detach", + "id": att.ID, + "path": *att.Local.Path, + }) + l.Info("successfully detached") + return att, nil +} diff --git a/internal/api/attach_loopback.go b/internal/api/attach_loopback.go index 8cb4d5b..519e2f3 100644 --- a/internal/api/attach_loopback.go +++ b/internal/api/attach_loopback.go @@ -1,7 +1,5 @@ package api -// API operations on rbd maps - import ( "os" "path" @@ -32,8 +30,8 @@ func (a *AttachDriverLoopback) Attach(att *Attach) (ret *Attach, err error) { return nil, ERRINVALDAT } l = l.WithFields(logrus.Fields{ - "path": att.Loopback.Path, - "base": att.Loopback.Base, + "path": *att.Loopback.Path, + "base": *att.Loopback.Base, }) base := "/" @@ -85,8 +83,8 @@ func (a *AttachDriverLoopback) Detach(att *Attach) (ret *Attach, err error) { l := a.log.WithFields(logrus.Fields{ "operation": "unmap", "id": att.ID, - "path": att.Loopback.Path, - "base": att.Loopback.Base, + "path": *att.Loopback.Path, + "base": *att.Loopback.Base, }) if err = loop.ClearFile(att.DeviceFile); err != nil { l.Debug("failed to clear loopback association") diff --git a/internal/api/mount_nfs.go b/internal/api/mount_nfs.go index b8e6bf1..11f1d1f 100644 --- a/internal/api/mount_nfs.go +++ b/internal/api/mount_nfs.go @@ -68,8 +68,8 @@ func (m *MountDriverNFS) Unmount(mnt *Mount) (ret *Mount, err error) { l := m.log.WithFields(logrus.Fields{ "operation": "unmount", "id": mnt.ID, - "host": mnt.Nfs.Host, - "path": mnt.Nfs.Path, + "host": *mnt.Nfs.Host, + "path": *mnt.Nfs.Path, }) // always lazy unmount. Good idea? diff --git a/models/attach.go b/models/attach.go index 17b2c9c..edf63bc 100644 --- a/models/attach.go +++ b/models/attach.go @@ -43,6 +43,9 @@ type Attach struct { // Enum: [iscsi local loopback rbd] Kind string `json:"kind,omitempty"` + // local + Local *AttachLocal `json:"local,omitempty"` + // loopback Loopback *AttachLoopback `json:"loopback,omitempty"` @@ -66,6 +69,10 @@ func (m *Attach) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateLocal(formats); err != nil { + res = append(res, err) + } + if err := m.validateLoopback(formats); err != nil { res = append(res, err) } @@ -143,6 +150,23 @@ func (m *Attach) validateKind(formats strfmt.Registry) error { return nil } +func (m *Attach) validateLocal(formats strfmt.Registry) error { + if swag.IsZero(m.Local) { // not required + return nil + } + + if m.Local != nil { + if err := m.Local.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("local") + } + return err + } + } + + return nil +} + func (m *Attach) validateLoopback(formats strfmt.Registry) error { if swag.IsZero(m.Loopback) { // not required return nil @@ -189,6 +213,10 @@ func (m *Attach) ContextValidate(ctx context.Context, formats strfmt.Registry) e res = append(res, err) } + if err := m.contextValidateLocal(ctx, formats); err != nil { + res = append(res, err) + } + if err := m.contextValidateLoopback(ctx, formats); err != nil { res = append(res, err) } @@ -228,6 +256,20 @@ func (m *Attach) contextValidateID(ctx context.Context, formats strfmt.Registry) return nil } +func (m *Attach) contextValidateLocal(ctx context.Context, formats strfmt.Registry) error { + + if m.Local != nil { + if err := m.Local.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("local") + } + return err + } + } + + return nil +} + func (m *Attach) contextValidateLoopback(ctx context.Context, formats strfmt.Registry) error { if m.Loopback != nil { diff --git a/models/attach_local.go b/models/attach_local.go new file mode 100644 index 0000000..b309657 --- /dev/null +++ b/models/attach_local.go @@ -0,0 +1,76 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" + "github.com/go-openapi/validate" +) + +// AttachLocal `attach_local` describes a block device that is locally present. +// This can be used to get a reference to a local disk, for instance. +// +// Local only supports finding device files on the local (root) system. +// It only takes one parameter: the path to the device file. +// +// +// swagger:model attach_local +type AttachLocal struct { + + // A unix-formatted filesystem path pointing to a block device file. + // Required: true + Path *string `json:"path"` +} + +// Validate validates this attach local +func (m *AttachLocal) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validatePath(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *AttachLocal) validatePath(formats strfmt.Registry) error { + + if err := validate.Required("path", "body", m.Path); err != nil { + return err + } + + return nil +} + +// ContextValidate validates this attach local based on context it is used +func (m *AttachLocal) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *AttachLocal) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *AttachLocal) UnmarshalBinary(b []byte) error { + var res AttachLocal + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/restapi/embedded_spec.go b/restapi/embedded_spec.go index 5786a44..f946509 100644 --- a/restapi/embedded_spec.go +++ b/restapi/embedded_spec.go @@ -466,6 +466,9 @@ func init() { "rbd" ] }, + "local": { + "$ref": "#/definitions/attach_local" + }, "loopback": { "$ref": "#/definitions/attach_loopback" }, @@ -479,6 +482,19 @@ func init() { } } }, + "attach_local": { + "description": "` + "`" + `attach_local` + "`" + ` describes a block device that is locally present.\nThis can be used to get a reference to a local disk, for instance.\n\nLocal only supports finding device files on the local (root) system.\nIt only takes one parameter: the path to the device file.\n", + "type": "object", + "required": [ + "path" + ], + "properties": { + "path": { + "description": "A unix-formatted filesystem path pointing to a block device file.", + "type": "string" + } + } + }, "attach_loopback": { "description": "` + "`" + `attach_loopback` + "`" + ` describes a loopback device based on an available file.\nThe file can live either on ` + "`" + `/` + "`" + ` (\"root\") or a mount, as specified by base.\nPath specifies the path relative to the base.\n", "type": "object", @@ -1346,6 +1362,9 @@ func init() { "rbd" ] }, + "local": { + "$ref": "#/definitions/attach_local" + }, "loopback": { "$ref": "#/definitions/attach_loopback" }, @@ -1359,6 +1378,19 @@ func init() { } } }, + "attach_local": { + "description": "` + "`" + `attach_local` + "`" + ` describes a block device that is locally present.\nThis can be used to get a reference to a local disk, for instance.\n\nLocal only supports finding device files on the local (root) system.\nIt only takes one parameter: the path to the device file.\n", + "type": "object", + "required": [ + "path" + ], + "properties": { + "path": { + "description": "A unix-formatted filesystem path pointing to a block device file.", + "type": "string" + } + } + }, "attach_loopback": { "description": "` + "`" + `attach_loopback` + "`" + ` describes a loopback device based on an available file.\nThe file can live either on ` + "`" + `/` + "`" + ` (\"root\") or a mount, as specified by base.\nPath specifies the path relative to the base.\n", "type": "object", diff --git a/swagger.yaml b/swagger.yaml index d475c16..7c19d60 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -166,6 +166,21 @@ definitions: mount: $ref: "#/definitions/mount" + attach_local: + description: | + `attach_local` describes a block device that is locally present. + This can be used to get a reference to a local disk, for instance. + + Local only supports finding device files on the local (root) system. + It only takes one parameter: the path to the device file. + type: object + required: + - path + properties: + path: + type: string + description: A unix-formatted filesystem path pointing to a block device file. + attach: description: | Generically address attachments. Attachments are objects that ultimately provide a block device file. @@ -197,6 +212,8 @@ definitions: readOnly: true rbd: $ref: "#/definitions/attach_rbd" + local: + $ref: "#/definitions/attach_local" loopback: $ref: "#/definitions/attach_loopback"