add authentication method
Some checks failed
CI / test-build (push) Failing after 2m1s

This commit is contained in:
2025-12-14 23:55:12 +07:00
parent ed96137bad
commit 54e76d9304
18 changed files with 2197 additions and 34 deletions

182
internal/storage/iscsi.go Normal file
View File

@@ -0,0 +1,182 @@
package storage
import (
"errors"
"fmt"
"sync"
"gitea.avt.data-center.id/othman.suseno/atlas/internal/models"
)
var (
ErrISCSITargetNotFound = errors.New("iSCSI target not found")
ErrISCSITargetExists = errors.New("iSCSI target already exists")
ErrLUNNotFound = errors.New("LUN not found")
ErrLUNExists = errors.New("LUN already exists")
)
// ISCSIStore manages iSCSI targets and LUNs
type ISCSIStore struct {
mu sync.RWMutex
targets map[string]*models.ISCSITarget
nextID int
}
// NewISCSIStore creates a new iSCSI store
func NewISCSIStore() *ISCSIStore {
return &ISCSIStore{
targets: make(map[string]*models.ISCSITarget),
nextID: 1,
}
}
// List returns all iSCSI targets
func (s *ISCSIStore) List() []models.ISCSITarget {
s.mu.RLock()
defer s.mu.RUnlock()
targets := make([]models.ISCSITarget, 0, len(s.targets))
for _, target := range s.targets {
targets = append(targets, *target)
}
return targets
}
// Get returns a target by ID
func (s *ISCSIStore) Get(id string) (*models.ISCSITarget, error) {
s.mu.RLock()
defer s.mu.RUnlock()
target, ok := s.targets[id]
if !ok {
return nil, ErrISCSITargetNotFound
}
return target, nil
}
// GetByIQN returns a target by IQN
func (s *ISCSIStore) GetByIQN(iqn string) (*models.ISCSITarget, error) {
s.mu.RLock()
defer s.mu.RUnlock()
for _, target := range s.targets {
if target.IQN == iqn {
return target, nil
}
}
return nil, ErrISCSITargetNotFound
}
// Create creates a new iSCSI target
func (s *ISCSIStore) Create(iqn string, initiators []string) (*models.ISCSITarget, error) {
s.mu.Lock()
defer s.mu.Unlock()
// Check if IQN already exists
for _, target := range s.targets {
if target.IQN == iqn {
return nil, ErrISCSITargetExists
}
}
id := fmt.Sprintf("iscsi-%d", s.nextID)
s.nextID++
target := &models.ISCSITarget{
ID: id,
IQN: iqn,
LUNs: []models.LUN{},
Initiators: initiators,
Enabled: true,
}
s.targets[id] = target
return target, nil
}
// Update updates an existing target
func (s *ISCSIStore) Update(id string, initiators []string, enabled bool) error {
s.mu.Lock()
defer s.mu.Unlock()
target, ok := s.targets[id]
if !ok {
return ErrISCSITargetNotFound
}
target.Enabled = enabled
if initiators != nil {
target.Initiators = initiators
}
return nil
}
// Delete removes a target
func (s *ISCSIStore) Delete(id string) error {
s.mu.Lock()
defer s.mu.Unlock()
if _, ok := s.targets[id]; !ok {
return ErrISCSITargetNotFound
}
delete(s.targets, id)
return nil
}
// AddLUN adds a LUN to a target
func (s *ISCSIStore) AddLUN(targetID string, zvol string, size uint64) (*models.LUN, error) {
s.mu.Lock()
defer s.mu.Unlock()
target, ok := s.targets[targetID]
if !ok {
return nil, ErrISCSITargetNotFound
}
// Check if ZVOL already mapped
for _, lun := range target.LUNs {
if lun.ZVOL == zvol {
return nil, ErrLUNExists
}
}
// Find next available LUN ID
lunID := 0
for _, lun := range target.LUNs {
if lun.ID >= lunID {
lunID = lun.ID + 1
}
}
lun := models.LUN{
ID: lunID,
ZVOL: zvol,
Size: size,
Backend: "zvol",
}
target.LUNs = append(target.LUNs, lun)
return &lun, nil
}
// RemoveLUN removes a LUN from a target
func (s *ISCSIStore) RemoveLUN(targetID string, lunID int) error {
s.mu.Lock()
defer s.mu.Unlock()
target, ok := s.targets[targetID]
if !ok {
return ErrISCSITargetNotFound
}
for i, lun := range target.LUNs {
if lun.ID == lunID {
target.LUNs = append(target.LUNs[:i], target.LUNs[i+1:]...)
return nil
}
}
return ErrLUNNotFound
}

128
internal/storage/nfs.go Normal file
View File

@@ -0,0 +1,128 @@
package storage
import (
"errors"
"fmt"
"sync"
"gitea.avt.data-center.id/othman.suseno/atlas/internal/models"
)
var (
ErrNFSExportNotFound = errors.New("NFS export not found")
ErrNFSExportExists = errors.New("NFS export already exists")
)
// NFSStore manages NFS exports
type NFSStore struct {
mu sync.RWMutex
exports map[string]*models.NFSExport
nextID int
}
// NewNFSStore creates a new NFS export store
func NewNFSStore() *NFSStore {
return &NFSStore{
exports: make(map[string]*models.NFSExport),
nextID: 1,
}
}
// List returns all NFS exports
func (s *NFSStore) List() []models.NFSExport {
s.mu.RLock()
defer s.mu.RUnlock()
exports := make([]models.NFSExport, 0, len(s.exports))
for _, export := range s.exports {
exports = append(exports, *export)
}
return exports
}
// Get returns an export by ID
func (s *NFSStore) Get(id string) (*models.NFSExport, error) {
s.mu.RLock()
defer s.mu.RUnlock()
export, ok := s.exports[id]
if !ok {
return nil, ErrNFSExportNotFound
}
return export, nil
}
// GetByPath returns an export by path
func (s *NFSStore) GetByPath(path string) (*models.NFSExport, error) {
s.mu.RLock()
defer s.mu.RUnlock()
for _, export := range s.exports {
if export.Path == path {
return export, nil
}
}
return nil, ErrNFSExportNotFound
}
// Create creates a new NFS export
func (s *NFSStore) Create(path, dataset string, clients []string, readOnly, rootSquash bool) (*models.NFSExport, error) {
s.mu.Lock()
defer s.mu.Unlock()
// Check if path already exists
for _, export := range s.exports {
if export.Path == path {
return nil, ErrNFSExportExists
}
}
id := fmt.Sprintf("nfs-%d", s.nextID)
s.nextID++
export := &models.NFSExport{
ID: id,
Path: path,
Dataset: dataset,
Clients: clients,
ReadOnly: readOnly,
RootSquash: rootSquash,
Enabled: true,
}
s.exports[id] = export
return export, nil
}
// Update updates an existing export
func (s *NFSStore) Update(id string, clients []string, readOnly, rootSquash, enabled bool) error {
s.mu.Lock()
defer s.mu.Unlock()
export, ok := s.exports[id]
if !ok {
return ErrNFSExportNotFound
}
export.ReadOnly = readOnly
export.RootSquash = rootSquash
export.Enabled = enabled
if clients != nil {
export.Clients = clients
}
return nil
}
// Delete removes an export
func (s *NFSStore) Delete(id string) error {
s.mu.Lock()
defer s.mu.Unlock()
if _, ok := s.exports[id]; !ok {
return ErrNFSExportNotFound
}
delete(s.exports, id)
return nil
}

131
internal/storage/smb.go Normal file
View File

@@ -0,0 +1,131 @@
package storage
import (
"errors"
"fmt"
"sync"
"gitea.avt.data-center.id/othman.suseno/atlas/internal/models"
)
var (
ErrSMBShareNotFound = errors.New("SMB share not found")
ErrSMBShareExists = errors.New("SMB share already exists")
)
// SMBStore manages SMB shares
type SMBStore struct {
mu sync.RWMutex
shares map[string]*models.SMBShare
nextID int
}
// NewSMBStore creates a new SMB share store
func NewSMBStore() *SMBStore {
return &SMBStore{
shares: make(map[string]*models.SMBShare),
nextID: 1,
}
}
// List returns all SMB shares
func (s *SMBStore) List() []models.SMBShare {
s.mu.RLock()
defer s.mu.RUnlock()
shares := make([]models.SMBShare, 0, len(s.shares))
for _, share := range s.shares {
shares = append(shares, *share)
}
return shares
}
// Get returns a share by ID
func (s *SMBStore) Get(id string) (*models.SMBShare, error) {
s.mu.RLock()
defer s.mu.RUnlock()
share, ok := s.shares[id]
if !ok {
return nil, ErrSMBShareNotFound
}
return share, nil
}
// GetByName returns a share by name
func (s *SMBStore) GetByName(name string) (*models.SMBShare, error) {
s.mu.RLock()
defer s.mu.RUnlock()
for _, share := range s.shares {
if share.Name == name {
return share, nil
}
}
return nil, ErrSMBShareNotFound
}
// Create creates a new SMB share
func (s *SMBStore) Create(name, path, dataset, description string, readOnly, guestOK bool, validUsers []string) (*models.SMBShare, error) {
s.mu.Lock()
defer s.mu.Unlock()
// Check if name already exists
for _, share := range s.shares {
if share.Name == name {
return nil, ErrSMBShareExists
}
}
id := fmt.Sprintf("smb-%d", s.nextID)
s.nextID++
share := &models.SMBShare{
ID: id,
Name: name,
Path: path,
Dataset: dataset,
Description: description,
ReadOnly: readOnly,
GuestOK: guestOK,
ValidUsers: validUsers,
Enabled: true,
}
s.shares[id] = share
return share, nil
}
// Update updates an existing share
func (s *SMBStore) Update(id, description string, readOnly, guestOK, enabled bool, validUsers []string) error {
s.mu.Lock()
defer s.mu.Unlock()
share, ok := s.shares[id]
if !ok {
return ErrSMBShareNotFound
}
share.Description = description
share.ReadOnly = readOnly
share.GuestOK = guestOK
share.Enabled = enabled
if validUsers != nil {
share.ValidUsers = validUsers
}
return nil
}
// Delete removes a share
func (s *SMBStore) Delete(id string) error {
s.mu.Lock()
defer s.mu.Unlock()
if _, ok := s.shares[id]; !ok {
return ErrSMBShareNotFound
}
delete(s.shares, id)
return nil
}