diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-08-12 04:38:45 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-08-14 04:52:49 +0900 |
| commit | 99ac96511bed17f48d908db3d00a1f48579ba011 (patch) | |
| tree | 30d404efb7256c9f053f64192068637d6b846db4 /hst/fs.go | |
| parent | e99d7affb0c128fa82722f9b3d79c99b5e5918cd (diff) | |
hst/fs: interface filesystem config
This allows mount points to be represented by different underlying structs.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'hst/fs.go')
| -rw-r--r-- | hst/fs.go | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/hst/fs.go b/hst/fs.go new file mode 100644 index 00000000..f60ca32f --- /dev/null +++ b/hst/fs.go @@ -0,0 +1,121 @@ +package hst + +import ( + "encoding/json" + "errors" + "fmt" + "reflect" + + "hakurei.app/container" +) + +// FilesystemConfig is an abstract representation of a mount point. +type FilesystemConfig interface { + // Type returns the type of this mount point. + Type() string + // Target returns the pathname of the mount point in the container. + Target() *container.Absolute + // Host returns a slice of all host paths used by this mount point. + Host() []*container.Absolute + // Apply appends the [container.Op] implementing this mount point. + Apply(ops *container.Ops) + + fmt.Stringer +} + +var ( + ErrFSNull = errors.New("unexpected null in mount point") +) + +// FSTypeError is returned when [ContainerConfig.Filesystem] contains an entry with invalid type. +type FSTypeError string + +func (f FSTypeError) Error() string { return fmt.Sprintf("invalid filesystem type %q", string(f)) } + +// FSImplError is returned when the underlying struct of [FilesystemConfig] does not match +// what [FilesystemConfig.Type] claims to be. +type FSImplError struct { + Type string + Value FilesystemConfig +} + +func (f FSImplError) Error() string { + implType := reflect.TypeOf(f.Value) + var name string + for implType != nil && implType.Kind() == reflect.Ptr { + name += "*" + implType = implType.Elem() + } + if implType != nil { + name += implType.Name() + } else { + name += "nil" + } + return fmt.Sprintf("implementation %s is not %s", name, f.Type) +} + +// FilesystemConfigJSON is the [json] adapter for [FilesystemConfig]. +type FilesystemConfigJSON struct { + FilesystemConfig +} + +// Valid returns whether the [FilesystemConfigJSON] is valid. +func (f *FilesystemConfigJSON) Valid() bool { return f != nil && f.FilesystemConfig != nil } + +func (f *FilesystemConfigJSON) MarshalJSON() ([]byte, error) { + if f == nil || f.FilesystemConfig == nil { + return nil, ErrFSNull + } + var v any + t := f.Type() + switch t { + case FilesystemBind: + if ct, ok := f.FilesystemConfig.(*FSBind); !ok { + return nil, FSImplError{t, f.FilesystemConfig} + } else { + v = &struct { + Type string `json:"type"` + *FSBind + }{FilesystemBind, ct} + } + + case FilesystemEphemeral: + if ct, ok := f.FilesystemConfig.(*FSEphemeral); !ok { + return nil, FSImplError{t, f.FilesystemConfig} + } else { + v = &struct { + Type string `json:"type"` + *FSEphemeral + }{FilesystemEphemeral, ct} + } + + default: + return nil, FSTypeError(t) + } + + return json.Marshal(v) +} + +func (f *FilesystemConfigJSON) UnmarshalJSON(data []byte) error { + t := new(struct { + Type string `json:"type"` + }) + if err := json.Unmarshal(data, &t); err != nil { + return err + } + if t == nil { + return ErrFSNull + } + switch t.Type { + case FilesystemBind: + *f = FilesystemConfigJSON{new(FSBind)} + + case FilesystemEphemeral: + *f = FilesystemConfigJSON{new(FSEphemeral)} + + default: + return FSTypeError(t.Type) + } + + return json.Unmarshal(data, f.FilesystemConfig) +} |
