Skip to content

Commit

Permalink
Merge pull request #1208 from threefoldtech/development_ncgateway_squash
Browse files Browse the repository at this point in the history
Nextcloud with disk and gateway
  • Loading branch information
scottyeager authored Oct 23, 2023
2 parents e367620 + d457202 commit 6701395
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 107 deletions.
2 changes: 1 addition & 1 deletion packages/playground/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ const routes: AppRoute[] = [
{ title: "Discourse", icon: "discourse.png", route: "/discourse" },
{ title: "Taiga", icon: "taiga.png", route: "/taiga" },
{ title: "Owncloud", icon: "owncloud.png", route: "/owncloud" },
// { title: "Nextcloud", icon: "nextcloud.png", route: "/nextcloud" },
{ title: "Nextcloud", icon: "nextcloud.png", route: "/nextcloud" },
{ title: "Presearch", icon: "presearch.png", route: "/presearch" },
{ title: "Subsquid", icon: "subsquid.png", route: "/subsquid" },
{ title: "Casperlabs", icon: "casperlabs.png", route: "/casperlabs" },
Expand Down
1 change: 1 addition & 0 deletions packages/playground/src/constants/deployment_list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export const deploymentListEnvironments = {

nextcloud: {
SSH_KEY: _ssh,
NEXTCLOUD_AIO_LINK: "Nextcloud Setup",
NEXTCLOUD_DOMAIN: "Nextcloud Domain",
},

Expand Down
1 change: 1 addition & 0 deletions packages/playground/src/utils/delete_deployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export function solutionHasGateway(projectName: ProjectName) {
ProjectName.Subsquid,
ProjectName.Taiga,
ProjectName.Wordpress,
ProjectName.Nextcloud,
];
return solutions.includes(projectName) ? true : solutions.map(s => s.toLowerCase()).includes(projectName);
}
8 changes: 7 additions & 1 deletion packages/playground/src/weblets/tf_deployment_list.vue
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,15 @@
/>
<IconActionBtn
tooltip="Open Nextcloud"
color="anchor"
icon="mdi-web"
:href="'https://' + item.value[0].env.NEXTCLOUD_DOMAIN"
/>
<IconActionBtn
tooltip="Nextcloud Setup"
color="anchor"
:href="'https://' + item.value[0].env.NEXTCLOUD_DOMAIN + ':8443'"
icon="mdi-view-dashboard"
:href="'https://' + item.value[0].env.NEXTCLOUD_AIO_LINK"
/>
</template>

Expand Down
234 changes: 129 additions & 105 deletions packages/playground/src/weblets/tf_nextcloud.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
title-image="images/icons/nextcloud.png"
>
<template #title>Deploy a Nextcloud All-in-One Instance </template>

<form-validator v-model="valid">
<input-validator
:value="name"
:rules="[
validators.required('Name is required.'),
validators.isLowercase('Name should consist of lowercase letters only.'),
validators.isAlphanumeric('Name should consist of alphabets & numbers only.'),
validators.isAlphanumeric('Name should consist of letters and numbers only.'),
name => validators.isAlpha('Name must start with alphabet char.')(name[0]),
validators.minLength('Name must be at least 2 characters.', 2),
validators.maxLength('Name cannot exceed 15 characters.', 15),
Expand All @@ -28,199 +29,222 @@
</input-tooltip>
</input-validator>

<input-validator
:value="domain"
:rules="[
validators.required('Domain is required.'),
validators.pattern('Please provide a valid domain.', {
pattern: /^\b((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}\b$/,
}),
]"
#="{ props }"
>
<input-tooltip tooltip="Domain name.">
<v-text-field label="Domain" v-model="domain" v-bind="props" />
</input-tooltip>
</input-validator>

<v-alert type="warning" variant="tonal" class="mb-6">
<p :style="{ maxWidth: '880px' }">Write a valid domain (e.g. "example.com").</p>
<p :style="{ maxWidth: '880px' }">
After deployment, add a DNS A record (Host: "@", Value: &lt;Public IPv4 Address&gt;) to your domain to access
Nextcloud.
</p>

<p class="font-weight-bold mt-4">
For more information, read the
<a
target="_blank"
href="https://www.manual.grid.tf/playground/nextcloud.html#set-the-dns-record"
:style="{ color: 'inherit' }"
>
documentation</a
>.
</p>
</v-alert>

<SelectSolutionFlavor
v-model="solution"
:minimum="{ cpu: 2, memory: 1024 * 4, disk: 50 }"
:standard="{ cpu: 4, memory: 1024 * 8, disk: 500 }"
:recommended="{ cpu: 4, memory: 1024 * 16, disk: 1000 }"
v-model="solution"
:disabled="loadingFarm"
/>
<Networks v-model:ipv4="ipv4" :disabled="loadingFarm" />

<Network v-model:planetary="planetary" v-model:wireguard="wireguard" ref="network" />
<input-tooltip
inline
tooltip="Click to know more about dedicated nodes."
href="https://manual.grid.tf/dashboard/portal/dashboard_portal_dedicated_nodes.html"
>
<v-switch color="primary" inset label="Dedicated" v-model="dedicated" hide-details />
<v-switch color="primary" inset label="Dedicated" v-model="dedicated" :disabled="loadingFarm" hide-details />
</input-tooltip>

<input-tooltip inline tooltip="Renting capacity on certified nodes is charged 25% extra.">
<v-switch color="primary" inset label="Certified" v-model="certified" hide-details />
<v-switch color="primary" inset label="Certified" v-model="certified" :disabled="loadingFarm" hide-details />
</input-tooltip>

<SelectFarmManager>
<SelectFarm
:filters="{
cpu: solution?.cpu,
memory: solution?.memory,
publicIp: ipv4,
ssd: (solution?.disk ?? 0) + rootFilesystemSize,
rentedBy: dedicated ? profileManager.profile?.twinId : undefined,
certified: certified,
}"
v-model="farm"
/>
<SelectNode
v-model="selectedNode"
:filters="{
farmId: farm?.farmID,
cpu: solution?.cpu,
memory: solution?.memory,
diskSizes: [solution?.disk],
ipv4: ipv4,
rentedBy: dedicated ? profileManager.profile?.twinId : undefined,
certified: certified,
}"
:root-file-system-size="rootFilesystemSize"
/>
<FarmGateWayManager>
<SelectFarm
:filters="{
cpu: solution?.cpu,
memory: solution?.memory,
ssd: (solution?.disk ?? 0) + rootFilesystemSize,
publicIp: ipv4,
rentedBy: dedicated ? profileManager.profile?.twinId : undefined,
certified: certified,
}"
v-model="farm"
v-model:loading="loadingFarm"
/>
<SelectNode
v-model="selectedNode"
:filters="{
farmId: farm?.farmID,
cpu: solution?.cpu,
memory: solution?.memory,
diskSizes: [solution?.disk],
rentedBy: dedicated ? profileManager.profile?.twinId : undefined,
certified: certified,
}"
:root-file-system-size="rootFilesystemSize"
/>
<DomainName :hasIPv4="ipv4" ref="domainNameCmp" />
</FarmGateWayManager>
</SelectFarmManager>
</form-validator>

<template #footer-actions>
<v-btn color="primary" variant="tonal" :disabled="!valid" @click="deploy">Deploy</v-btn>
<v-btn
color="primary"
variant="tonal"
@click="deploy(domainNameCmp?.domain, domainNameCmp?.customDomain)"
:disabled="!valid"
>
Deploy
</v-btn>
</template>
</weblet-layout>
</template>

<script lang="ts" setup>
import type { GridClient } from "@threefold/grid_client";
import { computed, type Ref, ref } from "vue";
import Network from "../components/networks.vue";
import SelectSolutionFlavor from "../components/select_solution_flavor.vue";
import { useLayout } from "../components/weblet_layout.vue";
import { useProfileManager } from "../stores";
import { type Farm, type Flist, ProjectName } from "../types";
import { deployVM, type Disk, type Env } from "../utils/deploy_vm";
import type { Farm, Flist, GatewayNode, solutionFlavor as SolutionFlavor } from "../types";
import { ProjectName } from "../types";
import { deployVM } from "../utils/deploy_vm";
import { deployGatewayName, getSubdomain, rollbackDeployment } from "../utils/gateway";
import { getGrid } from "../utils/grid";
import { normalizeError } from "../utils/helpers";
import { generateName } from "../utils/strings";
const layout = useLayout();
const valid = ref(false);
const domain = ref("");
const profileManager = useProfileManager();
const name = ref(generateName({ prefix: "nc" }));
const solution = ref() as Ref<SolutionFlavor>;
const farm = ref() as Ref<Farm>;
const flist: Flist = {
value: "https://hub.grid.tf/tf-official-apps/threefoldtech-nextcloudaio-latest.flist",
entryPoint: "/sbin/zinit init",
};
const name = ref(generateName({ prefix: "nc" }));
const ipv4 = ref(true);
const planetary = ref(true);
const wireguard = ref(true);
const farm = ref() as Ref<Farm>;
const network = ref();
const dedicated = ref(false);
const certified = ref(false);
const selectedNode = ref() as Ref<INode>;
const rootFilesystemSize = 40;
async function deploy() {
const ipv4 = ref(false);
const loadingFarm = ref(false);
const domainNameCmp = ref();
const rootFilesystemSize = computed(() => rootFs(solution.value?.cpu ?? 0, solution.value?.memory ?? 0));
function finalize(deployment: any) {
layout.value.reloadDeploymentsList();
layout.value.setStatus(
"success",
"Successfully deployed a Nextcloud instance. Under Actions, click on the button Nextcloud Setup to set up Nextcloud. After installation, you can access the Nextcloud instance by clicking on the Open Nextcloud button or navigating to your Nextcloud domain.",
);
layout.value.openDialog(deployment, deploymentListEnvironments.nextcloud);
}
async function deploy(gatewayName: GatewayNode, customDomain: boolean) {
layout.value.setStatus("deploy");
const projectName = ProjectName.Nextcloud.toLowerCase() + "/" + name.value;
const subdomain = getSubdomain({
deploymentName: name.value,
projectName,
twinId: profileManager.profile!.twinId,
});
const domain = customDomain ? gatewayName.domain : subdomain + "." + gatewayName.domain;
const has_gateway = !(customDomain && ipv4.value);
const aio_link = domain + "/aio";
let grid: GridClient | null;
let vm: any;
try {
const grid = await getGrid(profileManager.profile!, projectName);
layout.value?.validateSSH();
grid = await getGrid(profileManager.profile!, projectName);
await layout.value.validateBalance(grid!);
const vm = await deployVM(grid!, {
vm = await deployVM(grid!, {
name: name.value,
network: {
addAccess: wireguard.value,
addAccess: !!gatewayName.id,
accessNodeId: gatewayName.id,
},
machines: [
{
name: name.value,
cpu: solution.value.cpu,
memory: solution.value.memory,
flist: flist.value,
entryPoint: flist.entryPoint,
farmId: farm.value.farmID,
farmName: farm.value.name,
country: farm.value.country,
disks: [
{
size: solution.value.disk,
mountPoint: "/mnt/next_cloud",
mountPoint: "/mnt/data",
},
],
flist: flist.value,
entryPoint: flist.entryPoint,
farmId: farm.value.farmID,
farmName: farm.value.name,
publicIpv4: ipv4.value,
country: farm.value.country,
envs: [
{ key: "SSH_KEY", value: profileManager.profile!.ssh },
{ key: "NEXTCLOUD_DOMAIN", value: domain.value },
{ key: "NEXTCLOUD_DOMAIN", value: domain },
{ key: "NEXTCLOUD_AIO_LINK", value: aio_link },
{ key: "GATEWAY", value: String(has_gateway) },
{ key: "IPV4", value: String(ipv4.value) },
],
planetary: planetary.value,
publicIpv4: ipv4.value,
rootFilesystemSize: rootFilesystemSize,
nodeId: selectedNode.value.nodeId,
rentedBy: dedicated.value ? grid!.twinId : undefined,
certified: certified.value,
rootFilesystemSize: rootFilesystemSize.value,
},
],
});
} catch (e) {
return layout.value.setStatus("failed", normalizeError(e, "Failed to deploy a Nextcloud instance."));
}
if (customDomain && ipv4.value) {
vm[0].customDomain = gatewayName.domain;
finalize(vm);
return;
}
try {
layout.value.setStatus("deploy", "Preparing to deploy gateway...");
await deployGatewayName(grid!, {
name: subdomain,
nodeId: gatewayName.id!,
ip: vm[0].interfaces[0].ip,
port: 80,
networkName: vm[0].interfaces[0].network,
fqdn: gatewayName?.useFQDN ? gatewayName?.domain : undefined,
tlsPassthrough: false,
});
layout.value.reloadDeploymentsList();
layout.value.setStatus(
"success",
'Successfully deployed a Nextcloud AIO instance. Make sure to add a DNS A record (Host: "@", Value: <Public IPv4 Address>) to your domain. After DNS propagation, under Actions, click Open Nextcloud.',
);
layout.value.openDialog(vm, deploymentListEnvironments.vm);
finalize(vm);
} catch (e) {
layout.value.setStatus("failed", normalizeError(e, "Failed to deploy Nextcloud."));
layout.value.setStatus("deploy", "Rolling back due to fail to deploy gateway...");
await rollbackDeployment(grid!, name.value);
layout.value.setStatus("failed", normalizeError(e, "Failed to deploy a Nextcloud instance."));
}
}
</script>

<script lang="ts">
import ExpandableLayout from "../components/expandable_layout.vue";
import DomainName from "../components/domain_name.vue";
import FarmGateWayManager from "../components/farm_gateway_manager.vue";
import Networks from "../components/networks.vue";
import SelectFarm from "../components/select_farm.vue";
import SelectFarmManager from "../components/select_farm_manager.vue";
import SelectNode from "../components/select_node.vue";
import SelectSolutionFlavor from "../components/select_solution_flavor.vue";
import { deploymentListEnvironments } from "../constants";
import type { solutionFlavor as SolutionFlavor } from "../types";
import type { INode } from "../utils/filter_nodes";
import { normalizeError } from "../utils/helpers";
const solution = ref() as Ref<SolutionFlavor>;
import rootFs from "../utils/root_fs";
export default {
name: "TfNextcloud",
name: "TFNextcloud",
components: {
SelectSolutionFlavor,
DomainName,
FarmGateWayManager,
Networks,
SelectFarm,
SelectNode,
SelectFarmManager,
Expand Down

0 comments on commit 6701395

Please sign in to comment.