mirror of
https://github.com/m1k1o/neko-rooms.git
synced 2026-11-02 22:51:34 -06:00
add mount types.
This commit is contained in:
@@ -246,6 +246,10 @@ components:
|
|||||||
RoomMount:
|
RoomMount:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
enum: [ private, template, public ]
|
||||||
|
example: private
|
||||||
host_path:
|
host_path:
|
||||||
type: string
|
type: string
|
||||||
example: /profile
|
example: /profile
|
||||||
|
|||||||
@@ -113,6 +113,12 @@ export interface RoomMember {
|
|||||||
* @interface RoomMount
|
* @interface RoomMount
|
||||||
*/
|
*/
|
||||||
export interface RoomMount {
|
export interface RoomMount {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof RoomMount
|
||||||
|
*/
|
||||||
|
type?: RoomMountTypeEnum;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
@@ -126,6 +132,17 @@ export interface RoomMount {
|
|||||||
*/
|
*/
|
||||||
container_path?: string;
|
container_path?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @export
|
||||||
|
* @enum {string}
|
||||||
|
*/
|
||||||
|
export enum RoomMountTypeEnum {
|
||||||
|
private = 'private',
|
||||||
|
template = 'template',
|
||||||
|
public = 'public'
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
|
|||||||
@@ -210,7 +210,7 @@
|
|||||||
</v-row>
|
</v-row>
|
||||||
<v-row align="center" no-gutters class="mt-3">
|
<v-row align="center" no-gutters class="mt-3">
|
||||||
<h2> Mounts </h2>
|
<h2> Mounts </h2>
|
||||||
<v-btn @click="data.mounts = [ ...data.mounts, { host_path: '', container_path: '' }]" icon color="green"><v-icon>mdi-plus</v-icon></v-btn>
|
<v-btn @click="data.mounts = [ ...data.mounts, { type: 'private', host_path: '', container_path: '' }]" icon color="green"><v-icon>mdi-plus</v-icon></v-btn>
|
||||||
</v-row>
|
</v-row>
|
||||||
<v-row align="center" v-for="({ host_path, container_path }, index) in data.mounts" :key="index">
|
<v-row align="center" v-for="({ host_path, container_path }, index) in data.mounts" :key="index">
|
||||||
<v-col class="py-0">
|
<v-col class="py-0">
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ type Room struct {
|
|||||||
StorageInternal string
|
StorageInternal string
|
||||||
StorageExternal string
|
StorageExternal string
|
||||||
|
|
||||||
|
MountsWhitelist []string
|
||||||
|
|
||||||
InstanceName string
|
InstanceName string
|
||||||
InstanceUrl string
|
InstanceUrl string
|
||||||
|
|
||||||
@@ -73,6 +75,11 @@ func (Room) Init(cmd *cobra.Command) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd.PersistentFlags().StringSlice("mounts.whitelist", []string{}, "whitelisted public mounts for containers")
|
||||||
|
if err := viper.BindPFlag("mounts.whitelist", cmd.PersistentFlags().Lookup("mounts.whitelist")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Instance
|
// Instance
|
||||||
|
|
||||||
cmd.PersistentFlags().String("instance.name", "neko-rooms", "unique instance name (if running muliple on the same host)")
|
cmd.PersistentFlags().String("instance.name", "neko-rooms", "unique instance name (if running muliple on the same host)")
|
||||||
@@ -159,6 +166,15 @@ func (s *Room) Set() {
|
|||||||
s.StorageEnabled = false
|
s.StorageEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.MountsWhitelist = viper.GetStringSlice("mounts.whitelist")
|
||||||
|
for _, path := range s.MountsWhitelist {
|
||||||
|
path = filepath.Clean(path)
|
||||||
|
|
||||||
|
if !filepath.IsAbs(path) {
|
||||||
|
log.Panic().Msg("invalid `mounts.whitelist`, must be an absolute path")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s.InstanceName = viper.GetString("instance.name")
|
s.InstanceName = viper.GetString("instance.name")
|
||||||
if !dockerNames.RestrictedNamePattern.MatchString(s.InstanceName) {
|
if !dockerNames.RestrictedNamePattern.MatchString(s.InstanceName) {
|
||||||
log.Panic().Msg("invalid `instance.name`, must match " + dockerNames.RestrictedNameChars)
|
log.Panic().Msg("invalid `instance.name`, must match " + dockerNames.RestrictedNameChars)
|
||||||
|
|||||||
+58
-15
@@ -26,10 +26,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
frontendPort = 8080
|
frontendPort = 8080
|
||||||
roomStoragePath = "./rooms"
|
templateStoragePath = "./templates"
|
||||||
roomStorageUid = 1000
|
privateStoragePath = "./rooms"
|
||||||
roomStorageGid = 1000
|
privateStorageUid = 1000
|
||||||
|
privateStorageGid = 1000
|
||||||
)
|
)
|
||||||
|
|
||||||
func New(config *config.Room) *RoomManagerCtx {
|
func New(config *config.Room) *RoomManagerCtx {
|
||||||
@@ -195,6 +196,8 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro
|
|||||||
|
|
||||||
mounts := []dockerMount.Mount{}
|
mounts := []dockerMount.Mount{}
|
||||||
for _, mount := range settings.Mounts {
|
for _, mount := range settings.Mounts {
|
||||||
|
readOnly := false
|
||||||
|
|
||||||
hostPath := filepath.Clean(mount.HostPath)
|
hostPath := filepath.Clean(mount.HostPath)
|
||||||
containerPath := filepath.Clean(mount.ContainerPath)
|
containerPath := filepath.Clean(mount.ContainerPath)
|
||||||
|
|
||||||
@@ -202,23 +205,49 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro
|
|||||||
return "", fmt.Errorf("mount paths must be absolute")
|
return "", fmt.Errorf("mount paths must be absolute")
|
||||||
}
|
}
|
||||||
|
|
||||||
externalPath := path.Join(manager.config.StorageExternal, roomStoragePath, roomName, hostPath)
|
// private container's data
|
||||||
internalPath := path.Join(manager.config.StorageInternal, roomStoragePath, roomName, hostPath)
|
if mount.Type == types.MountPrivate {
|
||||||
|
// ensure that target exists
|
||||||
|
internalPath := path.Join(manager.config.StorageInternal, privateStoragePath, roomName, hostPath)
|
||||||
|
if err := os.MkdirAll(internalPath, os.ModePerm); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
if err := os.MkdirAll(internalPath, os.ModePerm); err != nil {
|
if err := utils.ChownR(internalPath, privateStorageUid, privateStorageGid); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := utils.ChownR(internalPath, roomStorageUid, roomStorageGid); err != nil {
|
// prefix host path
|
||||||
return "", err
|
hostPath = path.Join(manager.config.StorageExternal, privateStoragePath, roomName, hostPath)
|
||||||
|
} else if mount.Type == types.MountTemplate {
|
||||||
|
// readonly template data
|
||||||
|
readOnly = true
|
||||||
|
|
||||||
|
// prefix host path
|
||||||
|
hostPath = path.Join(manager.config.StorageExternal, templateStoragePath, hostPath)
|
||||||
|
} else if mount.Type == types.MountPublic {
|
||||||
|
// public whitelisted mounts
|
||||||
|
var isAllowed = false
|
||||||
|
for _, path := range manager.config.MountsWhitelist {
|
||||||
|
if strings.HasPrefix(hostPath, path) {
|
||||||
|
isAllowed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isAllowed {
|
||||||
|
return "", fmt.Errorf("mount path is not whitelisted in config")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "", fmt.Errorf("unknown mount type %q", mount.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
mounts = append(mounts,
|
mounts = append(mounts,
|
||||||
dockerMount.Mount{
|
dockerMount.Mount{
|
||||||
Type: dockerMount.TypeBind,
|
Type: dockerMount.TypeBind,
|
||||||
Source: externalPath,
|
Source: hostPath,
|
||||||
Target: containerPath,
|
Target: containerPath,
|
||||||
ReadOnly: false,
|
ReadOnly: readOnly,
|
||||||
Consistency: dockerMount.ConsistencyDefault,
|
Consistency: dockerMount.ConsistencyDefault,
|
||||||
|
|
||||||
BindOptions: &dockerMount.BindOptions{
|
BindOptions: &dockerMount.BindOptions{
|
||||||
@@ -349,11 +378,25 @@ func (manager *RoomManagerCtx) GetSettings(id string) (*types.RoomSettings, erro
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
privateStorageRoot := path.Join(manager.config.StorageExternal, privateStoragePath, roomName)
|
||||||
|
templateStorageRoot := path.Join(manager.config.StorageExternal, templateStoragePath)
|
||||||
|
|
||||||
mounts := []types.RoomMount{}
|
mounts := []types.RoomMount{}
|
||||||
dataRoot := path.Join(manager.config.StorageExternal, roomStoragePath, roomName)
|
|
||||||
for _, mount := range container.Mounts {
|
for _, mount := range container.Mounts {
|
||||||
|
mountType := types.MountPublic
|
||||||
|
hostPath := mount.Source
|
||||||
|
|
||||||
|
if strings.HasPrefix(hostPath, privateStorageRoot) {
|
||||||
|
mountType = types.MountPrivate
|
||||||
|
hostPath = strings.TrimPrefix(hostPath, privateStorageRoot)
|
||||||
|
} else if strings.HasPrefix(hostPath, templateStorageRoot) {
|
||||||
|
mountType = types.MountTemplate
|
||||||
|
hostPath = strings.TrimPrefix(hostPath, templateStorageRoot)
|
||||||
|
}
|
||||||
|
|
||||||
mounts = append(mounts, types.RoomMount{
|
mounts = append(mounts, types.RoomMount{
|
||||||
HostPath: strings.TrimPrefix(mount.Source, dataRoot),
|
Type: mountType,
|
||||||
|
HostPath: hostPath,
|
||||||
ContainerPath: mount.Destination,
|
ContainerPath: mount.Destination,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-2
@@ -39,9 +39,18 @@ type RoomEntry struct {
|
|||||||
Created time.Time `json:"created"`
|
Created time.Time `json:"created"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MountType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
MountPrivate MountType = "private"
|
||||||
|
MountTemplate MountType = "template"
|
||||||
|
MountPublic MountType = "public"
|
||||||
|
)
|
||||||
|
|
||||||
type RoomMount struct {
|
type RoomMount struct {
|
||||||
HostPath string `json:"host_path"`
|
Type MountType `json:"type"`
|
||||||
ContainerPath string `json:"container_path"`
|
HostPath string `json:"host_path"`
|
||||||
|
ContainerPath string `json:"container_path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RoomSettings struct {
|
type RoomSettings struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user