Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions setup/setup-canary.sh
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,16 @@ select_network_interface
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# THIS MUST BE INSTALLED ON ALL NODES --> https://longhorn.io/docs/1.7.2/deploy/install/#installing-nfsv4-client
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# install nfs-common and open-iscsi
# install nfs-common, open-iscsi and jq
echo "Installing nfs-common..."
sudo apt-get update
sudo apt-get install open-iscsi nfs-common -y
sudo apt-get install open-iscsi nfs-common jq -y

echo "Fetching version information..."
K3S_VERSION=$(curl -s https://get.quickstack.dev/k3s-versions.json | jq -r '.canaryInstallVersion')
LONGHORN_VERSION=$(curl -s https://get.quickstack.dev/longhorn-versions.json | jq -r '.canaryInstallVersion')
echo "Using K3s version: $K3S_VERSION"
echo "Using Longhorn version: $LONGHORN_VERSION"

# Disable portmapper services --> https://github.com/biersoeckli/QuickStack/issues/18
sudo systemctl stop rpcbind.service rpcbind.socket
Expand All @@ -100,15 +106,15 @@ sudo systemctl disable rpcbind.service rpcbind.socket
#curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--node-ip=192.168.1.2 --advertise-address=192.168.1.2 --node-external-ip=188.245.236.232 --flannel-iface=enp7s0" INSTALL_K3S_VERSION="v1.31.3+k3s1" sh -

echo "Installing k3s with --flannel-iface=$selected_iface"
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--flannel-iface=$selected_iface" INSTALL_K3S_VERSION="v1.31.3+k3s1" sh -
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--flannel-iface=$selected_iface" INSTALL_K3S_VERSION="$K3S_VERSION" sh -
# Todo: Check for Ready node, takes ~30 seconds
sudo k3s kubectl get node

echo "Waiting for Kubernetes to start..."
wait_until_all_pods_running

# Installation of Longhorn
sudo kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.7.2/deploy/longhorn.yaml
sudo kubectl apply -f "https://raw.githubusercontent.com/longhorn/longhorn/${LONGHORN_VERSION}/deploy/longhorn.yaml"
echo "Waiting for Longhorn to start..."
wait_until_all_pods_running

Expand All @@ -119,8 +125,7 @@ wait_until_all_pods_running
sudo kubectl -n cert-manager get pod

# Checking installation of Longhorn
sudo apt-get install jq -y
sudo curl -sSfL https://raw.githubusercontent.com/longhorn/longhorn/v1.7.2/scripts/environment_check.sh | sudo bash
sudo curl -sSfL "https://raw.githubusercontent.com/longhorn/longhorn/${LONGHORN_VERSION}/scripts/environment_check.sh" | sudo bash

joinTokenForOtherNodes=$(sudo cat /var/lib/rancher/k3s/server/node-token)

Expand Down
10 changes: 7 additions & 3 deletions setup/setup-worker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,21 @@ select_network_interface
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# THIS MUST BE INSTALLED ON ALL NODES --> https://longhorn.io/docs/1.7.2/deploy/install/#installing-nfsv4-client
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# install nfs-common and open-iscsi
# install nfs-common, open-iscsi and jq
sudo apt-get update
sudo apt-get install open-iscsi nfs-common -y
sudo apt-get install open-iscsi nfs-common jq -y

echo "Fetching version information..."
K3S_VERSION=$(curl -s https://get.quickstack.dev/k3s-versions.json | jq -r '.prodInstallVersion')
echo "Using K3s version: $K3S_VERSION"

# Disable portmapper services --> https://github.com/biersoeckli/QuickStack/issues/18
sudo systemctl stop rpcbind.service rpcbind.socket
sudo systemctl disable rpcbind.service rpcbind.socket

# Installation of k3s
echo "Installing k3s with --flannel-iface=$selected_iface"
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--flannel-iface=$selected_iface" INSTALL_K3S_VERSION="v1.31.3+k3s1" K3S_URL=${K3S_URL} K3S_TOKEN=${JOIN_TOKEN} sh -
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--flannel-iface=$selected_iface" INSTALL_K3S_VERSION="$K3S_VERSION" K3S_URL=${K3S_URL} K3S_TOKEN=${JOIN_TOKEN} sh -

echo ""
echo "-----------------------------------------------------------------------------------------------------------"
Expand Down
16 changes: 10 additions & 6 deletions setup/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,15 @@ select_network_interface
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# THIS MUST BE INSTALLED ON ALL NODES --> https://longhorn.io/docs/1.7.2/deploy/install/#installing-nfsv4-client
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
echo "Installing nfs-common..."
echo "Installing nfs-common and jq..."
sudo apt-get update
sudo apt-get install open-iscsi nfs-common -y
sudo apt-get install open-iscsi nfs-common jq -y

echo "Fetching version information..."
K3S_VERSION=$(curl -s https://get.quickstack.dev/k3s-versions.json | jq -r '.prodInstallVersion')
LONGHORN_VERSION=$(curl -s https://get.quickstack.dev/longhorn-versions.json | jq -r '.prodInstallVersion')
echo "Using K3s version: $K3S_VERSION"
echo "Using Longhorn version: $LONGHORN_VERSION"

# Disable portmapper services --> https://github.com/biersoeckli/QuickStack/issues/18
sudo systemctl stop rpcbind.service rpcbind.socket
Expand All @@ -99,15 +105,15 @@ sudo systemctl disable rpcbind.service rpcbind.socket
#curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--node-ip=192.168.1.2 --advertise-address=192.168.1.2 --node-external-ip=188.245.236.232 --flannel-iface=enp7s0" INSTALL_K3S_VERSION="v1.31.3+k3s1" sh -

echo "Installing k3s with --flannel-iface=$selected_iface"
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--flannel-iface=$selected_iface" INSTALL_K3S_VERSION="v1.31.3+k3s1" sh -
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--flannel-iface=$selected_iface" INSTALL_K3S_VERSION="$K3S_VERSION" sh -
# Todo: Check for Ready node, takes ~30 seconds
sudo k3s kubectl get node

echo "Waiting for Kubernetes to start..."
wait_until_all_pods_running

# Installation of Longhorn
sudo kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.7.2/deploy/longhorn.yaml
sudo kubectl apply -f "https://raw.githubusercontent.com/longhorn/longhorn/${LONGHORN_VERSION}/deploy/longhorn.yaml"
echo "Waiting for Longhorn to start..."
wait_until_all_pods_running

Expand All @@ -117,8 +123,6 @@ echo "Waiting for Cert-Manager to start..."
wait_until_all_pods_running
sudo kubectl -n cert-manager get pod

sudo apt-get install jq -y

# Use this for checking installation of Longhorn
# sudo curl -sSfL https://raw.githubusercontent.com/longhorn/longhorn/v1.7.2/scripts/environment_check.sh | sudo bash

Expand Down
37 changes: 37 additions & 0 deletions src/__tests__/server/k3s-version.utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { K3sVersionUtils } from '@/server/utils/k3s-version.utils';

describe('K3sVersionUtils', () => {
describe('getMinorVersion', () => {
it('should extract minor version from full K3s version', () => {
expect(K3sVersionUtils.getMinorVersion('v1.31.3+k3s1')).toBe('v1.31');
expect(K3sVersionUtils.getMinorVersion('v1.30.5+k3s2')).toBe('v1.30');
expect(K3sVersionUtils.getMinorVersion('v1.32.0+k3s1')).toBe('v1.32');
});

it('should handle versions without K3s suffix', () => {
expect(K3sVersionUtils.getMinorVersion('v1.31.3')).toBe('v1.31');
expect(K3sVersionUtils.getMinorVersion('v1.30.5')).toBe('v1.30');
});

it('should handle versions without v prefix', () => {
expect(K3sVersionUtils.getMinorVersion('1.31.3+k3s1')).toBe('v1.31');
expect(K3sVersionUtils.getMinorVersion('1.30.5')).toBe('v1.30');
});

it('should handle versions with only major.minor', () => {
expect(K3sVersionUtils.getMinorVersion('v1.31')).toBe('v1.31');
expect(K3sVersionUtils.getMinorVersion('1.30')).toBe('v1.30');
});

it('should throw error for invalid version formats', () => {
expect(() => K3sVersionUtils.getMinorVersion('')).toThrow('Version string is required');
expect(() => K3sVersionUtils.getMinorVersion('v1')).toThrow('Invalid version format');
expect(() => K3sVersionUtils.getMinorVersion('invalid')).toThrow('Invalid version format');
expect(() => K3sVersionUtils.getMinorVersion('v1.x.3')).toThrow('Invalid version format');
});

it('should handle edge cases with multiple dots', () => {
expect(K3sVersionUtils.getMinorVersion('v1.31.3.4+k3s1')).toBe('v1.31');
});
});
});
6 changes: 3 additions & 3 deletions src/__tests__/shared/utils/domain-dns-provider.utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ describe('DomainDnsProviderUtils', () => {

describe('getHostnameForIpAddress', () => {
it('should convert IP address to hostname with hex format', () => {
expect(HostnameDnsProviderUtils.getHexHostanmeForIpAddress('192.168.1.1')).toBe('c0a80101.quickstack.me');
expect(HostnameDnsProviderUtils.getHexHostnameForIpAddress('192.168.1.1')).toBe('c0a80101.quickstack.me');
});

it('should handle another IP address format', () => {
expect(HostnameDnsProviderUtils.getHexHostanmeForIpAddress('10.0.0.1')).toBe('0a000001.quickstack.me');
expect(HostnameDnsProviderUtils.getHexHostnameForIpAddress('10.0.0.1')).toBe('0a000001.quickstack.me');
});

it('should handle localhost IP', () => {
expect(HostnameDnsProviderUtils.getHexHostanmeForIpAddress('127.0.0.1')).toBe('7f000001.quickstack.me');
expect(HostnameDnsProviderUtils.getHexHostnameForIpAddress('127.0.0.1')).toBe('7f000001.quickstack.me');
});
});

Expand Down
8 changes: 1 addition & 7 deletions src/app/backups/actions.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
'use server'

import monitoringService from "@/server/services/monitoring.service";
import clusterService from "@/server/services/node.service";
import pvcService from "@/server/services/pvc.service";
import backupService from "@/server/services/standalone-services/backup.service";
import { getAuthUserSession, isAuthorizedForBackups, simpleAction } from "@/server/utils/action-wrapper.utils";
import { AppMonitoringUsageModel } from "@/shared/model/app-monitoring-usage.model";
import { AppVolumeMonitoringUsageModel } from "@/shared/model/app-volume-monitoring-usage.model";
import { NodeResourceModel } from "@/shared/model/node-resource.model";
import { isAuthorizedForBackups, simpleAction } from "@/server/utils/action-wrapper.utils";
import { ServerActionResult, SuccessActionResult } from "@/shared/model/server-action-error-return.model";
import { z } from "zod";

Expand Down
2 changes: 1 addition & 1 deletion src/app/monitoring/actions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use server'

import monitoringService from "@/server/services/monitoring.service";
import clusterService from "@/server/services/node.service";
import clusterService from "@/server/services/cluster.service";
import { getAuthUserSession, simpleAction } from "@/server/utils/action-wrapper.utils";
import { UserGroupUtils } from "@/shared/utils/role.utils";
import { AppMonitoringUsageModel } from "@/shared/model/app-monitoring-usage.model";
Expand Down
2 changes: 1 addition & 1 deletion src/app/monitoring/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { getAuthUserSession } from "@/server/utils/action-wrapper.utils";
import PageTitle from "@/components/custom/page-title";
import clusterService from "@/server/services/node.service";
import clusterService from "@/server/services/cluster.service";
import ResourceNodes from "./monitoring-nodes";
import { NodeResourceModel } from "@/shared/model/node-resource.model";
import { AppVolumeMonitoringUsageModel } from "@/shared/model/app-volume-monitoring-usage.model";
Expand Down
2 changes: 1 addition & 1 deletion src/app/project/app/[appId]/domains/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,5 @@ export const getQuickstackDomainSuffix = async () => simpleAction(async () => {
if (!publicIpv4) {
throw new ServiceException('Please set the main public IPv4 address in the QuickStack settings first.');
}
return HostnameDnsProviderUtils.getHexHostanmeForIpAddress(publicIpv4);
return HostnameDnsProviderUtils.getHexHostnameForIpAddress(publicIpv4);
});
2 changes: 1 addition & 1 deletion src/app/project/app/[appId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import AppBreadcrumbs from "./app-breadcrumbs";
import s3TargetService from "@/server/services/s3-target.service";
import volumeBackupService from "@/server/services/volume-backup.service";
import { UserGroupUtils } from "@/shared/utils/role.utils";
import clusterService from "@/server/services/node.service";
import clusterService from "@/server/services/cluster.service";

export default async function AppPage({
searchParams,
Expand Down
63 changes: 60 additions & 3 deletions src/app/settings/server/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ import fs from "fs";
import { z } from "zod";
import { revalidateTag } from "next/cache";
import { Tags } from "@/server/utils/cache-tag-generator.utils";
import clusterService from "@/server/services/node.service";
import clusterService from "@/server/services/cluster.service";
import { TraefikIpPropagationStatus } from "@/shared/model/traefik-ip-propagation.model";
import k3sUpdateService from "@/server/services/upgrade-services/k3s-update.service";
import longhornUpdateService from "@/server/services/upgrade-services/longhorn-update.service";
import longhornUiService from "@/server/services/longhorn-ui.service";

export const setNodeStatus = async (nodeName: string, schedulable: boolean) =>
simpleAction(async () => {
Expand Down Expand Up @@ -287,8 +290,62 @@ export const downloadSystemBackup = async (backupKey: string) =>
export const setTraefikIpPropagation = async (prevState: any, inputData: { enableIpPreservation: boolean }) =>
saveFormAction(inputData, z.object({ enableIpPreservation: z.boolean() }), async (validatedData) => {
await getAdminUserSession();

await traefikService.applyExternalTrafficPolicy(validatedData.enableIpPreservation);

return new SuccessActionResult(undefined, `Traefik externalTrafficPolicy set to ${validatedData.enableIpPreservation ? 'Local' : 'Cluster'}.`);
});

export const checkK3sUpgradeControllerStatus = async () =>
simpleAction(async () => {
await getAdminUserSession();
return await k3sUpdateService.isSystemUpgradeControllerPresent();
});

export const installK3sUpgradeController = async () =>
simpleAction(async () => {
await getAdminUserSession();
await k3sUpdateService.getCurrentK3sMinorVersion(); // if this succeds alls nodes have the same version and cluster is ready for upgrades
await k3sUpdateService.installSystemUpgradeController();
return new SuccessActionResult(undefined, 'K3s System Upgrade Controller has been installed successfully.');
});

export const startK3sUpgrade = async () =>
simpleAction(async () => {
await getAdminUserSession();
await k3sUpdateService.createUpgradePlans();
return new SuccessActionResult(undefined, 'The upgrade process has started.');
});

export const startLonghornUpgrade = async () =>
simpleAction(async () => {
await getAdminUserSession();
await longhornUpdateService.upgrade();
return new SuccessActionResult(undefined, 'Longhorn upgrade has been initiated. Volume engines will be upgraded automatically.');
});

export const getLonghornUiIngressStatus = async () =>
simpleAction(async () => {
await getAdminUserSession();
const active = await longhornUiService.isIngressActive();
return new SuccessActionResult(active);
}) as Promise<ServerActionResult<unknown, boolean>>;

export const enableLonghornUiIngress = async () =>
simpleAction(async () => {
await getAdminUserSession();
const credentials = await longhornUiService.enable();
return new SuccessActionResult(credentials, 'Longhorn UI is now accessible.');
}) as Promise<ServerActionResult<unknown, { url: string; username: string; password: string }>>;

export const getLonghornUiCredentials = async () =>
simpleAction(async () => {
await getAdminUserSession();
const credentials = await longhornUiService.getCredentials();
return new SuccessActionResult(credentials);
}) as Promise<ServerActionResult<unknown, { url: string; username: string; password: string } | undefined>>;

export const disableLonghornUiIngress = async () =>
simpleAction(async () => {
await getAdminUserSession();
await longhornUiService.disable();
return new SuccessActionResult(undefined, 'Longhorn UI access has been disabled.');
});
Loading