Skip to content
Snippets Groups Projects
Commit 9572a290 authored by Alena Petraki's avatar Alena Petraki :nail_care_tone1:
Browse files

Изменен тип Resource struct -> interface

parent 1b8d6062
No related tags found
No related merge requests found
......@@ -6,8 +6,8 @@ import (
"git.perx.ru/perxis/perxis-go/pkg/spaces"
)
// type ProviderType string
//
type ProviderType string
// const (
// ProviderTypeStandalone ProviderType = "standalone"
// ProviderTypeYandexCloud = "yandex_cloud"
......@@ -15,9 +15,11 @@ import (
type Kind string
func (k Kind) String() string { return string(k) }
const (
KindDatabase Kind = "database"
KindObjectStorage = "object_storage"
KindObjectStorage Kind = "object_storage"
)
type ProviderDescription struct {
......@@ -38,30 +40,47 @@ func (d *ProviderDescription) SetName(s string) { d.Name = s }
// Provider() (Provider, error)
// }
// Provider отвечает за взаимодействие с провайдером и знает про внутреннее устройство
// ресурсов типа ProviderType:
// - в зависимости от того, имплементирует ли провайдер DatabaseProvider или ObjectStorageProvider,
// отвечает за взаимодействие с провайдером для создания/удаления БД/облачного хранилища
// - является кэшэм для соединений с ресурсами, чтобы соединения могли переиспользоваться для разных ресурсов
// - Знает внутреннее строение ресурсов и может валидировать конфигурацию ресурса
type Provider interface {
GetID() string
SetID() string
SetID(s string)
GetName() string
SetName(s string)
Kind() []Kind
// Type() ProviderType
Type() ProviderType
// GetDefaultResource - получить ресурс запрашиваемого типа, готовый к аллокации
// todo: параметр пространства выглядит не к месту, передается для установки
// названия БД. Убрать передачу пространства и генерировать для БД просто ID? Чем грозит?
GetDefaultResource(ctx context.Context, space *spaces.Space, kind Kind) (Resource, error)
UpdateResource(ctx context.Context, space *spaces.Space, params Resource) error
GetConnectedResource(ctx context.Context, resource *Resource) (any, error)
AllocateResource(ctx context.Context, resource Resource) error
DeallocateResource(ctx context.Context, resource Resource) error
Close(ctx context.Context)
}
// GetConnectedResource - получить готовый к взаимодействию ресурс. В случае,
// если для ресурса доступна lazy-аллокация, ресурс будет сначала аллоцирован
GetConnectedResource(ctx context.Context, resource Resource) (*ConnectedResource, error)
type DatabaseProvider interface {
CreateDatabase(ctx context.Context, resource Resource) error
DropDatabase(ctx context.Context, resource Resource) error
Close(ctx context.Context)
}
type ObjectStorageProvider interface {
CreateObjectStorage(ctx context.Context, resource Resource) error
DropObjectStorage(ctx context.Context, resource Resource) error
}
// // DatabaseProvider интерфейс, который должен имплементировать провайдер, если
// // он может управлять БД.
// type DatabaseProvider interface {
// CreateDatabase(ctx context.Context, resource Resource) error
// DropDatabase(ctx context.Context, resource Resource) error
// }
//
// // ObjectStorageProvider интерфейс, который должен имплементировать провайдер, если
// // он может управлять хранилищами объектов.
// type ObjectStorageProvider interface {
// CreateObjectStorage(ctx context.Context, resource Resource) error
// DropObjectStorage(ctx context.Context, resource Resource) error
// }
......@@ -43,13 +43,16 @@ type Resource interface {
GetName() string
SetName(s string)
GetProviderID()
GetProviderID() string
// SetProviderID(s string)
GetStateInfo() StateInfo
SetStateInfo(s StateInfo)
Kind() Kind
Type() ResourceType
Validate() error
}
type StateInfo struct {
......@@ -58,6 +61,8 @@ type StateInfo struct {
Message string
}
// ResourceDescription имплементирует часть методов интерфейса Resource и может
// использоваться для встраивания в имплементации ресурсов
type ResourceDescription struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
......@@ -70,24 +75,26 @@ func (d *ResourceDescription) SetID(s string) { d.ID = s }
func (d *ResourceDescription) GetName() string { return d.Name }
func (d *ResourceDescription) SetName(s string) { d.Name = s }
func (d *ResourceDescription) GetProviderID() string { return d.ProviderID }
func (d *ResourceDescription) SetProviderID(s string) { d.ProviderID = s }
// func (d *ResourceDescription) SetProviderID(s string) { d.ProviderID = s }
func (d *ResourceDescription) GetStateInfo() StateInfo { return d.Info }
func (d *ResourceDescription) SetStateInfo(s StateInfo) { d.Info = s }
type Resources struct {
// ConnectedResource содержит в себе готовый к взаимодействию ресурс, причем один. Остальные поля nil
type ConnectedResource struct {
*MongoResource
// FSResource
// S3Resource
}
func NewResources(rr ...any) *Resources {
res := new(Resources)
for _, r := range rr {
switch v := r.(type) {
case *MongoResource:
res.MongoResource = v
}
}
return res
}
// func NewResources(rr ...any) *ConnectedResource {
// res := new(ConnectedResource)
// for _, r := range rr {
// switch v := r.(type) {
// case *MongoResource:
// res.MongoResource = v
// }
// }
// return res
// }
......@@ -2,33 +2,23 @@ package resources
import "context"
// Service -
// TODO:
// - опционально аллоцировать ресурс при создании/lazy-аллокация (может решаться внутри имплементации провайдера)
// - при обновлении ресурса если изменилась конфигурация и ресурс
// был аллоцирован с ошибкой, пытаться аллоцировать снова - ???
type Service interface {
// // AllocateSpaceResources - инициализировать ресурсы для пространства. Аллокация происходит
// // в синхронном режиме и может занимать значительное время
// AllocateSpaceResources(ctx context.Context, spaceID string) error
//
// // DeallocateSpaceResources - полностью удалить ресурсы для пространства. Деаллокация происходит
// // в синхронном режиме и может занимать значительное время
// DeallocateSpaceResources(ctx context.Context, spaceID string) error
//
// // GetSpaceResources - получить инициализированные и готовые к работе ресурсы пространства
// GetSpaceResources(ctx context.Context, spaceID string) (*Resources, error)
CreateProvider(ctx context.Context, provider *Provider) (*Provider, error)
UpdateProvider(ctx context.Context, provider *Provider) error
GetProvider(ctx context.Context, providerID string) (*Provider, error)
FindProviders(ctx context.Context, filter *Filter) ([]*Provider, error)
CreateProvider(ctx context.Context, provider Provider) (Provider, error)
UpdateProvider(ctx context.Context, provider Provider) error
GetProvider(ctx context.Context, providerID string) (Provider, error)
FindProviders(ctx context.Context, filter *Filter) ([]Provider, error)
DeleteProvider(ctx context.Context, providerID string) error
CreateResource(ctx context.Context, resource *Resource) (*Resource, error)
UpdateResource(ctx context.Context, resource *Resource) error
GetResource(ctx context.Context, resourceID string) (*Resource, error)
FindResources(ctx context.Context, filter *Filter) ([]*Resource, error)
CreateResource(ctx context.Context, resource Resource) (Resource, error)
UpdateResource(ctx context.Context, resource Resource) error
GetResource(ctx context.Context, resourceID string) (Resource, error)
FindResources(ctx context.Context, filter *Filter) ([]Resource, error)
DeleteResource(ctx context.Context, resourceID string) error
AllocateResource(ctx context.Context, resourceID string) error
DeallocateResource(ctx context.Context, resourceID string) error
}
type Storage interface {
......@@ -38,10 +28,10 @@ type Storage interface {
FindProviders(ctx context.Context, filter *Filter) ([]Provider, error)
DeleteProvider(ctx context.Context, providerID string) error
CreateResource(ctx context.Context, resource *Resource) error
UpdateResource(ctx context.Context, resource *Resource) error
GetResource(ctx context.Context, resourceID string) (*Resource, error)
FindResources(ctx context.Context, filter *Filter) ([]*Resource, error)
CreateResource(ctx context.Context, resource Resource) error
UpdateResource(ctx context.Context, resource Resource) error
GetResource(ctx context.Context, resourceID string) (Resource, error)
FindResources(ctx context.Context, filter *Filter) ([]Resource, error)
DeleteResource(ctx context.Context, resourceID string) error
}
......
package resources
import (
"context"
"fmt"
"sync"
"git.perx.ru/perxis/perxis-go/pkg/data"
"git.perx.ru/perxis/perxis-go/pkg/organizations"
"git.perx.ru/perxis/perxis-go/pkg/spaces"
"github.com/pkg/errors"
)
// SpaceResources используется для управления ресурсами пространств и системными.
type SpaceResources struct {
// ресурсы и провайдеры, которые будут создаваться при старте сервиса
defaultResources []Resource
defaultProviders []Provider
resources Service
spaces spaces.Spaces
orgs organizations.Organizations
// TODO: разобраться, где будет кэш
resourcesCache sync.Map
providersCache sync.Map
}
func NewSpaceResources(
svc Service,
spaces spaces.Spaces,
orgs organizations.Organizations,
defaultResources []Resource,
defaultProviders []Provider,
) *SpaceResources {
return &SpaceResources{
defaultResources: defaultResources,
defaultProviders: defaultProviders,
resources: svc,
spaces: spaces,
orgs: orgs,
resourcesCache: sync.Map{},
providersCache: sync.Map{},
}
}
// Start создать системные провайдеры и ресурсы, доступные для всех организаций по умолчанию
func (m *SpaceResources) Start() { panic("implement me") }
// Stop - закрыть соединения?
func (m *SpaceResources) Stop() { panic("implement me") }
// Allocate - инициализировать ресурсы для пространства.
// Если для пространства не задан идентификатор ресурса, то ресурс будет создан
// по следующему алгоритму:
// - взять список провайдеров, заданных как основные в организации
// - поскольку список провайдеров упорядочен по приоритету, найти первый
// провайдер подходящего типа (KindDatabase/KindObjectStorage)
// - у выбранного провайдера запросить конфигурацию ресурса по умолчанию
//
// Аллокация происходит в синхронном режиме и может занимать значительное время
func (m *SpaceResources) Allocate(ctx context.Context, spaceID string) (*spaces.Resources, error) {
space, err := m.spaces.Get(ctx, spaceID)
if err != nil {
return nil, errors.Wrap(err, "get space")
}
org, err := m.orgs.Get(ctx, space.OrgID)
if err != nil {
return nil, errors.Wrap(err, "get organization")
}
res := space.Resources
// если resourceID уже заполнено, то предполагается, что ответственность за создание ресурса лежит на пользователе
if res.DatabaseResourceID == "" {
res.DatabaseResourceID, err = m.createSpaceResource(ctx, KindDatabase, space, org)
if err != nil {
return nil, err
}
}
if res.ObjectStorageResourceID == "" {
res.ObjectStorageResourceID, err = m.createSpaceResource(ctx, KindObjectStorage, space, org)
if err != nil {
return nil, err
}
}
return res, nil
}
func (m *SpaceResources) createSpaceResource(ctx context.Context, kind Kind, space *spaces.Space, org *organizations.Organization) (string, error) {
providers, err := m.resources.FindProviders(ctx, &Filter{ID: org.Providers})
if err != nil {
return "", err
}
var provider Provider
for _, p := range providers {
if data.Contains(kind, p.Kind()) {
provider = p
break
}
}
if provider == nil {
return "", errors.New("todo")
}
resource, err := provider.GetDefaultResource(ctx, space, kind)
if err != nil {
return "", err
}
resource.SetName(fmt.Sprintf("Resource kind: '%s', space: '%s'", kind, space.ID))
if resource, err = m.resources.CreateResource(ctx, resource); err != nil {
return "", err
}
return resource.GetID(), nil
}
func (m *SpaceResources) GetDatabaseResource(ctx context.Context, spaceID string) (*ConnectedResource, error) {
return m.getConnectedResource(ctx, spaceID, KindDatabase)
}
func (m *SpaceResources) GetObjectStorageResource(ctx context.Context, spaceID string) (*ConnectedResource, error) {
return m.getConnectedResource(ctx, spaceID, KindObjectStorage)
}
func (m *SpaceResources) getConnectedResource(ctx context.Context, spaceID string, kind Kind) (*ConnectedResource, error) {
if v, ok := m.resourcesCache.Load(spaceID + "-" + kind.String()); ok {
return v.(*ConnectedResource), nil
}
space, err := m.spaces.Get(ctx, spaceID)
if err != nil {
return nil, errors.Wrap(err, "get space")
}
if space.Resources == nil {
return nil, errors.New("todo")
}
var resourceID string
switch kind {
case KindDatabase:
resourceID = space.Resources.DatabaseResourceID
case KindObjectStorage:
resourceID = space.Resources.ObjectStorageResourceID
}
if resourceID == "" {
return nil, errors.New("todo")
}
res, err := m.resources.GetResource(ctx, resourceID)
if err != nil {
return nil, err
}
// где-то должен быть кэш, из которого будет доставаться провайдер
provider, err := m.resources.GetProvider(ctx, res.GetProviderID())
if err != nil {
return nil, err
}
r, err := provider.GetConnectedResource(ctx, res)
if err != nil {
return nil, err
}
m.resourcesCache.Store(spaceID+"-"+kind.String(), res)
return r, nil
}
// TODO - где кэш провайдеров? учесть, что настройки провайдера/ресурса изменились?
// func (m *SpaceResources) getProvider(ctx context.Context, providerID string) (*Provider, error) {
// if v, ok := m.providersCache.Load(providerID); ok {
// return v.(*Provider), nil
// }
// provider, err := m.GetProvider(ctx, providerID)
// if err != nil {
// // todo
// }
// m.providersCache.Store(providerID, provider)
// return provider, nil
// }
func (m *SpaceResources) Deallocate(ctx context.Context, spaceID string) (err error) {
space, err := m.spaces.Get(ctx, spaceID)
if err != nil {
return errors.Wrap(err, "get space")
}
if space.Resources.DatabaseResourceID != "" {
err = m.resources.DeleteResource(ctx, space.Resources.DatabaseResourceID)
if err != nil {
// todo
}
}
if space.Resources.ObjectStorageResourceID != "" {
err = m.resources.DeleteResource(ctx, space.Resources.ObjectStorageResourceID)
if err != nil {
// todo
}
}
return nil
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment