Implement getSharedStorage operation & use it in the add-on

Implement the `getSharedStorage` operation in the Manager, and use it in
the add-on to get the shared storage location in a way that makes sense
for the platform of the user.

Manifest task: T100196
This commit is contained in:
Sybren A. Stüvel 2022-08-31 11:44:37 +02:00
parent 31769bcdf2
commit 31cf0a4ecc
4 changed files with 115 additions and 10 deletions

View File

@ -4,6 +4,7 @@
import logging import logging
import dataclasses import dataclasses
import platform
from typing import TYPE_CHECKING, Optional from typing import TYPE_CHECKING, Optional
from urllib3.exceptions import HTTPError, MaxRetryError from urllib3.exceptions import HTTPError, MaxRetryError
@ -16,20 +17,20 @@ if TYPE_CHECKING:
from flamenco.manager import ApiClient as _ApiClient from flamenco.manager import ApiClient as _ApiClient
from flamenco.manager.models import ( from flamenco.manager.models import (
FlamencoVersion as _FlamencoVersion, FlamencoVersion as _FlamencoVersion,
ManagerConfiguration as _ManagerConfiguration, SharedStorageLocation as _SharedStorageLocation,
) )
from .preferences import FlamencoPreferences as _FlamencoPreferences from .preferences import FlamencoPreferences as _FlamencoPreferences
else: else:
_ApiClient = object _ApiClient = object
_FlamencoPreferences = object _FlamencoPreferences = object
_FlamencoVersion = object _FlamencoVersion = object
_ManagerConfiguration = object _SharedStorageLocation = object
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
class ManagerInfo: class ManagerInfo:
version: Optional[_FlamencoVersion] = None version: Optional[_FlamencoVersion] = None
config: Optional[_ManagerConfiguration] = None storage: Optional[_SharedStorageLocation] = None
error: str = "" error: str = ""
@classmethod @classmethod
@ -38,9 +39,9 @@ class ManagerInfo:
@classmethod @classmethod
def with_info( def with_info(
cls, version: _FlamencoVersion, config: _ManagerConfiguration cls, version: _FlamencoVersion, storage: _SharedStorageLocation
) -> "ManagerInfo": ) -> "ManagerInfo":
return cls(version=version, config=config) return cls(version=version, storage=storage)
def flamenco_api_client(manager_url: str) -> _ApiClient: def flamenco_api_client(manager_url: str) -> _ApiClient:
@ -120,13 +121,14 @@ def ping_manager(
# Do a late import, so that the API is only imported when actually used. # Do a late import, so that the API is only imported when actually used.
from flamenco.manager import ApiException from flamenco.manager import ApiException
from flamenco.manager.apis import MetaApi from flamenco.manager.apis import MetaApi
from flamenco.manager.models import FlamencoVersion, ManagerConfiguration from flamenco.manager.models import FlamencoVersion, SharedStorageLocation
meta_api = MetaApi(api_client) meta_api = MetaApi(api_client)
error = "" error = ""
try: try:
version: FlamencoVersion = meta_api.get_version() version: FlamencoVersion = meta_api.get_version()
config: ManagerConfiguration = meta_api.get_configuration() storage: SharedStorageLocation = meta_api.get_shared_storage(
"users", platform.system().lower())
except ApiException as ex: except ApiException as ex:
error = "Manager cannot be reached: %s" % ex error = "Manager cannot be reached: %s" % ex
except MaxRetryError as ex: except MaxRetryError as ex:
@ -142,10 +144,10 @@ def ping_manager(
return ManagerInfo.with_error(error) return ManagerInfo.with_error(error)
# Store whether this Manager supports the Shaman API. # Store whether this Manager supports the Shaman API.
prefs.is_shaman_enabled = config.shaman_enabled prefs.is_shaman_enabled = storage.shaman_enabled
prefs.job_storage = config.storage_location prefs.job_storage = storage.location
report = "%s version %s found" % (version.name, version.version) report = "%s version %s found" % (version.name, version.version)
window_manager.flamenco_status_ping = report window_manager.flamenco_status_ping = report
return ManagerInfo.with_info(version, config) return ManagerInfo.with_info(version, storage)

View File

@ -68,6 +68,24 @@ func (f *Flamenco) GetVariables(e echo.Context, audience api.ManagerVariableAudi
return e.JSON(http.StatusOK, apiVars) return e.JSON(http.StatusOK, apiVars)
} }
func (f *Flamenco) GetSharedStorage(e echo.Context, audience api.ManagerVariableAudience, platform string) error {
location := f.config.EffectiveStoragePath()
feeder := make(chan string, 1)
receiver := make(chan string, 1)
feeder <- location
close(feeder)
f.config.ExpandVariables(feeder, receiver, config.VariableAudience(audience), config.VariablePlatform(platform))
return e.JSON(http.StatusOK, api.SharedStorageLocation{
Audience: audience,
Platform: platform,
Location: <-receiver,
ShamanEnabled: f.isShamanEnabled(),
})
}
func (f *Flamenco) CheckSharedStoragePath(e echo.Context) error { func (f *Flamenco) CheckSharedStoragePath(e echo.Context) error {
logger := requestLogger(e) logger := requestLogger(e)

View File

@ -15,6 +15,7 @@ import (
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestGetVariables(t *testing.T) { func TestGetVariables(t *testing.T) {
@ -64,6 +65,89 @@ func TestGetVariables(t *testing.T) {
} }
} }
func TestGetSharedStorage(t *testing.T) {
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
mf := newMockedFlamenco(mockCtrl)
conf := config.GetTestConfig(func(c *config.Conf) {
// Test with a Manager on Windows.
c.MockCurrentGOOSForTests("windows")
// Set up a two-way variable to do the mapping.
c.Variables["shared_storage_mapping"] = config.Variable{
IsTwoWay: true,
Values: []config.VariableValue{
{Value: "/user/shared/storage", Platform: config.VariablePlatformLinux, Audience: config.VariableAudienceUsers},
{Value: "/worker/shared/storage", Platform: config.VariablePlatformLinux, Audience: config.VariableAudienceWorkers},
{Value: `S:\storage`, Platform: config.VariablePlatformWindows, Audience: config.VariableAudienceAll},
},
}
})
mf.config.EXPECT().Get().Return(&conf).AnyTimes()
mf.config.EXPECT().EffectiveStoragePath().Return(`S:\storage\flamenco`).AnyTimes()
{ // Test user client on Linux.
mf.config.EXPECT().
ExpandVariables(gomock.Any(), gomock.Any(), config.VariableAudienceUsers, config.VariablePlatformLinux).
Do(func(inputChannel <-chan string, outputChannel chan<- string, audience config.VariableAudience, platform config.VariablePlatform) {
// Defer to the actual ExpandVariables() implementation of the above config.
conf.ExpandVariables(inputChannel, outputChannel, audience, platform)
})
mf.shaman.EXPECT().IsEnabled().Return(false)
echoCtx := mf.prepareMockedRequest(nil)
err := mf.flamenco.GetSharedStorage(echoCtx, api.ManagerVariableAudienceUsers, "linux")
require.NoError(t, err)
assertResponseJSON(t, echoCtx, http.StatusOK, api.SharedStorageLocation{
Location: "/user/shared/storage/flamenco",
Audience: api.ManagerVariableAudienceUsers,
Platform: "linux",
})
}
{ // Test worker client on Linux with Shaman enabled.
mf.config.EXPECT().
ExpandVariables(gomock.Any(), gomock.Any(), config.VariableAudienceWorkers, config.VariablePlatformLinux).
Do(func(inputChannel <-chan string, outputChannel chan<- string, audience config.VariableAudience, platform config.VariablePlatform) {
// Defer to the actual ExpandVariables() implementation of the above config.
conf.ExpandVariables(inputChannel, outputChannel, audience, platform)
})
mf.shaman.EXPECT().IsEnabled().Return(true)
echoCtx := mf.prepareMockedRequest(nil)
err := mf.flamenco.GetSharedStorage(echoCtx, api.ManagerVariableAudienceWorkers, "linux")
require.NoError(t, err)
assertResponseJSON(t, echoCtx, http.StatusOK, api.SharedStorageLocation{
Location: "/worker/shared/storage/flamenco",
Audience: api.ManagerVariableAudienceWorkers,
Platform: "linux",
ShamanEnabled: true,
})
}
{ // Test user client on Windows.
mf.config.EXPECT().
ExpandVariables(gomock.Any(), gomock.Any(), config.VariableAudienceUsers, config.VariablePlatformWindows).
Do(func(inputChannel <-chan string, outputChannel chan<- string, audience config.VariableAudience, platform config.VariablePlatform) {
// Defer to the actual ExpandVariables() implementation of the above config.
conf.ExpandVariables(inputChannel, outputChannel, audience, platform)
})
mf.shaman.EXPECT().IsEnabled().Return(false)
echoCtx := mf.prepareMockedRequest(nil)
err := mf.flamenco.GetSharedStorage(echoCtx, api.ManagerVariableAudienceUsers, "windows")
require.NoError(t, err)
assertResponseJSON(t, echoCtx, http.StatusOK, api.SharedStorageLocation{
Location: `S:\storage\flamenco`,
Audience: api.ManagerVariableAudienceUsers,
Platform: "windows",
})
}
}
func TestCheckSharedStoragePath(t *testing.T) { func TestCheckSharedStoragePath(t *testing.T) {
mf, finish := metaTestFixtures(t) mf, finish := metaTestFixtures(t)
defer finish() defer finish()

View File

@ -70,6 +70,7 @@ func newMockedFlamenco(mockCtrl *gomock.Controller) mockedFlamenco {
logStorage: logStore, logStorage: logStore,
config: cs, config: cs,
stateMachine: sm, stateMachine: sm,
shaman: sha,
clock: clock, clock: clock,
lastRender: lr, lastRender: lr,
localStorage: localStore, localStorage: localStore,