Skip to content

Commit

Permalink
Object Store creation templates.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmchilton committed Apr 14, 2023
1 parent af81541 commit 7d8b7af
Show file tree
Hide file tree
Showing 26 changed files with 1,310 additions and 35 deletions.
54 changes: 54 additions & 0 deletions client/src/components/ObjectStore/Templates/CreateForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script lang="ts" setup>
import { computed } from "vue";
import type { ObjectStoreTemplateSummary } from "../services";
import FormCard from "@/components/Form/FormCard.vue";
import FormDisplay from "@/components/Form/FormDisplay.vue";
interface CreateFormProps {
template: ObjectStoreTemplateSummary;
}
const props = defineProps<CreateFormProps>();
const title = "Create new object store for your data";
const submitTitle = "Submit";
const inputs = computed(() => {
const form = [];
const variables = props.template.variables ?? [];
const secrets = props.template.secrets ?? [];
for (const variable of variables) {
// TODO: markdown on help
form.push({
name: variable.name,
type: "text",
help: variable.help,
});
}
for (const secret of secrets) {
// TODO: markdown on help
form.push({
name: secret.name,
type: "password",
help: secret.help,
});
}
return form;
});
async function onSubmit() {
console.log("Submitted!");
}
</script>
<template>
<div>
<FormCard :title="title">
<template v-slot:body>
<FormDisplay :inputs="inputs" />
</template>
</FormCard>
<div class="mt-3">
<b-button id="submit" variant="primary" class="mr-1" @click="onSubmit()">
{{ submitTitle }}
</b-button>
</div>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<script lang="ts" setup>
import { computed, ref, onMounted } from "vue";
import SelectTemplate from "./SelectTemplate.vue";
import CreateForm from "./CreateForm.vue";
import { type ObjectStoreTemplateSummaries, type ObjectStoreTemplateSummary, getTemplates } from "../services";
import LoadingSpan from "@/components/LoadingSpan.vue";
import { errorMessageAsString } from "@/utils/simple-error";
const loadingTemplatesInfoMessage = "Loading object store templates";
const templateId = ref<string | null>(null);
const templates = ref<ObjectStoreTemplateSummaries>([]);
const currentTemplate = computed<ObjectStoreTemplateSummary | null>(() => {
const tId = templateId.value;
if (tId) {
for (const template of templates.value) {
if (template.id == tId) {
return template;
}
}
}
return null;
});
const loading = ref(true);
const error = ref<string | null>(null);
function handleError(e: unknown) {
const errorMessage = errorMessageAsString(e);
error.value = errorMessage;
}
onMounted(async () => {
try {
const data = await getTemplates();
templates.value = data;
loading.value = false;
} catch (e) {
handleError(e);
}
});
async function chooseTemplate(selectTemplateId: string) {
templateId.value = selectTemplateId;
}
</script>
<template>
<b-container fluid class="p-0">
<loading-span v-if="loading" :message="loadingTemplatesInfoMessage" />
<div v-else>
<b-alert v-if="error" variant="danger" class="object-store-selection-error" show>
{{ error }}
</b-alert>

<div v-if="currentTemplate == null">
<select-template :templates="templates" @onSubmit="chooseTemplate" />
</div>

<div v-else>
<create-form :template="currentTemplate" />
</div>
</div>
</b-container>
</template>
52 changes: 52 additions & 0 deletions client/src/components/ObjectStore/Templates/SelectTemplate.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<script lang="ts" setup>
import type { ObjectStoreTemplateSummaries } from "../services";
interface SelectTemplateProps {
templates: ObjectStoreTemplateSummaries;
}
withDefaults(defineProps<SelectTemplateProps>(), {});
const popoverPlacement = "rightbottom";
const emit = defineEmits<{
(e: "onSubmit", id: string): void;
}>();
async function handleSubmit(templateId: string) {
emit("onSubmit", templateId);
}
</script>

<template>
<div>
<b-row>
<b-col cols="7">
<b-button-group vertical size="lg" style="width: 100%">
<b-button
v-for="template in templates"
:id="`object-store-template-button-${template.id}`"
:key="template.id"
class="object-store-template-select-button"
:data-template-id="template.id"
@click="handleSubmit(template.id)"
>{{ template.name }}
</b-button>
</b-button-group>
</b-col>
<b-col cols="5">
<p v-localize style="float: right">Some cool text will go here or maybe it will be bigger!</p>
</b-col>
</b-row>
<b-popover
v-for="template in templates"
:key="template.id"
:target="`object-store-template-button-${template.id}`"
triggers="hover"
boundary="window"
:placement="popoverPlacement">
<template v-slot:title>{{ template.name }}</template>
TODO: put interesting information in this popup.
</b-popover>
</div>
</template>
10 changes: 10 additions & 0 deletions client/src/components/ObjectStore/services.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import type { components } from "@/schema";
import { fetcher } from "@/schema/fetcher";

const getObjectStores = fetcher.path("/api/object_stores").method("get").create();
const getObjectStoreTemplates = fetcher.path("/api/object_store_templates").method("get").create();

export type ObjectStoreTemplateSummaries = components["schemas"]["ObjectStoreTemplateSummaries"];
export type ObjectStoreTemplateSummary = components["schemas"]["ObjectStoreTemplateSummary"];

export async function getSelectableObjectStores() {
const { data } = await getObjectStores({ selectable: true });
return data;
}

export async function getTemplates(): Promise<ObjectStoreTemplateSummaries> {
const { data } = await getObjectStoreTemplates({});
return data;
}
5 changes: 5 additions & 0 deletions client/src/entry/analysis/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import VisualizationPublished from "components/Visualizations/VisualizationPubli
import WorkflowImport from "components/Workflow/WorkflowImport";
import WorkflowList from "components/Workflow/WorkflowList";
import WorkflowPublished from "components/Workflow/WorkflowPublished";
import CreateUserObjectStore from "components/ObjectStore/Templates/CreateUserObjectStore";
import { APIKey } from "components/User/APIKey";
import { CloudAuth } from "components/User/CloudAuth";
import { ExternalIdentities } from "components/User/ExternalIdentities";
Expand Down Expand Up @@ -278,6 +279,10 @@ export function getRouter(Galaxy) {
termsUrl: Galaxy.config.terms_url,
},
},
{
path: "object_store/create",
component: CreateUserObjectStore,
},
{
path: "pages/create",
component: FormGeneric,
Expand Down
28 changes: 24 additions & 4 deletions lib/galaxy/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from galaxy.managers.jobs import JobSearch
from galaxy.managers.libraries import LibraryManager
from galaxy.managers.library_datasets import LibraryDatasetsManager
from galaxy.managers.object_store_instances import UserObjectStoreResolverImpl
from galaxy.managers.roles import RoleManager
from galaxy.managers.session import GalaxySessionManager
from galaxy.managers.tasks import (
Expand Down Expand Up @@ -80,7 +81,10 @@
from galaxy.objectstore import (
BaseObjectStore,
build_object_store_from_config,
UserObjectStoreResolver,
UserObjectStoresAppConfig,
)
from galaxy.objectstore.templates import ConfiguredObjectStoreTemplates
from galaxy.queue_worker import (
GalaxyQueueWorker,
send_local_control_task,
Expand Down Expand Up @@ -236,8 +240,6 @@ def __init__(self, fsmon=False, **kwargs) -> None:
# Read config file and check for errors
self.config = self._register_singleton(config.GalaxyAppConfiguration, config.GalaxyAppConfiguration(**kwargs))
self.config.check()
self._configure_object_store(fsmon=True)
self._register_singleton(BaseObjectStore, self.object_store)
config_file = kwargs.get("global_conf", {}).get("__file__", None)
if config_file:
log.debug('Using "galaxy.ini" config file: %s', config_file)
Expand All @@ -249,6 +251,10 @@ def __init__(self, fsmon=False, **kwargs) -> None:
self._register_singleton(GalaxyModelMapping, self.model)
self._register_singleton(galaxy_scoped_session, self.model.context)
self._register_singleton(install_model_scoped_session, self.install_model.context)
self.vault = self._register_singleton(Vault, VaultFactory.from_app(self)) # type: ignore[type-abstract]
self._configure_object_store(fsmon=True)
self._register_singleton(BaseObjectStore, self.object_store)
galaxy.model.Dataset.object_store = self.object_store

def configure_fluent_log(self):
if self.config.fluent_log:
Expand Down Expand Up @@ -389,6 +395,21 @@ def _configure_datatypes_registry(self, use_display_applications=True, use_conve
)

def _configure_object_store(self, **kwds):
app_config = UserObjectStoresAppConfig(
jobs_directory=self.config.jobs_directory,
new_file_path=self.config.new_file_path,
umask=self.config.umask,
)
self._register_singleton(UserObjectStoresAppConfig, app_config)
user_object_store_resolver = self._register_abstract_singleton(
UserObjectStoreResolver, UserObjectStoreResolverImpl # type: ignore[type-abstract]
) # Ignored because of https://github.com/python/mypy/issues/4717

self.object_store_templates = self._register_singleton(
ConfiguredObjectStoreTemplates, ConfiguredObjectStoreTemplates.from_app_config(self.config)
)
# kwds["object_store_templates"] = self.object_store_templates
kwds["user_object_store_resolver"] = user_object_store_resolver
self.object_store = build_object_store_from_config(self.config, **kwds)

def _configure_security(self):
Expand Down Expand Up @@ -430,7 +451,7 @@ def _configure_models(self, check_migrate_databases=False, config_file=None):

self.model = mapping.configure_model_mapping(
self.config.file_path,
self.object_store,
None, # setting object store later now...
self.config.use_pbkdf2,
engine,
combined_install_database,
Expand Down Expand Up @@ -549,7 +570,6 @@ def __init__(self, configure_logging=True, use_converters=True, use_display_appl
ConfiguredFileSources, ConfiguredFileSources.from_app_config(self.config)
)

self.vault = self._register_singleton(Vault, VaultFactory.from_app(self)) # type: ignore[type-abstract]
# Load security policy.
self.security_agent = self.model.security_agent
self.host_security_agent = galaxy.model.security.HostAgent(
Expand Down
15 changes: 15 additions & 0 deletions lib/galaxy/config/schemas/config_schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,21 @@ mapping:
desc: |
FileSource plugins described embedded into Galaxy's config.
object_store_templates_config_file:
type: str
default: object_store_templates.yml
path_resolves_to: config_dir
required: false
desc: |
Configured Object Store templates configuration file.
object_store_templates:
type: seq
sequence:
- type: any
desc: |
Configured Object Store templates embedded into Galaxy's config.
enable_mulled_containers:
type: bool
default: true
Expand Down
1 change: 1 addition & 0 deletions lib/galaxy/jobs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1681,6 +1681,7 @@ def split_object_stores(output_name):

require_shareable = job.requires_shareable_storage(self.app.security_agent)
if not split_object_stores:
log.info(f"\n\n\n\nNot splitting stores and have object_store_id {object_store_id}\n\n")
object_store_populator = ObjectStorePopulator(self.app, user)

if object_store_id:
Expand Down
Loading

0 comments on commit 7d8b7af

Please sign in to comment.