Skip to content
This repository was archived by the owner on Aug 30, 2023. It is now read-only.

Commit 447e49d

Browse files
authored
Merge pull request #428 from threefoldtech/development_mattermost
Mattermost weblet
2 parents 0a7f7a0 + 93dd217 commit 447e49d

File tree

9 files changed

+289
-1
lines changed

9 files changed

+289
-1
lines changed

easy-docs/src/App.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export default class App extends Vue {
4444
"taiga",
4545
"owncloud",
4646
"contractslist",
47+
"mattermost",
4748
];
4849
sidenav: ISidenav | null = null;
4950

easy-docs/src/views/Editor.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ export default class Editor extends Vue {
116116
117117
new Weblet("Peertube", "peertube", "peertube", "deployment"),
118118
new Weblet("Funkwhale", "funkwhale", "funkwhale", "deployment"),
119+
new Weblet("Mattermost", "mattermost", "mattermost", "deployment"),
119120
new Weblet("Taiga", "taiga", "taiga", "deployment"),
120121
new Weblet("Owncloud", "owncloud", "owncloud", "deployment"),
121122
];

src/elements/DeployedList/DeployedList.wc.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
| "caprover"
1414
| "funkwhale"
1515
| "peertube"
16+
| "mattermost"
1617
| "owncloud";
1718
export let tab: TabsType = undefined;
1819
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<svelte:options tag="tf-mattermost" />
2+
3+
<script lang="ts">
4+
import DeployBtn from "../../components/DeployBtn.svelte";
5+
import Input from "../../components/Input.svelte";
6+
import SelectProfile from "../../components/SelectProfile.svelte";
7+
import type { IFormField } from "../../types";
8+
import type { IProfile } from "../../types/Profile";
9+
10+
import Mattermost from "../../types/mattermost";
11+
import Alert from "../../components/Alert.svelte";
12+
import { noActiveProfile } from "../../utils/message";
13+
import SelectNodeId from "../../components/SelectNodeId.svelte";
14+
import deployMattermost from "../../utils/deployMattermost";
15+
import validateName from "../../utils/validateName";
16+
import validateDomainName from "../../utils/validateDomainName";
17+
18+
const currentDeployment = window.configs?.currentDeploymentStore;
19+
const deploymentStore = window.configs?.deploymentStore;
20+
const data = new Mattermost();
21+
const validator = (x: string) => x.trim().length === 0 ? "Value can't be empty." : null; // prettier-ignore
22+
23+
// prettier-ignore
24+
const fields: IFormField[] = [
25+
{ label: "Name", symbol: "name", type: "text", placeholder: "Mattermost name", validator: validateName, invalid: false },
26+
{ label: "SMTP Username", symbol: "username", type: "text", placeholder: "Mattermost Username", validator, invalid: false },
27+
{ label: "SMTP Password", symbol: "password", type: "password", placeholder: "Database & Mattermost Password", validator, invalid: false },
28+
// { label: "Domain", symbol: "domain", type: "text", placeholder: "Site Url", validator, invalid: false },
29+
{ label: "SMTP Server", symbol: "server", type: "text", placeholder: "SMTP server", validator: validateDomainName, invalid: false },
30+
{ label: "SMTP port", symbol: "port", type: "text", placeholder: "SMTP port", validator, invalid: false },
31+
];
32+
33+
let profile: IProfile;
34+
let loading: boolean = false;
35+
let failed: boolean = false;
36+
let success: boolean = false;
37+
let message: string;
38+
$: disabled = data.invalid || data.status !== "valid";
39+
40+
function onDeployMattermost() {
41+
loading = true;
42+
deployMattermost(profile, data)
43+
.then((res) => {
44+
deploymentStore.set(0);
45+
success = true;
46+
})
47+
.catch((err) => {
48+
console.log("Error", err);
49+
failed = true;
50+
message = err.message || err;
51+
})
52+
.finally(() => {
53+
loading = false;
54+
});
55+
}
56+
57+
$: logs = $currentDeployment;
58+
</script>
59+
60+
<SelectProfile on:profile={({ detail }) => (profile = detail)} />
61+
62+
<div style="padding: 15px;">
63+
<form class="box" on:submit|preventDefault={onDeployMattermost}>
64+
<h4 class="is-size-4">Deploy Mattermost</h4>
65+
<hr />
66+
67+
{#if loading || (logs !== null && logs.type === "VM")}
68+
<Alert type="info" message={logs?.message ?? "Loading..."} />
69+
{:else if !profile}
70+
<Alert type="info" message={noActiveProfile} />
71+
{:else if success}
72+
<Alert
73+
type="success"
74+
message="Successfully deployed Mattermost."
75+
deployed={true}
76+
/>
77+
{:else if failed}
78+
<Alert
79+
type="danger"
80+
message={message || "Failed to deploy Mattermost."}
81+
/>
82+
{:else}
83+
{#each fields as field (field.symbol)}
84+
<Input
85+
bind:data={data[field.symbol]}
86+
bind:invalid={field.invalid}
87+
{field}
88+
/>
89+
{/each}
90+
91+
<SelectNodeId
92+
bind:data={data.nodeId}
93+
bind:status={data.status}
94+
bind:nodeSelection={data.selection.type}
95+
{profile}
96+
cpu={2}
97+
ssd={10}
98+
memory={2048}
99+
publicIp={false}
100+
nodes={data.selection.nodes}
101+
filters={data.selection.filters}
102+
on:fetch={({ detail }) => (data.selection.nodes = detail)}
103+
/>
104+
{/if}
105+
106+
<DeployBtn
107+
{disabled}
108+
{loading}
109+
{failed}
110+
{success}
111+
on:click={(e) => {
112+
if (success || failed) {
113+
e.preventDefault();
114+
success = false;
115+
failed = false;
116+
loading = false;
117+
}
118+
}}
119+
/>
120+
</form>
121+
</div>
122+
123+
<style lang="scss" scoped>
124+
@import url("https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css");
125+
</style>

src/elements/Mattermost/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import "./Mattermost.wc.svelte";

src/types/deployedList.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ export default class DeployedList {
108108
});
109109
}
110110

111+
public loadMattermost(): Promise<any[]> {
112+
return this.loadVm().then((vms) => {
113+
return vms.filter((vm) => vm.flist.toLowerCase().includes("mattermost"));
114+
});
115+
}
116+
111117
public loadOwncloud(): Promise<any[]> {
112118
return this.loadVm().then((vms) => {
113119
return vms.filter((vm) => vm.flist.toLowerCase().includes("owncloud"));

src/types/mattermost.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { v4 } from "uuid";
2+
import generatePassword from "../utils/generatePassword";
3+
import isValidInteger from "../utils/isValidInteger";
4+
import NodeID from "./nodeId";
5+
6+
interface IMattermost {
7+
name: string;
8+
username: string;
9+
password: string;
10+
nodeId: number;
11+
domain: string;
12+
server: string;
13+
port: string;
14+
}
15+
16+
export default class Mattermost implements IMattermost {
17+
id = v4();
18+
selection = new NodeID();
19+
status: "invalid" | "valid" = "invalid";
20+
21+
name: string;
22+
username: string;
23+
password: string;
24+
nodeId: number;
25+
domain: string;
26+
server: string;
27+
port: string;
28+
29+
constructor({
30+
name,
31+
username,
32+
password,
33+
nodeId,
34+
domain,
35+
server,
36+
port,
37+
}: Partial<IMattermost> = {}) {
38+
this.name = name || "mm" + this.id.split("-")[0];
39+
this.username = username || "";
40+
this.password = password || generatePassword(10);
41+
this.nodeId = nodeId;
42+
this.domain = domain || "";
43+
this.server = server || "";
44+
this.port = port || "587";
45+
}
46+
47+
get invalid(): boolean {
48+
const { name, username, password, nodeId } = this;
49+
const { domain, server, port } = this;
50+
return (
51+
name.trim() === "" ||
52+
username.trim() === "" ||
53+
password.trim() === "" ||
54+
// domain.trim() === "" ||
55+
server.trim() === "" ||
56+
port.trim() === "" ||
57+
!isValidInteger(port.trim()) ||
58+
!isValidInteger(nodeId)
59+
);
60+
}
61+
}

src/utils/deployMattermost.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { Network } from "../types/kubernetes";
2+
import type Mattermost from "../types/mattermost";
3+
import type { IProfile } from "../types/Profile";
4+
import createNetwork from "./createNetwork";
5+
import deploy from "./deploy";
6+
import { selectGatewayNode } from "./gatewayHelpers";
7+
import getGrid from "./getGrid";
8+
import rootFs from "./rootFs";
9+
const { MachineModel, MachinesModel, GatewayNameModel } =
10+
window.configs?.grid3_client ?? {};
11+
12+
export default async function deployMattermost(
13+
profile: IProfile,
14+
mattermost: Mattermost
15+
) {
16+
let [publicNodeId, nodeDomain] = await selectGatewayNode();
17+
const domain = `${mattermost.name}.${nodeDomain}`;
18+
19+
mattermost.domain = domain;
20+
21+
const matterMostVm = await _deployMatterMost(profile, mattermost);
22+
const ip = matterMostVm.planetary as string;
23+
24+
try {
25+
await _deployGateway(profile, mattermost, ip, publicNodeId);
26+
} catch (err) {
27+
await getGrid(profile, (grid) => {
28+
return grid.machines.delete({ name: mattermost.name });
29+
});
30+
console.log("Error", err);
31+
throw err;
32+
}
33+
34+
return matterMostVm;
35+
}
36+
37+
function _deployMatterMost(profile: IProfile, mattermost: Mattermost) {
38+
const { name, username, password, server, domain, port, nodeId } = mattermost;
39+
40+
const vm = new MachineModel();
41+
vm.name = name;
42+
vm.node_id = nodeId;
43+
vm.disks = [];
44+
vm.public_ip = false;
45+
vm.planetary = true;
46+
vm.cpu = 4;
47+
vm.memory = 8 * 1024;
48+
vm.rootfs_size = rootFs(4, 8 * 1024);
49+
vm.flist = "https://hub.grid.tf/ashraf.3bot/ashraffouda-mattermost-latest.flist"; // prettier-ignore
50+
vm.entrypoint = "/entrypoint.sh mattermost";
51+
vm.env = {
52+
DB_PASSWORD: password,
53+
SITE_URL: "https://" + domain,
54+
SMTPUsername: username,
55+
SMTPPassword: password,
56+
SMTPServer: server,
57+
SMTPPort: port,
58+
SSH_KEY: profile.sshKey,
59+
};
60+
61+
const vms = new MachinesModel();
62+
vms.name = name;
63+
vms.network = createNetwork(new Network());
64+
vms.machines = [vm];
65+
66+
return deploy(profile, "VM", name, (grid) => {
67+
return grid.machines
68+
.deploy(vms)
69+
.then(() => grid.machines.getObj(name))
70+
.then(([vm]) => vm);
71+
});
72+
}
73+
74+
function _deployGateway(
75+
profile: IProfile,
76+
{ name }: Mattermost,
77+
ip: string,
78+
nodeId: number
79+
) {
80+
const gw = new GatewayNameModel();
81+
gw.name = name;
82+
gw.node_id = nodeId;
83+
gw.tls_passthrough = false;
84+
gw.backends = [`http://[${ip}]:8000`];
85+
86+
return deploy(profile, "GatewayName", name, (grid) => {
87+
return grid.gateway
88+
.deploy_name(gw)
89+
.then(() => grid.gateway.getObj(name))
90+
.then(([gw]) => gw);
91+
});
92+
}

src/utils/gatewayHelpers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const { GridClient, Nodes, randomChoice } = window.configs?.grid3_client ?? {};
22

3-
export async function selectGatewayNode() {
3+
export async function selectGatewayNode(): Promise<[number, string]> {
44
const nodes = new Nodes(
55
GridClient.config.graphqlURL,
66
GridClient.config.rmbClient["proxyURL"]

0 commit comments

Comments
 (0)