Compare commits

...

28 Commits

Author SHA1 Message Date
hys
a822538fc3 feat: add deviceName to helm values 2025-12-22 17:36:31 +08:00
hys
c5610cbcb7 fix: pending canceled namespace delete 2025-12-19 20:54:16 +08:00
hysyeah
47e82908c4 app-service: app entrance update (#2278)
* fix: app entrance update

* fix: set appservice tag
2025-12-18 23:22:41 +08:00
lovehunter9
4b79a7aefe fix: sync add support video of 3gp, mpg and vob (#2276) 2025-12-18 23:22:02 +08:00
hysyeah
dfd74239dd kubeblock-addons: fix image repo transfer due to opa (#2275) 2025-12-18 23:21:08 +08:00
berg
5a08f918c6 system frontend, market backend: fix market page suspend and upgrade status bug (#2274)
feat: update system frontend and market backend verison
2025-12-18 21:16:38 +08:00
hysyeah
8fde456f74 app-service: fix check resource before resume operation and image offset cal (#2273)
* fix: check resource before resume operation

* fix: image service tag to 0.4.65
2025-12-18 21:16:07 +08:00
eball
b173f005cd ci: bump version to 1.12.4 in workflows and scripts (#2271)
chore: bump version to 1.12.4 in workflows and scripts
2025-12-18 19:14:46 +08:00
eball
ecf8849b55 daemon: handle restarting state for system errors (#2270) 2025-12-18 17:07:57 +08:00
aby913
c8f416c4c0 backup: replace 85% disk usage percentage threshold (#2233) 2025-12-18 16:32:11 +08:00
dkeven
ed183b8e4e fix(ci): specify working dir in github action for backup module (#2269) 2025-12-18 15:21:16 +08:00
hysyeah
20595b72c7 app-serivce: delete applyenv created ns (#2268)
fix: update appservice image tag
2025-12-18 15:20:37 +08:00
dkeven
04c9e8309b feat(cli): add upgrader for stable version 1.12.3 (#2267) 2025-12-18 15:05:31 +08:00
lovehunter9
3cd388d83a feat: files sync search (#2262)
* feat: files sync search

* feat(olares-app): update sync search

* fix(login):  nginx config error

---------

Co-authored-by: qq815776412 <815776412@qq.com>
2025-12-18 14:56:29 +08:00
hysyeah
8266fc6085 app-service: fix concurrency cause two app in downloading state (#2265)
* fix: helm failed release

* fix: update app-service,image-service image tag

* fix: compatible with legacy mongodb uninstall

* fix: concurrency cause two app in downloading state

* fix: for an app with env will create namespace at first
2025-12-17 23:59:29 +08:00
dkeven
78fb8bcdca chore(gpu): upgrade NVIDIA driver version to 590.44.01 (#2264) 2025-12-17 23:59:05 +08:00
dkeven
cdb7afafef fix(cli): only (un)label current node in multi-node cluster (#2260) 2025-12-17 23:58:07 +08:00
salt
c4e1c74538 fix: document rest api error (#2259)
* fix: fix document recreate error, x-bfl-headeer replace error

* feat: upgrade to v0.0.95

---------

Co-authored-by: ubuntu <you@example.com>
2025-12-17 23:57:19 +08:00
Yajing
07b9470e4e docs: add access olares locally doc (#2224) 2025-12-17 22:00:33 +08:00
yajing wang
da11265189 fix format and add learn more 2025-12-17 21:55:03 +08:00
Yajing
f6d1addc7d Apply suggestions 2025-12-17 21:38:46 +08:00
hys
3b644efa0a cli: argo workflow breaking change 2025-12-17 21:26:16 +08:00
yajing wang
27d8463775 address comments and improve wording 2025-12-17 21:12:47 +08:00
yajing wang
aa9b2aa243 refactor vpn-related docs 2025-12-15 22:59:52 +08:00
yajing wang
1c4257065f fix anchor links 2025-12-12 18:51:33 +08:00
yajing wang
40c0491925 compress image sizes 2025-12-12 18:46:50 +08:00
yajing wang
a7f2d9c583 add screenshots and zh-cn version 2025-12-12 18:15:32 +08:00
yajing wang
a72c760b07 docs: add access olares locally doc 2025-12-11 23:19:37 +08:00
81 changed files with 1434 additions and 313 deletions

View File

@@ -75,7 +75,7 @@ jobs:
steps:
- id: generate
run: |
v=1.12.3-$(echo $RANDOM$RANDOM)
v=1.12.4-$(echo $RANDOM$RANDOM)
echo "version=$v" >> "$GITHUB_OUTPUT"
upload-cli:

View File

@@ -26,6 +26,6 @@ jobs:
with:
go-version: '1.21.10'
- name: Run Build
working-directory: framework/backup-server
run: |
make all
working-directory: framework/backup-server
make build

View File

@@ -17,7 +17,7 @@ jobs:
steps:
- id: generate
run: |
v=1.12.3-$(date +"%Y%m%d")
v=1.12.4-$(date +"%Y%m%d")
echo "version=$v" >> "$GITHUB_OUTPUT"
release-id:

View File

@@ -53,6 +53,7 @@ rules:
- "/seahub/api/*"
- "/system/configuration/encoding"
- "/api/search/get_directory/"
- "/api/search/sync_search/"
verbs: ["*"]
---

View File

@@ -317,7 +317,7 @@ spec:
chown -R 1000:1000 /uploadstemp && \
chown -R 1000:1000 /appdata
- name: olares-app-init
image: beclab/system-frontend:v1.6.24
image: beclab/system-frontend:v1.6.27
imagePullPolicy: IfNotPresent
command:
- /bin/sh
@@ -439,7 +439,7 @@ spec:
- name: NATS_SUBJECT_VAULT
value: os.vault.{{ .Values.bfl.username}}
- name: user-service
image: beclab/user-service:v0.0.78
image: beclab/user-service:v0.0.81
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3000

View File

@@ -18,7 +18,7 @@ fi
if [[ x"$VERSION" == x"" ]]; then
if [[ "$LOCAL_RELEASE" == "1" ]]; then
ts=$(date +%Y%m%d%H%M%S)
export VERSION="1.12.3-$ts"
export VERSION="1.12.4-$ts"
echo "will build and use a local release of Olares with version: $VERSION"
echo ""
else
@@ -28,7 +28,7 @@ fi
if [[ "x${VERSION}" == "x" || "x${VERSION:3}" == "xVERSION__" ]]; then
echo "error: Olares version is unspecified, please set the VERSION env var and rerun this script."
echo "for example: VERSION=1.12.3-20241124 bash $0"
echo "for example: VERSION=1.12.4-20241124 bash $0"
exit 1
fi

View File

@@ -158,7 +158,7 @@ export VERSION="#__VERSION__"
if [[ "x${VERSION}" == "x" || "x${VERSION:3}" == "xVERSION__" ]]; then
echo "error: Olares version is unspecified, please set the VERSION env var and rerun this script."
echo "for example: VERSION=1.12.3-20241124 bash $0"
echo "for example: VERSION=1.12.4-20241124 bash $0"
exit 1
fi

View File

@@ -49,7 +49,7 @@ func NewCmdRelease() *cobra.Command {
}
if version == "" {
version = fmt.Sprintf("1.12.3-%s", time.Now().Format("20060102150405"))
version = fmt.Sprintf("1.12.4-%s", time.Now().Format("20060102150405"))
fmt.Printf("--version unspecified, using: %s\n", version)
time.Sleep(1 * time.Second)
}

View File

@@ -25,7 +25,7 @@ const (
DefaultK3sVersion = "v1.33.3-k3s"
DefaultKubernetesVersion = ""
DefaultKubeSphereVersion = "v3.3.0"
CurrentVerifiedCudaVersion = "13.0"
CurrentVerifiedCudaVersion = "13.1"
)
const (

View File

@@ -251,7 +251,7 @@ func (l *NodeLabelingModule) Init() {
Name: "UpdateNode",
Prepare: &prepare.PrepareCollection{
new(CudaInstalled),
new(K8sNodeInstalled),
new(CurrentNodeInK8s),
},
Action: new(UpdateNodeLabels),
Retry: 1,
@@ -262,7 +262,7 @@ func (l *NodeLabelingModule) Init() {
Prepare: &prepare.PrepareCollection{
new(common.OnlyFirstMaster),
new(CudaInstalled),
new(K8sNodeInstalled),
new(CurrentNodeInK8s),
},
Action: new(RestartPlugin),
Retry: 1,
@@ -286,7 +286,7 @@ func (l *NodeUnlabelingModule) Init() {
Hosts: l.Runtime.GetHostsByRole(common.Master),
Prepare: &prepare.PrepareCollection{
new(common.OnlyFirstMaster),
new(K8sNodeInstalled),
new(CurrentNodeInK8s),
},
Action: new(RemoveNodeLabels),
Parallel: false,
@@ -298,7 +298,7 @@ func (l *NodeUnlabelingModule) Init() {
Hosts: l.Runtime.GetHostsByRole(common.Master),
Prepare: &prepare.PrepareCollection{
new(common.OnlyFirstMaster),
new(K8sNodeInstalled),
new(CurrentNodeInK8s),
new(GpuDevicePluginInstalled),
},
Action: new(RestartPlugin),

View File

@@ -63,11 +63,11 @@ func (p *CudaNotInstalled) PreCheck(runtime connector.Runtime) (bool, error) {
return false, nil
}
type K8sNodeInstalled struct {
type CurrentNodeInK8s struct {
common.KubePrepare
}
func (p *K8sNodeInstalled) PreCheck(runtime connector.Runtime) (bool, error) {
func (p *CurrentNodeInK8s) PreCheck(runtime connector.Runtime) (bool, error) {
client, err := clientset.NewKubeClient()
if err != nil {
logger.Debug(errors.Wrap(errors.WithStack(err), "kubeclient create error"))
@@ -84,11 +84,13 @@ func (p *K8sNodeInstalled) PreCheck(runtime connector.Runtime) (bool, error) {
return false, nil
}
if len(node.Items) == 0 {
return false, nil
for _, node := range node.Items {
if node.Name == runtime.GetSystemInfo().GetHostname() {
return true, nil
}
}
return true, nil
return false, nil
}
type NvidiaGraphicsCard struct {

92
cli/pkg/upgrade/1_12_3.go Normal file
View File

@@ -0,0 +1,92 @@
package upgrade
import (
"time"
"github.com/Masterminds/semver/v3"
"github.com/beclab/Olares/cli/pkg/core/task"
"github.com/beclab/Olares/cli/version"
)
var version_1_12_3 = semver.MustParse("1.12.3")
type upgrader_1_12_3 struct {
breakingUpgraderBase
}
func (u upgrader_1_12_3) Version() *semver.Version {
cliVersion, err := semver.NewVersion(version.VERSION)
// tolerate local dev version
if err != nil {
return version_1_12_3
}
if samePatchLevelVersion(version_1_12_3, cliVersion) && getReleaseLineOfVersion(cliVersion) == mainLine {
return cliVersion
}
return version_1_12_3
}
func (u upgrader_1_12_3) AddedBreakingChange() bool {
if u.Version().Equal(version_1_12_3) {
return true
}
return false
}
func (u upgrader_1_12_3) NeedRestart() bool {
return true
}
func (u upgrader_1_12_3) PrepareForUpgrade() []task.Interface {
tasks := make([]task.Interface, 0)
tasks = append(tasks, upgradeKsConfig()...)
tasks = append(tasks, upgradePrometheusServiceMonitorKubelet()...)
tasks = append(tasks, upgradeKSCore()...)
tasks = append(tasks, upgradeNodeExporter()...)
tasks = append(tasks,
&task.LocalTask{
Name: "DeleteArgoProjV1alpha1CRDs",
Action: new(deleteArgoProjV1alpha1CRDs),
Retry: 3,
Delay: 5 * time.Second,
},
)
tasks = append(tasks, regenerateKubeFiles()...)
tasks = append(tasks, u.upgraderBase.PrepareForUpgrade()...)
return tasks
}
func (u upgrader_1_12_3) UpgradeSystemComponents() []task.Interface {
pre := []task.Interface{
&task.LocalTask{
Name: "UpgradeL4BFLProxy",
Action: &upgradeL4BFLProxy{Tag: "v0.3.9"},
Retry: 3,
Delay: 5 * time.Second,
},
}
return append(pre, u.upgraderBase.UpgradeSystemComponents()...)
}
func (u upgrader_1_12_3) UpdateOlaresVersion() []task.Interface {
var tasks []task.Interface
tasks = append(tasks,
&task.LocalTask{
Name: "UpgradeGPUDriver",
Action: new(upgradeGPUDriverIfNeeded),
},
)
tasks = append(tasks, u.upgraderBase.UpdateOlaresVersion()...)
tasks = append(tasks,
&task.LocalTask{
Name: "RebootIfNeeded",
Action: new(rebootIfNeeded),
},
)
return tasks
}
func init() {
registerMainUpgrader(upgrader_1_12_3{})
}

View File

@@ -0,0 +1,102 @@
package upgrade
import (
"context"
"fmt"
"time"
"github.com/beclab/Olares/cli/pkg/common"
"github.com/beclab/Olares/cli/pkg/core/connector"
"github.com/beclab/Olares/cli/pkg/core/task"
"github.com/Masterminds/semver/v3"
apixclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
)
type upgrader_1_12_3_20251217 struct {
breakingUpgraderBase
}
func (u upgrader_1_12_3_20251217) Version() *semver.Version {
return semver.MustParse("1.12.3-20251217")
}
func (u upgrader_1_12_3_20251217) PrepareForUpgrade() []task.Interface {
tasks := make([]task.Interface, 0)
tasks = append(tasks,
&task.LocalTask{
Name: "DeleteArgoProjV1alpha1CRDs",
Action: new(deleteArgoProjV1alpha1CRDs),
Retry: 3,
Delay: 5 * time.Second,
},
)
tasks = append(tasks, u.upgraderBase.PrepareForUpgrade()...)
return tasks
}
func (u upgrader_1_12_3_20251217) NeedRestart() bool {
return true
}
func (u upgrader_1_12_3_20251217) UpdateOlaresVersion() []task.Interface {
var tasks []task.Interface
tasks = append(tasks,
&task.LocalTask{
Name: "UpgradeGPUDriver",
Action: new(upgradeGPUDriverIfNeeded),
},
)
tasks = append(tasks, u.upgraderBase.UpdateOlaresVersion()...)
tasks = append(tasks,
&task.LocalTask{
Name: "RebootIfNeeded",
Action: new(rebootIfNeeded),
},
)
return tasks
}
func init() {
registerDailyUpgrader(upgrader_1_12_3_20251217{})
}
type deleteArgoProjV1alpha1CRDs struct {
common.KubeAction
}
func (a *deleteArgoProjV1alpha1CRDs) Execute(runtime connector.Runtime) error {
config, err := ctrl.GetConfig()
if err != nil {
return fmt.Errorf("failed to get rest config: %s", err)
}
client, err := apixclientset.NewForConfig(config)
if err != nil {
return fmt.Errorf("failed to create crd client: %v", err)
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
crds, err := client.ApiextensionsV1().CustomResourceDefinitions().List(ctx, metav1.ListOptions{})
if err != nil {
return fmt.Errorf("failed to list CRDs: %v", err)
}
for _, crd := range crds.Items {
if crd.Spec.Group != "argoproj.io" {
continue
}
if crd.Annotations["meta.helm.sh/release-name"] != "knowledge" {
continue
}
if err := client.ApiextensionsV1().CustomResourceDefinitions().Delete(ctx, crd.Name, metav1.DeleteOptions{}); err != nil && !apierrors.IsNotFound(err) {
return fmt.Errorf("failed to delete CRD %s: %v", crd.Name, err)
}
}
return nil
}

View File

@@ -114,6 +114,17 @@ func CheckCurrentStatus(ctx context.Context) error {
var currentTerminusState TerminusState = CurrentState.TerminusState
defer func() {
if currentTerminusState == SystemError {
restarting, err := utils.SystemStartLessThan(10 * time.Minute) // uptime less then 10 minutes
if err != nil {
klog.Error(err)
}
if restarting {
currentTerminusState = Restarting
}
}
CurrentState.TerminusState = currentTerminusState
TerminusStateMu.Unlock()
klog.Info("current state: ", CurrentState.TerminusState)
@@ -447,16 +458,6 @@ func CheckCurrentStatus(ctx context.Context) error {
return nil
}
restarting, err := utils.SystemStartLessThan(10 * time.Minute) // uptime less then 10 minutes
if err != nil {
return err
}
if restarting {
currentTerminusState = Restarting
return nil
}
currentTerminusState = SystemError
}

View File

@@ -12,6 +12,9 @@ import (
"github.com/beclab/Olares/daemon/pkg/cluster/state"
"github.com/beclab/Olares/daemon/pkg/containerd"
"github.com/dustin/go-humanize"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
v1 "k8s.io/cri-api/pkg/apis/runtime/v1"
"k8s.io/utils/strings/slices"
@@ -192,6 +195,55 @@ func (i *preCheck) Execute(ctx context.Context, p any) (res any, err error) {
return nil, fmt.Errorf("waiting for user to finish activation: %s", strings.Join(activatingUsers, ", "))
}
// the new MongoDB version has a different implementation from the old version.
// if an old MongoDB instance exists, it must be uninstalled before upgrading.
{
gvr := schema.GroupVersionResource{Group: "app.bytetrade.io", Version: "v1alpha1", Resource: "applicationmanagers"}
am, err := dynamicClient.Resource(gvr).Get(ctx, "os-platform-mongodb", metav1.GetOptions{})
if err != nil {
if !apierrors.IsNotFound(err) {
return nil, fmt.Errorf("failed to check mongodb application manager: %v", err)
}
} else if am != nil {
state, _, _ := unstructured.NestedString(am.Object, "status", "state")
switch strings.ToLower(state) {
case "installing", "running":
return nil, fmt.Errorf("mongodb is %s, please remove it before upgrade. if mongodb is installing, you can cancel it in market. if running, execute 'kubectl delete appmgr os-platform-mongodb' in control-hub -> olares shell", state)
}
}
}
// in v1.12.3, argo has been moved to os-platform.
// to avoid CRD resource conflicts, if wise is installed and includes argo crd, you must uninstall wise first, including sharedserver.
{
isKnowledgeSharedNsExist := false
if _, err := client.CoreV1().Namespaces().Get(ctx, "knowledge-shared", metav1.GetOptions{}); err == nil {
isKnowledgeSharedNsExist = true
} else if !apierrors.IsNotFound(err) {
return nil, fmt.Errorf("failed to check namespace 'knowledge-shared': %v", err)
}
xclient, err := utils.GetApixClient()
if err != nil {
err = fmt.Errorf("failed to get apix client: %v", err)
klog.Error(err.Error())
return nil, err
}
isKnowledgeArgoCRDExist := false
crds, err := xclient.ApiextensionsV1().CustomResourceDefinitions().List(ctx, metav1.ListOptions{})
if err != nil {
klog.Errorf("failed to list crds %v", err)
return nil, err
}
for _, crd := range crds.Items {
if crd.Spec.Group == "argoproj.io" && crd.Annotations["meta.helm.sh/release-name"] == "knowledge" {
isKnowledgeArgoCRDExist = true
break
}
}
if isKnowledgeSharedNsExist && isKnowledgeArgoCRDExist {
return nil, fmt.Errorf("namespace 'knowledge-shared' exists (wise); please uninstall Wise and Shared Server before upgrade")
}
}
klog.Info("pre checks passed for upgrade")
return newExecutionRes(true, nil), nil

View File

@@ -14,6 +14,7 @@ import (
"github.com/beclab/Olares/daemon/pkg/nets"
"github.com/joho/godotenv"
corev1 "k8s.io/api/core/v1"
apixclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
k8sruntime "k8s.io/apimachinery/pkg/runtime"
@@ -582,3 +583,19 @@ func GetApplicationUrlAll(ctx context.Context) ([]string, error) {
return urls, nil
}
func GetApixClient() (apixclientset.Interface, error) {
config, err := ctrl.GetConfig()
if err != nil {
klog.Error("get k8s config error, ", err)
return nil, err
}
client, err := apixclientset.NewForConfig(config)
if err != nil {
klog.Error("get k8s apix client error, ", err)
return nil, err
}
return client, nil
}

View File

@@ -108,6 +108,10 @@ const side = {
text: "Back up mnemonics",
link: "/manual/larepass/back-up-mnemonics"
},
{
text: "Access Olares locally",
link: "/manual/get-started/local-access",
},
{
text: "What's next",
link: "/manual/get-started/next-steps",
@@ -128,7 +132,7 @@ const side = {
{text: "Manage integrations", link:"/manual/larepass/integrations"},
],
},
{text: "Manage VPN", link:"/manual/larepass/private-network"},
{text: "Use VPN", link:"/manual/larepass/private-network"},
{
text: "Manage device",
collapsed: true,

View File

@@ -109,6 +109,10 @@ const side = {
text: "备份助记词",
link: "/zh/manual/larepass/back-up-mnemonics",
},
{
text: "内网访问 Olares",
link: "zh/manual/get-started/local-access",
},
{
text: "探索",
link: "/zh/manual/get-started/next-steps",
@@ -129,7 +133,7 @@ const side = {
{text: "管理集成", link:"/zh/manual/larepass/integrations"},
],
},
{text: "管理专用网络", link:"/zh/manual/larepass/private-network"},
{text: "使用专用网络", link:"/zh/manual/larepass/private-network"},
{
text: "管理设备",
collapsed: true,

View File

@@ -1,4 +1,5 @@
---
outline: [2, 3]
description: Olares network architecture principles, covering application entrance types, local access mechanisms, endpoint configurations and internal network security policies.
---
# Network
@@ -9,29 +10,21 @@ Olares provides users with a barrier-free but secure and versatile network solut
Each Olares application can have one or more entrances that serve as access points. There are three types of entrances:
- **Public entrance**
### Public entrance
- Provides external services such as blogs, social media, etc.
- Accessible without authentication
- Basic security through Cloudflare
- Provides external services such as blogs, social media, etc.
- Accessible without authentication
- Basic security through Cloudflare
- **Private entrance**
### Private entrance
- Provides services exclusively for individual users, families, or teams
- Suitable for readers, entertainment, productivity tools, desktop applications, etc.
- Requires [authentication](account.md#multi-factor-authentication-mfa) for access
- Provides services exclusively for individual users, families, or teams
- Suitable for readers, entertainment, productivity tools, desktop applications, etc.
- Requires [authentication](account.md#multi-factor-authentication-mfa) for access
- **Internal entrance**
- Functions similarly to private entrance
- No authentication required when accessing applications through LarePass VPN
## Acccess to private entrances via VPN
Simply enable [LarePass VPN](/manual/larepass/private-network.md) on your device to securely and quickly access your private applications via their dedicated URLs (e.g., `https://vault.alice123.olares.com`).
::: tip Note
If LarePass VPN is not enabled, requests to your private entrances will be routed through your reverse proxy tunnel to Olares, which may cause network latency and incur charges.
:::
### Internal entrance
- Functions similarly to private entrance
- No authentication required when accessing applications through LarePass VPN
## Endpoints

View File

@@ -0,0 +1,235 @@
---
outline: [2,3]
description: Learn how to access Olares apps and services directly via your local network (LAN) for maximum speed, privacy, and offline reliability.
---
# Access Olares services locally
Typically, you access Olares services through a browser using a URL like `https://desktop.<username>.olares.com`. This way, you can reach your services from any device at any time.
However, accessing your devices directly over your Local Area Network (LAN) provides several advantages:
- **Maximum performance**: Transfer files at full speed without the latency and potential bottlenecks of the internet.
- **Enhanced privacy**: Keep your traffic contained within your home network for added security.
- **Offline independence**: Access your data and apps even when your internet service is unavailable.
This guide covers several methods to establish a local connection:
- [Enable LarePass VPN (Recommended)](#method-1-enable-larepass-vpn)<br/>This method is the easiest solution, as it automatically establishes the fastest connection without manual configuration.
- [Use `.local` domain](#method-2-use-local-domain)<br/>This method requires no installation, though you must use specific URL formats based on your operating system.
- [Configure local DNS (Advanced)](#method-3-configure-local-dns)<br/>This method allows standard URLs to work locally by updating DNS settings on your router or individual computer.
- [Modify host files (Fallback)](#method-4-modify-host-files)<br/>This method manually maps standard URLs to your local IP on a single computer, ensuring access even without an internet connection.
## Method 1: Enable LarePass VPN
The most robust way to connect, whether you are sitting next to the device or traveling, is using the LarePass VPN. It intelligently detects when you are on the same network and switches to a direct **Intranet** mode for maximum speed.
:::tip Always enable VPN for remote access
Keep LarePass VPN enabled. It automatically prioritizes the fastest available route to ensure you always get the best speed possible without manual switching.
:::
:::info iOS and macOS setup
On iOS or macOS, you may be prompted to add a VPN Configuration to your system settings the first time you enable the feature. Allow this to complete the setup.
:::
Enable the LarePass VPN directly on the device you are currently using to access Olares.
<tabs>
<template #On-LarePass-mobile-client>
1. Open the LarePass app, and go to **Settings**.
2. In the **My Olares** card, toggle on the VPN switch.
![Enable LarePass VPN on mobile](/images/manual/get-started/larepass-vpn-mobile.png#bordered)
</template>
<template #On-LarePass-desktop-client>
1. Open the LarePass app, and click your avatar in the top-left corner to open the user menu.
2. Toggle on the switch for **VPN connection**.
![Enable LarePass VPN on desktop](/images/manual/get-started/larepass-vpn-desktop.png#bordered)
</template>
</tabs>
Once enabled, check the status indicator in LarePass to verify the connection type:
| Status | Description |
|:-------------|:---------------------------------------------------------|
| **Intranet** | Direct connection via your local LAN IP. Fastest speeds. |
| **P2P** | Direct encrypted tunnel between devices. High speed. |
| **DERP** | Routed via a secure relay server. Used as a fallback. |
## Method 2: Use `.local` domain
If you prefer not to use a VPN, you can access services using the `.local` domain. There are two domain formats available depending on your compatibility needs.
### Single-level domain (All operating systems)
:::warning Supported for community apps only
Olares system apps such as Desktop and Files do not support this URL format and will not load correctly.
:::
This format uses a single-level domain by connecting the entrance ID and the username with hyphens (`-`).
- **Default URL**:
```plain
https://<entrance_id>.<username>.olares.com
```
- **Local-access URL**:
```plain
http://<entrance_id>-<username>-olares.local
```
### Multi-level domain (macOS and iOS only)
Apple devices support local service discovery via [Bonjour](https://developer.apple.com/bonjour/) (zeroconfiguration networking), which can resolve multilabel domains under `.local` on macOS and iOS. This allows a local URL format that mirrors the remote address.
- **Default URL**:
```plain
https://<entrance_id>.<username>.olares.com
```
- **Local-access URL**:
```plain
http://<entrance_id>.<username>.olares.local
```
![Multi-level local domain](/images/manual/get-started/multilevel-local-domain-mac.png#bordered)
## Method 3: Configure local DNS
For a seamless experience where standard URLs resolve to your local IP address automatically, you can configure your network DNS. This configuration ensures consistent access across all devices on the network without requiring individual client setup.
### Find the internal IP for Olares device
To configure DNS, first you need to find the internal IP for your Olares device.
<tabs>
<template #Check-via-the-LarePass-mobile-client>
If your phone and Olares device are on the same network:
1. Open the LarePass app, and go to **Settings** > **System** to navigate to the **Olares management** page
![Tap the System card](/images/manual/get-started/larepass-system.png#bordered)
2. Tap on the device card.
![Tap the device card](/images/manual/get-started/larepass-device-card.png#bordered)
3. Scroll down to the **Network** section. You can find the **Intranet IP** there.
![Find Network section](/images/manual/get-started/larepass-network.png#bordered)
</template>
<template #Check-via-Olares-Terminal>
Control Hub provides a built-in terminal that allows you to run system commands directly from the browser, without needing an external SSH client.
1. Open the Control Hub app, and under **Terminal**, select **Olares**in the left navigation bar.
![Find internal IP from Control Hub](/images/manual/get-started/find-internal-ip-from-controlhub.png#bordered)
2. Type `ifconfig` in the terminal and press **Enter**.
3. Look for your active connection, typically named `enp3s0` (wired) or `wlo1` (wireless). The IP address follows `inet`.
Example output:
```bash
enp3s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.50.116 netmask 255.255.255.0 broadcast 192.168.50.255
inet6 fe80::4194:4045:c35e:7b32 prefixlen 64 scopeid 0x20<link>
ether d8:43:ae:54:ce:fc txqueuelen 1000 (Ethernet)
RX packets 80655321 bytes 71481515308 (71.4 GB)
RX errors 0 dropped 136 overruns 0 frame 0
TX packets 51867817 bytes 15924740708 (15.9 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
```
In this example, `192.168.50.116` is the internal IP.
</template>
</tabs>
### Configure DNS
With the internal IP address identified, you must now configure your DNS settings to route traffic correctly. You can apply this configuration to a single computer for individual access, or update your router to enable seamless local resolution for all devices on your network.
<tabs>
<template #Configure-for-local-device>
Update the DNS settings on your specific computer. For example, on macOS:
1. Open Apple menu and go to **System Settings**.
2. Select **Wi-Fi**, then click **Details** on your connected network.
3. Select **DNS** and update the server list:
a. Click the **+** button under **DNS Servers** to add your Olares device's internal IP (e.g., `192.168.x.x`).
b. Ensure the Olares IP is listed at the top. Add your original DNS (or `1.1.1.1`) below it as a fallback. <br/>This ensures that if your Olares device shuts down, the router will automatically switch to the secondary DNS, keeping your internet connection alive.
4. Click **OK** to save changes.
</template>
<template #Configure-for-all-devices>
Update the DNS on your router to apply changes to every device in your network.
1. Log in to your router's admin panel.
2. Navigate to **DHCP / DNS Settings**.
3. Set **Primary DNS** to your Olares device's internal IP (e.g., `192.168.x.x`).
4. Set **Secondary DNS** to your current Primary DNS (or a public provider like `1.1.1.1`). <br/>This ensures that if your Olares device shuts down, the router will automatically switch to the secondary DNS, keeping your internet connection alive.
5. Save and reconnect your devices to refresh the DNS cache.
</template>
</tabs>
Once configured, you can access Olares using both your standard public address and your local address.
:::tip
You can install AdGuard Home from the Olares Market to monitor traffic and manage DNS mappings graphically.
:::
## Method 4: Modify host files
If you cannot change router settings and need immediate offline access on a specific computer, you can manually map the domains in your hosts file.
1. Locate your hosts file:
- **Windows:** `C:\Windows\System32\drivers\etc\hosts`
- **macOS/Linux:** `/etc/hosts`
2. Open the file with a text editor, which requires Administrator privileges.
3. Add the mapping lines:
```plain
# Replace with the actual internal IP and the username
# Olares apps
192.168.31.208 desktop.<username>.olares.com
192.168.31.208 auth.<username>.olares.com
192.168.31.208 files.<username>.olares.com
192.168.31.208 market.<username>.olares.com
192.168.31.208 settings.<username>.olares.com
192.168.31.208 dashboard.<username>.olares.com
192.168.31.208 control-hub.<username>.olares.com
192.168.31.208 profile.<username>.olares.com
192.168.31.208 vault.<username>.olares.com
# Add other community apps as needed
192.168.31.208 <entrance_id>.<username>.olares.com
```
4. Save the file to apply changes and ensure local access without an internet connection.
Verify the changes by checking the URL for quick loading or using the terminal:
```bash
ping desktop.<username>.olares.com
```
If the IP address starts with `192.168`, it indicates successful configuration.
## Learn more
- [Access Olares services remotely via LarePass VPN](../../manual/larepass/private-network.md): Understand how to use LarePass VPN.
- [Network](../../developer/concepts/network.md): Learn about the different entry points in Olares.
## FAQs
### Why doesn't LarePass VPN work on my Mac anymore?
If you successfully enabled the VPN previously, but it has stopped working, you might need to reset the system extension.
:::info
Depending on your macOS version, the UI might look slightly different.
:::
1. Open **System Settings**, search for "Extension", and select **Login Items & Extensions**.
2. Scroll to the **Network Extensions** section and click the info icon (ⓘ) to view loaded extensions.
3. Find LarePass, click the three dots (...), and select **Delete Extension**.
4. Confirm the uninstallation.
5. Restart your Mac and re-enable the VPN in the LarePass desktop client.
### Why I cannot enable LarePass VPN on Windows?
Third-party antivirus software might mistakenly flag the LarePass desktop client as suspicious, preventing it from launching the VPN service.
If prompted by your antivirus when opening LarePass for the first time, allow the application to continue.
If the VPN still fails to enable:
1. Open your security software and check if LarePass was blocked.
2. Add the main LarePass executable to the allowlist** or exclusions of your antivirus.
3. Restart LarePass and enable the VPN.
### Why the `.local` domain does not work in Chrome (macOS)?
Chrome may fail to access local URLs if macOS blocks local network permissions.
To enable access:
1. Open Apple menu and go to **System Settings**.
2. Go to **Privacy & Security** > **Local Network**.
3. Find Google Chrome and Google Chrome Helper in the list and enable the toggles.
![Enable local network](/images/manual/larepass/mac-chrome-local-access.png#bordered){width=400}
4. Restart Chrome and try accessing the local URL again.
### Why does the application fail to load in an iFrame when using a `.local` domain on Chrome (macOS)?
Chrome might default to HTTPS when using local domains, and you might see a "connection not secure" warning.
![Incorrect local address](/images/manual/get-started/incorrect-local-address.png#bordered)
To address this, explicitly add the HTTP protocol (`http://`) to the beginning of the URL. This tells Chrome it's a local, non-encrypted connection, which is expected on your home network.

View File

@@ -15,7 +15,6 @@ Keep your mnemonic phrase safe and secret, and never share it with anyone. It is
When exporting or backing up your mnemonic phrase for the first time, you may be prompted to set a local password for LarePass. This password is only used to unlock LarePass services on the current device.
After setting up, you can choose to enable biometric unlock for more secure and convenient access using face recognition or fingerprint.
![Set up local password](/images/manual/get-started/set-up-local-password.png)
:::info
@@ -52,4 +51,4 @@ To prevent this, we strongly recommend taking these precautions:
* **Multi-device backup**: Use LarePass's Vault to encrypt and save your mnemonic phrase on multiple devices. You will lose your mnemonic phrase only if all these devices are lost.
### I've activated Olares, why do I get a password error when trying to view my mnemonic phrase in LarePass?
If you encounter a password error, it may be because you haven't set a local password. Open the LarePass app, go to **Settings** > **LarePass settings** > **Security**, and set a local password. Then try the backup process again.
If you encounter a password error, it may be because you haven't set a local password. Open the LarePass app, go to **Settings** > **LarePass settings** > **Security**, and set a local password. Then try the backup process again.

View File

@@ -1,77 +1,38 @@
---
outline: [2, 3]
description: Learn how to securely access your Olares from anywhere. This guide explains public vs. private entrances, when to use LarePass VPN, how to enable VPN on mobile and desktop.
description: Learn how to securely access your Olares from anywhere.
---
# Access Olares services remotely via LarePass VPN
Your Olares device hosts critical applications intended for personal or internal use, such as Vault and Ollama. To ensure security, these applications are accessed via [private or internal entrances](../../developer/concepts/network.md#private-entrance).
# Access Olares anywhere
To ensure the best connection to these apps, it's recommended to enable LarePass VPN. Once enabled, LarePass uses Tailscale to establish a secure network and automatically selects the fastest route based on location:
This guide explains **how to reach your Olares from anywhere**. You will learn:
- **At home**: The app connects directly via the local network for maximum speed.
- **Remote**: The app creates a direct, encrypted P2P tunnel to the device.
1) The access paths for public vs. private entrances.
2) How to enable LarePass VPN on your mobile and desktop.
3) Interpret connection status and know when to troubleshoot.
## How access works in Olares
In Olares, you access each app or service via its own URL (`https://app.olares-id.olares.com`, for example, `https://desktop.nicholas.olares.com/`). Depending on who should reach it, there are two entrance types.
### Public entrance
* Accessible to anyone on the internet without authentication. For example, a public blog hosted on WordPress.
* Traffic is securely routed from the internet to Olares via Cloudflare Tunnel or FRP.
### Private entrance
Application entrances intended only for you, such as Desktop, Vault, and the management console of WordPress. Depending on where you are, there are two scenarios when accessing private entrances:
- **Remote access** (Outside your local network)
- **With LarePass VPN (Recommended):** Traffic is routed directly and securely through the VPN (Tailscale), no matter where you are.
- **Without LarePass VPN:** Traffic is routed through the same internet tunnel as public access (Cloudflare/FRP).
- **Local access** (On the same network)
Use the local URL (`http://app.yourname.olares.local`) for a direct, local connection that bypasses the VPN and internet tunnels.
:::tip For macOS users
Chrome may fail to access local URLs if macOS blocks local network permissions.
To enable access:
1. Open Apple menu and go to **System Settings**.
2. Go to **Privacy & Security** > **Local Network**.
3. Find Google Chrome and Google Chrome Helper in the list and enable the toggles.
![Enable local network](/public/images/manual/larepass/mac-chrome-local-access.png#bordered){width=400}
Restart Chrome and try accessing the local URL again.
:::
:::info For Windows users
Currently, local access via `.local` domains is not supported on Windows.
:::
:::warning Always enable VPN for remote access
For the best experience with private apps when you're away from your network, enable **LarePass VPN**. It keeps your connection to Olares encrypted, direct, and fast.
:::
If the VPN is disabled, traffic routes through standard public internet tunnels using Cloudflare or FRP.
## Enable VPN on LarePass
:::tip
For different LarePass download options, visit [the official page](https://www.olares.com/larepass).
:::info iOS and macOS setup
On iOS or macOS, you may be prompted to add a VPN Configuration to your system settings the first time you enable the feature. Allow this to complete the setup.
:::
![VPN](/images/manual/larepass/vpn.jpg)
<tabs>
<template #On-LarePass-mobile-client>
### On LarePass mobile client
1. Open LarePass, and go to **Settings**.
1. Open the LarePass app, and go to **Settings**.
2. In the **My Olares** card, toggle on the VPN switch.
### On LarePass desktop client
1. Open LarePass, click on the avatar area in the top left corner of the main interface.
2. Toggle on the switch for **VPN connection** in the pop-up panel.
![Enable LarePass VPN on mobile](/images/manual/get-started/larepass-vpn-mobile.png#bordered)
</template>
<template #On-LarePass-desktop-client>
Devices with activated VPN will use the VPN connection to access Olares, whether through the LarePass client or a browser.
1. Open the LarePass app, and click your avatar in the top-left corner to open the user menu.
2. Toggle on the switch for **VPN connection**.
:::info
iOS or macOS versions of LarePass will require adding a VPN configuration file to the system when turning on the VPN. Follow the prompts to complete the setup.
:::
![Enable LarePass VPN on desktop](/images/manual/get-started/larepass-vpn-desktop.png#bordered)
</template>
</tabs>
## Understand connection status
LarePass displays the connection status between your device and Olares, helping you understand or diagnose your current network connection.

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 890 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@@ -1,4 +1,5 @@
---
outline: [2, 3]
description: Olares 网络架构设计说明,阐述应用入口类型、本地访问机制、端点配置和内部网络安全策略的基本原理。
---
# 网络
@@ -9,26 +10,21 @@ Olares 为用户提供无障碍且安全灵活的网络解决方案。本文档
每个 Olares 应用可以配置一个或多个入口来接入外部访问。入口分为三种类型:
- **公开入口**
### 公开入口
- 托管博客、社交媒体等需要外部访问的服务
- 无需认证即可访问
- 通过 Cloudflare 提供基础安全防护
- 托管博客、社交媒体等需要外部访问的服务
- 无需认证即可访问
- 通过 Cloudflare 提供基础安全防护
- **私有入口**
### 私有入口
- 专门为个人、家庭或团队提供服务
- 适用于阅读器、娱乐、生产力工具、桌面应用等
- 需要通过[认证](account.md#多因素认证mfa)才能访问
- 专门为个人、家庭或团队提供服务
- 适用于阅读器、娱乐、生产力工具、桌面应用等
- 需要通过[认证](account.md#多因素认证mfa)才能访问
## 通过 LarePass 专用网络访问私有入口
只需在设备上安装 LarePass,并启用[专用网络](/zh/manual/larepass/private-network.md),即可通过专属网址(如
`https://vault.alice123.olares.com`)安全、快速地访问您的私有应用。
::: tip 注意
如不启用 LarePass 专用网络,私有入口的请求会通过你的反向代理通道到达 Olares可能会有网络延迟并产生费用。
:::
### 内部入口
- 功能与私有入口类似。
- 通过 LarePass VPN 访问应用时,无需身份验证。
## 端点

View File

@@ -0,0 +1,235 @@
---
outline: [2,3]
description: 了解如何通过局域网 (LAN) 直接访问 Olares 应用和服务,以获得最快速度、隐私保护和离线可靠性。
---
# 内网访问 Olares 服务
通常你可以通过浏览器使用 URL例如 `https://desktop.<username>.olares.com`)来访问 Olares 服务。这种方式让你可以随时随地通过任何设备访问服务。
然而,通过内网直接访问设备具有显著优势:
- **极致性能**:在本地网络上传输文件时不受互联网延迟和瓶颈的影响。
- **隐私保护**:将流量严格限制在家庭网络内以增加安全性。
- **离线可用**:即使互联网服务不可用,也可以访问数据和应用。
本文档介绍了建立本地连接的几种方法:
- [启用 LarePass VPN推荐](#方法-1-启用-larepass-vpn)<br/>这是最简单的方案,自动建立最快连接,无需手动配置。
- [使用 `.local` 域名](#方法-2-使用-local-域名)<br/>此方法无需安装,但需根据操作系统使用特定的 URL 格式。
- [配置本地 DNS进阶](#方法-3-配置本地-dns)<br/>此方法通过更新路由器或单台电脑的 DNS 设置,支持使用标准 URL 进行本地访问。
- [修改 Host 文件(备选)](#方法-4-修改-hosts-文件)<br/>此方法在单台电脑上手动将标准 URL 映射到本地 IP确保在无外网连接时也能访问。
## 方法 1启用 LarePass VPN
无论是在设备旁还是在旅途中,连接的最稳健方式都是使用 LarePass VPN。它能智能检测你是否处于同一网络并切换到直接的**内网**模式以获得最大速度。
:::tip 始终启用 VPN 以进行远程访问
保持 LarePass VPN 启用。它会自动优先选择最快的可用路由,确保你无需手动切换即可获得最佳速度。
:::
:::info iOS 和 macOS 设置
在 iOS 或 macOS 上首次启用该功能时,系统可能会提示你添加 VPN 配置文件。允许此操作以完成设置。
:::
直接在你当前用于访问 Olares 的设备上启用 LarePass VPN。
<tabs>
<template #使用-LarePass-移动端>
1. 打开 LarePass 应用,进入**设置**。
2. 在**我的 Olares** 卡片中,打开 VPN 开关。
![移动端开启 LarePass VPN](/images/zh/manual/get-started/larepass-vpn-mobile.png#bordered)
</template>
<template #使用-LarePass-桌面端>
1. 打开 LarePass 应用,点击左上角的头像打开用户菜单。
2. 打开**专用网络连接**开关。
![桌面端开启 LarePass VPN](/images/zh/manual/get-started/larepass-vpn-desktop.png#bordered)
</template>
</tabs>
启用后,可以查看 LarePass 中的网络状态以确认连接类型:
| 状态 | 描述 |
|:-----------|:---------------------------|
| **内网** | 通过本地局域网 IP 直连。速度最快。 |
| **P2P** | 设备间的直接加密隧道。连接速度快。 |
| **DERP** | 通过安全中继服务器路由,仅在无法直连时作为备用方案。 |
## 方法 2使用 `.local` 域名
如果不想使用 VPN可以使用 `.local` 域名访问服务。根据兼容性需求,有两种域名格式可用。
### 单级域名(所有操作系统适用)
:::warning 仅支持社区应用
Desktop 和文件管理器等 Olares 系统应用不支持此 URL 格式,因此无法正确加载。
:::
此格式通过连字符(`-`)连接入口 ID 和用户名来使用单级主机名。
- **默认 URL**:
```plain
https://<entrance_id>.<username>.olares.cn
```
- **本地访问 URL**:
```plain
http://<entrance_id>-<username>-olares.local
```
### 多级域名 (macOS 和 iOS 适用)
Apple 设备支持通过 [Bonjour](https://developer.apple.com/bonjour/)(零配置网络)进行本地服务发现,因此能够在 macOS 和 iOS 上解析 `.local` 下的多级域名。这使得本地 URL 格式可以与默认的远程访问地址保持结构一致。
- **默认 URL**:
```plain
https://<entrance_id>.<username>.olares.cn
```
- **本地访问 URL**:
```plain
http://<entrance_id>.<username>.olares.local
```
![多级域名](/images/manual/get-started/multilevel-local-domain-mac.png#bordered)
## 方法 3配置本地 DNS
为了获得无缝体验(即标准 URL 自动解析为你的本地 IP 地址),你可以配置网络 DNS。此配置确保网络上所有设备的访问一致无需单独设置客户端。
### 查找 Olares 设备的内网 IP
要配置 DNS首先需要找到 Olares 设备的内网 IP。
<tabs>
<template #通过-LarePass-手机端查看>
如果你的手机和 Olares 设备处于同一网络:
1. 打开 LarePass 应用,点击**设置** > **系统**,进入 **Olares 管理**页面。
![点击 System 卡片](/images/zh/manual/get-started/larepass-system.png#bordered)
2. 点击设备卡片。
![点击设备卡片](/images/zh/manual/get-started/larepass-device-card.png#bordered)
3. 并向下滚动到**网络**部分。可以在此处找到**内网 IP**。
![找到网络部分](/images/zh/manual/get-started/larepass-network.png#bordered)
</template>
<template #通过-Olares-终端查看>
控制面板提供了内置终端,允许你直接从浏览器运行系统命令,无需外部 SSH 客户端。
1. 打开控制面板,在左侧导航栏的**终端**下选择 **Olares**。
![从控制面板查找内网 IP](/images/zh/manual/get-started/find-internal-ip-from-controlhub.png#bordered)
2. 在终端输入 `ifconfig` 并按下 **Enter** 确认。
3. 寻找你的活动连接,通常命名为 `enp3s0`(有线)或 `wlo1`无线。IP 地址位于 `inet` 之后。
输出示例:
```bash
enp3s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.50.116 netmask 255.255.255.0 broadcast 192.168.50.255
inet6 fe80::4194:4045:c35e:7b32 prefixlen 64 scopeid 0x20<link>
ether d8:43:ae:54:ce:fc txqueuelen 1000 (Ethernet)
RX packets 80655321 bytes 71481515308 (71.4 GB)
RX errors 0 dropped 136 overruns 0 frame 0
TX packets 51867817 bytes 15924740708 (15.9 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
```
在此示例中,`192.168.50.116` 即为内网 IP。
</template>
</tabs>
### 配置 DNS
确定内网 IP 地址后,现在必须配置 DNS 设置以正确路由流量。你可以将此配置应用于单台电脑以供个人访问,或者更新路由器以实现网络上所有设备的无缝本地解析。
<tabs>
<template #设置单台设备>
更新特定电脑上的 DNS 设置。以 macOS 为例:
1. 打开 Apple 菜单,进入**系统设置**。
2. 选择 **Wi-Fi**,然后点击已连接网络的**详细信息**。
3. 选择 **DNS** 并更新服务器列表:
a. 点击 **DNS 服务器**下的 **+** 按钮,添加 Olares 设备的内网 IP例如 `192.168.x.x`)。
b. 确保 Olares IP 列在顶部。在其下方添加你原来的 DNS或 `1.1.1.1`)作为备用。<br/>这确保了如果 Olares 设备关机,路由器会自动切换到辅助 DNS保持互联网连接正常。
4. 点击**好**保存更改。
</template>
<template #设置所有设备>
更新路由器上的 DNS将更改应用于网络中的所有设备。
1. 登录路由器的管理面板。
2. 导航至 **DHCP / DNS 设置**。
3. 将**首选 DNS** 设置为 Olares 设备的内网 IP例如 `192.168.x.x`)。
4. 将**备用 DNS** 设置为当前的首选 DNS或公共提供商如 `1.1.1.1`)。<br/>这确保了如果 Olares 设备关机,路由器会自动切换到辅助 DNS保持互联网连接正常。
5. 保存并重新连接设备以刷新 DNS 缓存。
</template>
</tabs>
配置完成后,可以使用标准公网地址和本地地址访问 Olares。
:::tip
你可以从 Olares 应用市场安装 AdGuard Home以图形化方式监控流量并管理 DNS 映射。
:::
## 方法 4修改 hosts 文件
如果无法更改路由器设置且需要在特定电脑上立即离线访问,可以在 hosts 文件中手动映射域名。
1. 找到 hosts 文件:
- **Windows:** `C:\Windows\System32\drivers\etc\hosts`
- **macOS/Linux:** `/etc/hosts`
2. 使用文本编辑器打开文件(需要管理员权限)。
3. 添加映射行:
```plain
# 替换为实际内网 IP 和用户名
# Olares 应用
192.168.31.208 desktop.<username>.olares.cn
192.168.31.208 auth.<username>.olares.cn
192.168.31.208 files.<username>.olares.cn
192.168.31.208 market.<username>.olares.cn
192.168.31.208 settings.<username>.olares.cn
192.168.31.208 dashboard.<username>.olares.cn
192.168.31.208 control-hub.<username>.olares.cn
192.168.31.208 profile.<username>.olares.cn
192.168.31.208 vault.<username>.olares.cn
# 根据需要添加其他社区应用
192.168.31.208 <entrance_id>.<username>.olares.cn
```
4. 保存文件以应用更改。这能确保即使在断网情况下,你也能进行本地访问。
你可以通过检查 URL 加载速度或使用终端来验证更改。例如:
```bash
ping desktop.<username>.olares.cn
```
如果返回的 IP 地址以 `192.168` 开头,即表示配置成功。
## 了解更多
- [通过 LarePass VPN 随时随地访问 Olares](../../../zh/manual/larepass/private-network.md): 了解如何使用 LarePass VPN。
- [网络](../../../zh/developer/concepts/network.md): 了解 Olares 中的应用的各类入口。
## 常见问题
### 为什么在 Mac 上无法再启用 LarePass VPN
如果之前成功启用过 VPN但现在停止工作可能需要重置系统扩展。
:::info
根据 macOS 版本不同,界面可能略有差异。
:::
1. 打开**系统设置**,搜索 “扩展",选择**登录项与扩展**。
2. 滚动到**网络扩展** 部分,点击信息图标 (ⓘ) 查看已加载的扩展。
3. 找到 LarePass点击三个点 (...),选择**删除扩展**。
4. 确认卸载。
5. 重启 Mac 并在 LarePass 桌面客户端中重新启用 VPN。
### 为什么我在 Windows 上无法启用 LarePass VPN
第三方杀毒软件可能会错误地将 LarePass 桌面客户端标记为可疑,从而阻止其启动 VPN 服务。
如果在首次打开 LarePass 时收到杀毒软件提示,请允许应用程序继续运行。
如果 VPN 仍然无法启用:
1. 打开安全软件,检查 LarePass 是否被拦截。
2. 将 LarePass 主程序添加到杀毒软件的白名单或排除项中。
3. 重启 LarePass 并启用 VPN。
### 为什么在 macOS 上Chrome 无法访问 `.local` 域名?
如果 macOS 未授予局域网访问权限Chrome 可能会无法访问本地 URL。
要启用访问权限:
1. 打开 Apple 菜单,进入**系统设置**。
2. 进入**隐私与安全性** > **局域网**。
3. 在列表中找到 Google Chrome 和 Google Chrome Helper并开启开关。
![启用局域网权限](/images/manual/larepass/mac-chrome-local-access.png#bordered){width=400}
4. 重启 Chrome 并再次尝试访问本地 URL。
### 为什么在 Chrome 上使用 `.local` 域名时,应用无法在 iFrame 中加载 (macOS)
使用本地域名时Chrome 可能会默认使用 HTTPS你可能会看到“连接不安全”的警告。
![本地地址错误](/images/manual/get-started/incorrect-local-address.png#bordered)
要解决此问题,在 URL 开头显式添加 HTTP 协议头 (`http://`),告诉浏览器这是一个仅在本地网络中使用的链接。

View File

@@ -1,6 +1,8 @@
---
outline: [2, 3]
description: 学习备份 Olares 账号助记词的正确方法,确保账号安全和数据访问权限。
---
# 备份助记词
助记词是由 12 个单词组成的序列,是恢复 Olares ID 的唯一方式。如果你的设备丢失或需要导入 Olares 账号,可以使用助记词重新获得 Olares 的访问权限。
@@ -9,7 +11,6 @@ description: 学习备份 Olares 账号助记词的正确方法,确保账号
请妥善保管并确保助记词的私密性,切勿与任何人分享。这是恢复 Olares 账号的唯一方式。
:::
## 设置本地密码
首次导出或备份助记词时,系统可能会提示你为 LarePass 设置本地密码。该密码仅用于解锁当前设备上的 LarePass 服务。
@@ -19,7 +20,7 @@ description: 学习备份 Olares 账号助记词的正确方法,确保账号
:::info
* 同一设备上的所有 Olares ID 共用一个 LarePass 本地密码。
* 不同设备上安装的 LarePass 应用的本地密码相互独立,单独存储。
:::
:::
## 查看并备份助记词
1. 打开 LarePass 应用,进入**设置** > **LarePass 设置** > **安全**页面。
@@ -34,6 +35,7 @@ description: 学习备份 Olares 账号助记词的正确方法,确保账号
7. 按正确顺序排列助记词以验证备份。
8. 点击**完成**。
如果排列正确,即表示你已成功备份助记词。
![备份助记词](/images/manual/get-started/backup-mnemonic-phrase.png)
## 常见问题
@@ -49,8 +51,4 @@ description: 学习备份 Olares 账号助记词的正确方法,确保账号
* **多设备备份**:使用 LarePass 的保险库功能加密保存助记词在多个设备上。只有当所有这些设备都丢失时,你才会失去助记词。
### 我已经激活了 Olares为什么在 LarePass 中查看助记词时提示密码错误?
如果遇到密码错误,可能是因为你还没有设置本地密码。打开 LarePass 应用,进入**设置** > **LarePass 设置** > **安全**,设置本地密码后再尝试备份。
## 恭喜!
现在你已经准备好进一步探索 Olares
- [接下来做什么](../get-started/next-steps.md)
如果遇到密码错误,可能是因为你还没有设置本地密码。打开 LarePass 应用,进入**设置** > **LarePass 设置** > **安全**,设置本地密码后再尝试备份。

View File

@@ -1,82 +1,40 @@
---
outline: [2, 3]
description:"本文介绍如何在任意环境安全访问 Olares: 区分公有/私有入口、何时启用 LarePass VPN、本地直连的使用场景以及在 LarePass 中开启 VPN。"
description: 本文介绍如何在任意环境安全访问 Olares
---
# 通过 LarePass VPN 随时随地访问 Olares
Olares 设备上运行着 Vault 和 Ollama 等供个人或内部使用的关键应用。出于安全考量,这些应用只能通过[私有或内部入口](../../developer/concepts/network.md#私有入口)访问。
# 随时随地访问 Olares
为了获得最流畅的连接体验,建议你始终开启 LarePass VPN。开启后LarePass 会利用 Tailscale 建立安全连接,并根据你所处的位置智能选择最快线路:
本文档介绍如何从任何地点访问你的 Olares 应用。你将学到:
1)公有/私有入口的访问路径与适用场景
2) 在移动端与桌面端**开启 LarePass VPN**的方法
3) 如何**解读连接状态**并据此诊断网络质量。
- **居家** 通过局域网 (LAN) 直连,传输速度最快。
- **外出** 与设备建立点对点 (P2P) 加密隧道直连。
## 了解 Olares 上的应用访问
在 Olares 中,你通过每个应用或服务的专有 URL (例如 `https://app.olares-id.olares.cn`,如 `https://desktop.nicholas.olares.com/`) 来访问它们。根据访问对象的不同,有两种入口类型。
### 公共入口
* 互联网上的任何人都可以访问,无需身份验证。例如,一个托管在 WordPress 上的公共博客。
* 流量通过 Cloudflare Tunnel 或 FRP 从互联网安全路由到 Olares。
### 私有入口
仅供你个人使用的应用程序入口,例如桌面 (Desktop)、Vault 以及 WordPress 的管理控制台。根据你所在的位置,访问私有入口时有两种情况:
- **远程访问** (在你的本地网络之外)
- **使用 LarePass VPN (推荐)** 无论你身在何处,流量都可以通过 VPN (Tailscale) 进行直接且安全的路由。
- **不使用 LarePass VPN** 流量将通过与公共访问相同的互联网隧道 (Cloudflare/FRP) 进行路由。
- **本地访问** (在同一局域网中)
使用本地 URL (`http://app.yourname.olares.local`) 直连 Olares无需通过 VPN 和互联网隧道。
:::tip macOS 用户注意
如在 macOS 上使用 Chrome 无法访问本地 URL可能是因系统未授予 Chrome 本地网络访问权限。
请按以下步骤启用权限:
1. 打开苹果菜单 > **系统设置**
2. 点击边栏的**隐私与安全性**,然后点击**本地网络**。
3. 找到列表中的 Google Chrome 和 Google Chrome Helper 并开启旁边的开关。
![启用本地网络](/public/images/manual/larepass/mac-chrome-local-access.png#bordered){width=400}
完成后重新启动 Chrome再次尝试访问本地 URL。
:::
:::info Windows 用户注意
Windows 系统暂不支持通过`.local`结尾的域名访问本地服务。
:::
:::warning 远程访问时请启用 VPN
当你不在自己的本地网络中时,为获得最佳的私有应用访问体验,请启用 **LarePass VPN**。它能确保你与 Olares 之间可始终保持加密、私有且高速的通道。
:::
若未开启 VPN流量将经由 Cloudflare 或 FRP 等公网进行转发,速度可能会受到影响。
## 在 LarePass 中启用专用网络
::: tip
查看各平台安装包,请访问[官方下载页面](https://olares.cn/larepass)。
:::info iOS 和 macOS 设置
在 iOS 或 macOS 上首次启用该功能时,系统可能会提示你添加 VPN 配置文件。允许此操作以完成设置。
:::
![VPN](/images/manual/larepass/vpn.jpg)
<tabs>
<template #使用-LarePass-移动端>
### LarePass 移动端
1. 打开 LarePass 应用,进入**设置**。
2. 在**我的 Olares** 卡片中,打开 VPN 开关。
1. 打开 LarePass依次进入**设置** > **我的 Olares**
2. 打开 **VPN** 开关。
![移动端开启 LarePass VPN](/images/zh/manual/get-started/larepass-vpn-mobile.png#bordered)
</template>
<template #使用-LarePass-桌面端>
### LarePass 桌面端
1. 打开 LarePass 应用,点击左上角的头像打开用户菜单。
2. 打开**专用网络连接**开关。
1. 打开 LarePass点击主界面左上角头像区域。
2. 在弹出面板中打开**专用网络连接**开关。
启用专用网络后,无论使用 LarePass 客户端还是浏览器,设备都会通过专用网络访问 Olares。
::: info
在 iOS 或 macOS 上首次开启专用网络时,系统会提示添加专用网络配置文件,请按指引完成。
:::
![桌面端开启 LarePass VPN](/images/zh/manual/get-started/larepass-vpn-desktop.png#bordered)
</template>
</tabs>
## 了解连接状态
LarePass 会展示设备到 Olares 的连接状态,帮助你判断或诊断网络情况。
![连接状态](/images/manual/larepass/connection-status.jpg)
@@ -91,11 +49,10 @@ LarePass 会展示设备到 Olares 的连接状态,帮助你判断或诊断网
| Offline mode | 当前离线,无法连接到 Olares |
::: info
若在外网环境使用专用网络访问私有入口时,连接状态显示 **DERP**,说明专用网络无法直接通过 P2P 连接 Olares需要借助 Tailscale 中继服务器,这可能影响连接质量。如长期如此,请联系 Olares 支持。
若在外网环境使用专用网络访问私有入口时,连接状态显示 "DERP",说明专用网络无法直接通过 P2P 连接 Olares需要借助 Tailscale 中继服务器,这可能影响连接质量。如长期如此,请联系 Olares 支持。
:::
## 故障排查
出现连接问题时LarePass 会显示诊断信息。常见提示及处理办法如下:
![异常消息](/images/zh/manual/larepass/abnormal-state.jpg)

View File

@@ -64,7 +64,7 @@ Vault 项目是存储敏感信息的独立安全容器。每个 Vault 项目包
![Vault password](/images/manual/olares/vault-local-password.png)
:::tip 提示
如果你不知道助记词短语的位置,请参阅[备份助记词短语](../../larepass/back-up-mnemonics)。
如果你不知道助记词短语的位置,请参阅[备份助记词短语](../../larepass/back-up-mnemonics.md)。
:::
## 管理 Vault 项目

View File

@@ -170,7 +170,7 @@ spec:
priorityClassName: "system-cluster-critical"
containers:
- name: app-service
image: beclab/app-service:0.4.63
image: beclab/app-service:0.4.67
imagePullPolicy: IfNotPresent
securityContext:
runAsUser: 0
@@ -228,6 +228,8 @@ spec:
valueFrom:
fieldRef:
fieldPath: status.hostIP
- name: OLARESD_HOST
value: $(HOSTIP):18088
volumeMounts:
- mountPath: /charts
name: charts-store
@@ -393,7 +395,7 @@ spec:
priorityClassName: "system-cluster-critical"
containers:
- name: image-service
image: beclab/image-service:0.4.63
image: beclab/image-service:0.4.65
imagePullPolicy: IfNotPresent
securityContext:
runAsUser: 0

View File

@@ -377,6 +377,8 @@ func (r *ApplicationReconciler) createApplication(ctx context.Context, req ctrl.
func (r *ApplicationReconciler) updateApplication(ctx context.Context, req ctrl.Request,
deployment client.Object, app *appv1alpha1.Application, name string) error {
appCopy := app.DeepCopy()
appNames := getAppName(deployment)
isMultiApp := len(appNames) > 1
tailScale, err := r.getAppTailScale(deployment)
if err != nil {
@@ -390,11 +392,37 @@ func (r *ApplicationReconciler) updateApplication(ctx context.Context, req ctrl.
icon = icons[name]
entrancesMap, err := r.getEntranceServiceAddress(ctx, deployment, isMultiApp)
if err != nil {
ctrl.Log.Error(err, "get entrance error")
}
servicePortsMap, err := r.getAppPorts(ctx, deployment, isMultiApp)
if err != nil {
klog.Warningf("get app ports err=%v", err)
}
var appid string
if userspace.IsSysApp(name) {
appid = name
} else {
appid = appv1alpha1.AppName(name).GetAppID()
}
settings, sharedEntrances := r.getAppSettings(ctx, name, appid, owner, deployment, isMultiApp, entrancesMap[name])
appCopy.Spec.Name = name
appCopy.Spec.Namespace = deployment.GetNamespace()
appCopy.Spec.Owner = owner
appCopy.Spec.DeploymentName = deployment.GetName()
appCopy.Spec.Icon = icon
appCopy.Spec.SharedEntrances = sharedEntrances
appCopy.Spec.Ports = servicePortsMap[name]
appCopy.Spec.Entrances = entrancesMap[name]
if settings["defaultThirdLevelDomainConfig"] != "" {
if appCopy.Spec.Settings == nil {
appCopy.Spec.Settings = make(map[string]string)
}
appCopy.Spec.Settings["defaultThirdLevelDomainConfig"] = settings["defaultThirdLevelDomainConfig"]
}
if tailScale != nil {
appCopy.Spec.TailScale = *tailScale
}

View File

@@ -93,6 +93,7 @@ type RequirementResp struct {
Response
Resource string `json:"resource"`
Message string `json:"message"`
Reason string `json:"reason"`
}
// AppSource describe the source of an application, recommend,model,agent

View File

@@ -343,23 +343,25 @@ func (h *installHandlerHelper) validate(isAdmin bool, installedApps []*v1alpha1.
}
//resourceType, err := CheckAppRequirement(h.h.kubeConfig, h.token, h.appConfig)
resourceType, err := apputils.CheckAppRequirement(h.token, h.appConfig)
resourceType, resourceConditionType, err := apputils.CheckAppRequirement(h.token, h.appConfig, v1alpha1.InstallOp)
if err != nil {
klog.Errorf("Failed to check app requirement err=%v", err)
h.resp.WriteHeaderAndEntity(http.StatusBadRequest, api.RequirementResp{
Response: api.Response{Code: 400},
Resource: resourceType,
Resource: resourceType.String(),
Message: err.Error(),
Reason: resourceConditionType.String(),
})
return
}
resourceType, err = apputils.CheckUserResRequirement(h.req.Request.Context(), h.appConfig, h.owner)
resourceType, resourceConditionType, err = apputils.CheckUserResRequirement(h.req.Request.Context(), h.appConfig, v1alpha1.InstallOp)
if err != nil {
h.resp.WriteHeaderAndEntity(http.StatusBadRequest, api.RequirementResp{
Response: api.Response{Code: 400},
Resource: resourceType,
Resource: resourceType.String(),
Message: err.Error(),
Reason: resourceConditionType.String(),
})
return
}

View File

@@ -72,6 +72,9 @@ func (h *Handler) uninstall(req *restful.Request, resp *restful.Response) {
return
}
am.Spec.OpType = v1alpha1.UninstallOp
if am.Annotations == nil {
am.Annotations = make(map[string]string)
}
am.Annotations[api.AppTokenKey] = token
am.Annotations[api.AppUninstallAllKey] = fmt.Sprintf("%t", request.All)
err = h.ctrlClient.Update(req.Request.Context(), &am)

View File

@@ -1,13 +1,17 @@
package apiserver
import (
"encoding/json"
"errors"
"fmt"
"k8s.io/klog/v2"
"net/http"
"strconv"
"time"
"bytetrade.io/web3os/app-service/api/app.bytetrade.io/v1alpha1"
"bytetrade.io/web3os/app-service/pkg/apiserver/api"
"bytetrade.io/web3os/app-service/pkg/appcfg"
"bytetrade.io/web3os/app-service/pkg/appstate"
"bytetrade.io/web3os/app-service/pkg/constants"
"bytetrade.io/web3os/app-service/pkg/kubesphere"
@@ -99,6 +103,12 @@ func (h *Handler) suspend(req *restful.Request, resp *restful.Response) {
func (h *Handler) resume(req *restful.Request, resp *restful.Response) {
app := req.PathParameter(ParamAppName)
owner := req.Attribute(constants.UserContextAttribute).(string)
token, err := h.GetUserServiceAccountToken(req.Request.Context(), owner)
if err != nil {
klog.Error("Failed to get user service account token: ", err)
api.HandleError(resp, req, err)
return
}
name, err := apputils.FmtAppMgrName(app, owner, "")
if err != nil {
@@ -116,6 +126,36 @@ func (h *Handler) resume(req *restful.Request, resp *restful.Response) {
api.HandleBadRequest(resp, req, fmt.Errorf("%s operation is not allowed for %s state", v1alpha1.ResumeOp, am.Status.State))
return
}
var appCfg *appcfg.ApplicationConfig
err = json.Unmarshal([]byte(am.Spec.Config), &appCfg)
if err != nil {
klog.Errorf("unmarshal to appConfig failed %v", err)
api.HandleError(resp, req, err)
return
}
resourceType, resourceConditionType, err := apputils.CheckAppRequirement(token, appCfg, v1alpha1.ResumeOp)
if err != nil {
klog.Errorf("Failed to check app requirement err=%v", err)
resp.WriteHeaderAndEntity(http.StatusBadRequest, api.RequirementResp{
Response: api.Response{Code: 400},
Resource: resourceType.String(),
Message: err.Error(),
Reason: resourceConditionType.String(),
})
return
}
resourceType, resourceConditionType, err = apputils.CheckUserResRequirement(req.Request.Context(), appCfg, v1alpha1.ResumeOp)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, api.RequirementResp{
Response: api.Response{Code: 400},
Resource: resourceType.String(),
Message: err.Error(),
Reason: resourceConditionType.String(),
})
return
}
am.Spec.OpType = v1alpha1.ResumeOp
// if current user is admin, also resume server side

View File

@@ -1382,12 +1382,12 @@ func (h *Handler) installOpValidate(ctx context.Context, appConfig *appcfg.Appli
if err != nil {
return err
}
_, err = apputils.CheckAppRequirement("", appConfig)
_, _, err = apputils.CheckAppRequirement("", appConfig, v1alpha1.InstallOp)
if err != nil {
return err
}
_, err = apputils.CheckUserResRequirement(ctx, appConfig, appConfig.OwnerName)
_, _, err = apputils.CheckUserResRequirement(ctx, appConfig, v1alpha1.InstallOp)
if err != nil {
return err
}

View File

@@ -56,6 +56,7 @@ type ApplicationConfig struct {
APIVersion APIVersion
CfgFileVersion string
Namespace string
MiddlewareName string
ChartsName string
RepoURL string
Title string

View File

@@ -6,7 +6,7 @@ import (
"os"
"path/filepath"
"k8s.io/apimachinery/pkg/api/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"bytetrade.io/web3os/app-service/api/app.bytetrade.io/v1alpha1"
"bytetrade.io/web3os/app-service/pkg/appcfg"
@@ -258,6 +258,13 @@ func (h *HelmOps) SetValues() (values map[string]interface{}, err error) {
klog.Infof("values[node]: %#v", values["nodes"])
deviceName, err := utils.GetDeviceName()
if err != nil {
klog.Errorf("failed to get deviceName %v", err)
return values, err
}
values["deviceName"] = deviceName
return values, err
}
@@ -296,7 +303,7 @@ func (h *HelmOps) getInstalledApps(ctx context.Context) (installed bool, app []*
func (h *HelmOps) AddEnvironmentVariables(values map[string]interface{}) error {
values[constants.OlaresEnvHelmValuesKey] = make(map[string]interface{})
appEnv, err := h.client.AppClient.SysV1alpha1().AppEnvs(h.app.Namespace).Get(h.ctx, apputils.FormatAppEnvName(h.app.AppName, h.app.OwnerName), metav1.GetOptions{})
if errors.IsNotFound(err) {
if apierrors.IsNotFound(err) {
return nil
}
if err != nil {

View File

@@ -2,11 +2,16 @@ package appstate
import (
"context"
"fmt"
"time"
appsv1 "bytetrade.io/web3os/app-service/api/app.bytetrade.io/v1alpha1"
"bytetrade.io/web3os/app-service/pkg/constants"
"bytetrade.io/web3os/app-service/pkg/images"
apputils "bytetrade.io/web3os/app-service/pkg/utils/app"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -40,28 +45,47 @@ func NewDownloadingCancelingApp(c client.Client,
})
}
func (p *DownloadingCancelingApp) Exec(ctx context.Context) (StatefulInProgressApp, error) {
func (p *DownloadingCancelingApp) exec(ctx context.Context) error {
err := p.imageClient.UpdateStatus(ctx, p.manager.Name, appsv1.DownloadingCanceled.String(), appsv1.DownloadingCanceled.String())
if err != nil {
klog.Errorf("update im name=%s to downloadingCanceled state failed %v", p.manager.Name, err)
return nil, err
return err
}
if ok := appFactory.cancelOperation(p.manager.Name); !ok {
klog.Errorf("app %s operation is not ", p.manager.Name)
}
message := constants.OperationCanceledByUserTpl
if p.manager.Status.Message == constants.OperationCanceledByTerminusTpl {
message = constants.OperationCanceledByTerminusTpl
}
opRecord := makeRecord(p.manager, appsv1.DownloadingCanceled, message)
// FIXME: should check if the image downloading is canceled successfully
updateErr := p.updateStatus(ctx, p.manager, appsv1.DownloadingCanceled, opRecord, message, "")
if updateErr != nil {
klog.Errorf("update app manager %s to %s state failed %v", p.manager.Name, appsv1.DownloadingCanceled.String(), updateErr)
return nil, updateErr
if !apputils.IsProtectedNamespace(p.manager.Spec.AppNamespace) {
var ns corev1.Namespace
err = p.client.Get(ctx, types.NamespacedName{Name: p.manager.Spec.AppNamespace}, &ns)
if err != nil && !apierrors.IsNotFound(err) {
klog.Errorf("failed to get namespace %s, %v", p.manager.Spec.AppNamespace, err)
return err
}
if err == nil {
if delErr := p.client.Delete(ctx, &ns); delErr != nil && !apierrors.IsNotFound(delErr) {
klog.Errorf("failed to delete namespace %s, %v", p.manager.Spec.AppNamespace, delErr)
return delErr
}
}
}
return nil, nil
return nil
}
func (p *DownloadingCancelingApp) Exec(ctx context.Context) (StatefulInProgressApp, error) {
err := p.exec(ctx)
if err != nil {
updateErr := p.updateStatus(ctx, p.manager, appsv1.DownloadingCancelFailed, nil, err.Error(), "")
if updateErr != nil {
klog.Errorf("update app manager %s to %s state failed %v", p.manager.Name, appsv1.DownloadingCancelFailed.String(), updateErr)
return nil, updateErr
}
}
return &downloadingCancelInProgressApp{
DownloadingCancelingApp: p,
basePollableStatefulInProgressApp: &basePollableStatefulInProgressApp{},
}, nil
}
func (p *DownloadingCancelingApp) Cancel(ctx context.Context) error {
@@ -72,3 +96,58 @@ func (p *DownloadingCancelingApp) Cancel(ctx context.Context) error {
}
return nil
}
var _ PollableStatefulInProgressApp = &downloadingCancelInProgressApp{}
type downloadingCancelInProgressApp struct {
*DownloadingCancelingApp
*basePollableStatefulInProgressApp
}
func (p *downloadingCancelInProgressApp) Exec(ctx context.Context) (StatefulInProgressApp, error) {
return nil, nil
}
func (p *downloadingCancelInProgressApp) poll(ctx context.Context) error {
if apputils.IsProtectedNamespace(p.manager.Spec.AppNamespace) {
return nil
}
timer := time.NewTicker(time.Second)
defer timer.Stop()
for {
select {
case <-timer.C:
var ns corev1.Namespace
err := p.client.Get(ctx, types.NamespacedName{Name: p.manager.Spec.AppNamespace}, &ns)
klog.Infof("downloading cancel poll namespace %s err %v", p.manager.Spec.AppNamespace, err)
if apierrors.IsNotFound(err) {
return nil
}
case <-ctx.Done():
return fmt.Errorf("app %s execute cancel operation failed %w", p.manager.Spec.AppName, ctx.Err())
}
}
}
func (p *downloadingCancelInProgressApp) WaitAsync(ctx context.Context) {
appFactory.waitForPolling(ctx, p, func(err error) {
if err != nil {
updateErr := p.updateStatus(context.TODO(), p.manager, appsv1.DownloadingCancelFailed, nil, appsv1.DownloadingCancelFailed.String(), "")
if updateErr != nil {
klog.Errorf("update app manager %s to %s state failed %v", p.manager.Name, appsv1.DownloadingCancelFailed.String(), updateErr)
return
}
return
}
updateErr := p.updateStatus(context.TODO(), p.manager, appsv1.DownloadingCanceled, nil, appsv1.DownloadingCanceled.String(), "")
if updateErr != nil {
klog.Errorf("update app manager %s to %s state failed %v", p.manager.Name, appsv1.InstallingCanceled.String(), updateErr)
return
}
})
}

View File

@@ -181,6 +181,14 @@ func (p *DownloadingApp) exec(ctx context.Context) error {
}
values["nodes"] = nodeInfo
deviceName, err := utils.GetDeviceName()
if err != nil {
klog.Errorf("failed to get deviceName %v", err)
return err
}
values["deviceName"] = deviceName
refs, err := p.getRefsForImageManager(appConfig, values)
if err != nil {
klog.Errorf("get image refs from resources failed %v", err)

View File

@@ -1,18 +1,27 @@
package appstate
import (
"context"
"time"
appsv1 "bytetrade.io/web3os/app-service/api/app.bytetrade.io/v1alpha1"
"bytetrade.io/web3os/app-service/pkg/images"
apputils "bytetrade.io/web3os/app-service/pkg/utils/app"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// FIXME: impossible state
var _ StatefulApp = &DownloadingCancelFailedApp{}
var _ OperationApp = &DownloadingCancelFailedApp{}
type DownloadingCancelFailedApp struct {
*DoNothingApp
*baseOperationApp
imageClient images.ImageManager
}
func NewDownloadingCancelFailedApp(c client.Client,
@@ -20,12 +29,49 @@ func NewDownloadingCancelFailedApp(c client.Client,
return appFactory.New(c, manager, 0,
func(c client.Client, manager *appsv1.ApplicationManager, ttl time.Duration) StatefulApp {
return &DownloadingCancelFailedApp{
DoNothingApp: &DoNothingApp{
baseOperationApp: &baseOperationApp{
ttl: ttl,
baseStatefulApp: &baseStatefulApp{
manager: manager,
client: c,
},
},
imageClient: images.NewImageManager(c),
}
})
}
func (p *DownloadingCancelFailedApp) Exec(ctx context.Context) (StatefulInProgressApp, error) {
if !apputils.IsProtectedNamespace(p.manager.Spec.AppNamespace) {
var ns corev1.Namespace
err := p.client.Get(ctx, types.NamespacedName{Name: p.manager.Spec.AppNamespace}, &ns)
if err != nil && !apierrors.IsNotFound(err) {
return nil, err
}
if err == nil {
e := p.client.Delete(ctx, &ns)
if e != nil {
klog.Errorf("failed to delete ns %s, err=%v", p.manager.Spec.AppNamespace, e)
return nil, e
}
}
}
var im appsv1.ImageManager
err := p.client.Get(ctx, types.NamespacedName{Name: p.manager.Name}, &im)
if err != nil && !apierrors.IsNotFound(err) {
return nil, err
}
if im.Status.State != appsv1.DownloadingCanceled.String() {
err = p.imageClient.UpdateStatus(ctx, p.manager.Name, appsv1.DownloadingCanceled.String(), appsv1.DownloadingCanceled.String())
if err != nil {
return nil, err
}
}
return nil, nil
}
func (p *DownloadingCancelFailedApp) Cancel(ctx context.Context) error {
return nil
}

View File

@@ -67,9 +67,12 @@ func (p *PendingApp) Exec(ctx context.Context) (StatefulInProgressApp, error) {
if success, err := appFactory.addLimitedStatefulApp(ctx,
// limit
func() (bool, error) {
var apps appsv1.ApplicationManagerList
err := p.client.List(ctx, &apps)
clientset, err := utils.GetClient()
if err != nil {
klog.Errorf("failed to get clientset %v", err)
return false, err
}
apps, err := clientset.AppV1alpha1().ApplicationManagers().List(ctx, metav1.ListOptions{})
if err != nil {
klog.Errorf("list application managers error: %v", err)
return false, err

View File

@@ -1,18 +1,25 @@
package appstate
import (
"context"
"time"
appsv1 "bytetrade.io/web3os/app-service/api/app.bytetrade.io/v1alpha1"
apputils "bytetrade.io/web3os/app-service/pkg/utils/app"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// FIXME: impossible state
var _ StatefulApp = &PendingCancelFailedApp{}
var _ OperationApp = &PendingCancelFailedApp{}
type PendingCancelFailedApp struct {
*DoNothingApp
*baseOperationApp
}
func NewPendingCancelFailedApp(c client.Client,
@@ -20,7 +27,8 @@ func NewPendingCancelFailedApp(c client.Client,
return appFactory.New(c, manager, 0,
func(c client.Client, manager *appsv1.ApplicationManager, ttl time.Duration) StatefulApp {
return &PendingCancelFailedApp{
DoNothingApp: &DoNothingApp{
baseOperationApp: &baseOperationApp{
ttl: ttl,
baseStatefulApp: &baseStatefulApp{
manager: manager,
client: c,
@@ -30,11 +38,25 @@ func NewPendingCancelFailedApp(c client.Client,
})
}
//func (p *PendingCancelFailedApp) Exec(ctx context.Context) (StatefulInProgressApp, error) {
// // FIXME: should set a max retry count for cancel operation
// err := p.updateStatus(ctx, p.manager, appsv1.PendingCanceling, nil, appsv1.PendingCanceling.String())
// if err != nil {
// klog.Errorf("update app manager %s to %s state failed %v", p.manager.Name, appsv1.PendingCanceling, err)
// }
// return nil, err
//}
func (p *PendingCancelFailedApp) Exec(ctx context.Context) (StatefulInProgressApp, error) {
if !apputils.IsProtectedNamespace(p.manager.Spec.AppNamespace) {
var ns corev1.Namespace
err := p.client.Get(ctx, types.NamespacedName{Name: p.manager.Spec.AppNamespace}, &ns)
if err != nil && !apierrors.IsNotFound(err) {
return nil, err
}
if err == nil {
e := p.client.Delete(ctx, &ns)
if e != nil {
klog.Errorf("failed to delete ns %s, err=%v", p.manager.Spec.AppNamespace, e)
return nil, e
}
}
}
return nil, nil
}
func (p *PendingCancelFailedApp) Cancel(ctx context.Context) error {
return nil
}

View File

@@ -2,9 +2,15 @@ package appstate
import (
"context"
"fmt"
"time"
appsv1 "bytetrade.io/web3os/app-service/api/app.bytetrade.io/v1alpha1"
apputils "bytetrade.io/web3os/app-service/pkg/utils/app"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -41,6 +47,21 @@ func (p *PendingCancelingApp) Exec(ctx context.Context) (StatefulInProgressApp,
klog.Errorf("app %s operation is not ", p.manager.Name)
}
if !apputils.IsProtectedNamespace(p.manager.Spec.AppNamespace) {
var ns corev1.Namespace
err := p.client.Get(ctx, types.NamespacedName{Name: p.manager.Spec.AppNamespace}, &ns)
if err != nil && !apierrors.IsNotFound(err) {
klog.Errorf("failed to get namespace %s, %v", p.manager.Spec.AppNamespace, err)
return nil, err
}
if err == nil {
if delErr := p.client.Delete(ctx, &ns); delErr != nil && !apierrors.IsNotFound(delErr) {
klog.Errorf("failed to delete namespace %s, %v", p.manager.Spec.AppNamespace, delErr)
return nil, delErr
}
}
}
err := p.updateStatus(ctx, p.manager, appsv1.PendingCanceled, nil, appsv1.PendingCanceled.String(), "")
if err != nil {
klog.Errorf("update app manager %s to %s state failed %v", p.manager.Name, appsv1.PendingCanceled, err)
@@ -59,3 +80,58 @@ func (p *PendingCancelingApp) Cancel(ctx context.Context) error {
return nil
}
var _ PollableStatefulInProgressApp = &pendingCancelInProgressApp{}
type pendingCancelInProgressApp struct {
*PendingCancelingApp
*basePollableStatefulInProgressApp
}
func (p *pendingCancelInProgressApp) Exec(ctx context.Context) (StatefulInProgressApp, error) {
return nil, nil
}
func (p *pendingCancelInProgressApp) poll(ctx context.Context) error {
if apputils.IsProtectedNamespace(p.manager.Spec.AppNamespace) {
return nil
}
timer := time.NewTicker(time.Second)
defer timer.Stop()
for {
select {
case <-timer.C:
var ns corev1.Namespace
err := p.client.Get(ctx, types.NamespacedName{Name: p.manager.Spec.AppNamespace}, &ns)
klog.Infof("pending cancel poll namespace %s err %v", p.manager.Spec.AppNamespace, err)
if apierrors.IsNotFound(err) {
return nil
}
case <-ctx.Done():
return fmt.Errorf("app %s execute cancel operation failed %w", p.manager.Spec.AppName, ctx.Err())
}
}
}
func (p *pendingCancelInProgressApp) WaitAsync(ctx context.Context) {
appFactory.waitForPolling(ctx, p, func(err error) {
if err != nil {
updateErr := p.updateStatus(context.TODO(), p.manager, appsv1.PendingCancelFailed, nil, appsv1.PendingCancelFailed.String(), "")
if updateErr != nil {
klog.Errorf("update app manager %s to %s state failed %v", p.manager.Name, appsv1.PendingCancelFailed.String(), updateErr)
return
}
return
}
updateErr := p.updateStatus(context.TODO(), p.manager, appsv1.PendingCanceled, nil, appsv1.PendingCanceled.String(), "")
if updateErr != nil {
klog.Errorf("update app manager %s to %s state failed %v", p.manager.Name, appsv1.PendingCanceled.String(), updateErr)
return
}
})
}

View File

@@ -12,11 +12,15 @@ import (
"bytetrade.io/web3os/app-service/pkg/appinstaller"
"bytetrade.io/web3os/app-service/pkg/appinstaller/versioned"
appevent "bytetrade.io/web3os/app-service/pkg/event"
"bytetrade.io/web3os/app-service/pkg/middlewareinstaller"
"bytetrade.io/web3os/app-service/pkg/utils"
apputils "bytetrade.io/web3os/app-service/pkg/utils/app"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/rest"
"k8s.io/klog/v2"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -118,6 +122,10 @@ func (p *baseStatefulApp) forceDeleteApp(ctx context.Context) error {
klog.Errorf("get kube config failed %v", err)
return err
}
if appCfg.MiddlewareName == "mongodb" && appCfg.Namespace == "os-platform" {
return p.oldMongodbUninstall(ctx, kubeConfig)
}
ops, err := versioned.NewHelmOps(ctx, kubeConfig, appCfg, token, appinstaller.Opt{MarketSource: p.manager.GetMarketSource()})
if err != nil {
klog.Errorf("make helm ops failed %v", err)
@@ -244,3 +252,31 @@ func (p *basePollableStatefulInProgressApp) CreatePollContext() context.Context
return pollCtx
}
func (b *baseStatefulApp) oldMongodbUninstall(ctx context.Context, kubeConfig *rest.Config) error {
mc := &middlewareinstaller.MiddlewareConfig{
MiddlewareName: b.manager.Spec.AppName,
Namespace: b.manager.Spec.AppNamespace,
OwnerName: b.manager.Spec.AppOwner,
}
err := middlewareinstaller.Uninstall(ctx, kubeConfig, mc)
if err != nil && err.Error() != "failed to delete release: mongodb" {
klog.Errorf("failed to uninstall old mongodb %v", err)
return err
}
var secret corev1.Secret
err = b.client.Get(ctx, types.NamespacedName{Name: "sh.helm.release.v1.mongodb.v1", Namespace: mc.Namespace}, &secret)
if apierrors.IsNotFound(err) {
return nil
}
if err != nil {
return err
}
if err = b.client.Delete(ctx, &secret); err != nil && !apierrors.IsNotFound(err) {
klog.Errorf("failed to delete mongodb release secret: %s", secret.Name)
return err
}
return nil
}

View File

@@ -65,7 +65,7 @@ func (p *UninstallingApp) Exec(ctx context.Context) (StatefulInProgressApp, erro
err := p.exec(c)
if err != nil {
p.finally = func() {
klog.Infof("uninstalling app %s failed,", p.manager.Spec.AppName)
klog.Infof("uninstalling app %s failed %v", p.manager.Spec.AppName, err)
opRecord := makeRecord(p.manager, appsv1.UninstallFailed, fmt.Sprintf(constants.OperationFailedTpl, p.manager.Spec.OpType, err.Error()))
updateErr := p.updateStatus(context.TODO(), p.manager, appsv1.UninstallFailed, opRecord, err.Error(), "")
if updateErr != nil {
@@ -178,6 +178,10 @@ func (p *UninstallingApp) exec(ctx context.Context) error {
klog.Errorf("get kube config failed %v", err)
return err
}
if appCfg.MiddlewareName == "mongodb" && appCfg.Namespace == "os-platform" {
klog.Infof("delete old mongodb ..........")
return p.oldMongodbUninstall(ctx, kubeConfig)
}
ops, err := versioned.NewHelmOps(ctx, kubeConfig, appCfg, token, appinstaller.Opt{MarketSource: p.manager.GetMarketSource()})
if err != nil {
klog.Errorf("make helm ops failed %v", err)

View File

@@ -235,6 +235,14 @@ func (p *UpgradingApp) exec(ctx context.Context) error {
}
values["nodes"] = nodeInfo
deviceName, err := utils.GetDeviceName()
if err != nil {
klog.Errorf("failed to get deviceName %v", err)
return err
}
values["deviceName"] = deviceName
refs, err := p.getRefsForImageManager(appConfig, values)
if err != nil {
klog.Errorf("get image refs from resources failed %v", err)

View File

@@ -130,9 +130,6 @@ const (
var (
empty = sets.Empty{}
// States represents the state for whole application lifecycle.
States = sets.String{"pending": empty, "downloading": empty, "installing": empty, "initializing": empty, "running": empty,
"uninstalling": empty, "upgrading": empty, "suspend": empty, "resuming": empty}
// Sources represents the source of the application.
Sources = sets.String{"market": empty, "custom": empty, "devbox": empty, "system": empty, "unknown": empty}
// ResourceTypes represents the type of application system supported.
@@ -152,6 +149,47 @@ var (
OLARES_APP_NAME = "olares-app"
)
type ResourceConditionType string
const (
DiskPressure ResourceConditionType = "DiskPressure"
SystemCPUPressure ResourceConditionType = "SystemCPUPressure"
SystemMemoryPressure ResourceConditionType = "SystemMemoryPressure"
SystemGPUNotAvailable ResourceConditionType = "SystemGPUNotAvailable"
SystemGPUPressure ResourceConditionType = "SystemGPUPressure"
K8sRequestCPUPressure ResourceConditionType = "K8sReqeustCPUPressure"
K8sRequestMemoryPressure ResourceConditionType = "K8sRequestMemoryPressure"
UserCPUPressure ResourceConditionType = "UserCPUPressure"
UserMemoryPressure ResourceConditionType = "UserMemoryPressure"
DiskPressureMessage string = "Insufficient disk space. Unable to %s the application. Please stop other running applications to free up storage."
SystemCPUPressureMessage string = "Insufficient system CPU. Unable to %s the application. Please stop other running applications to free up resources."
SystemMemoryPressureMessage string = "Insufficient system memory. Unable to %s the application. Please stop other running applications to free up memory."
SystemGPUNotAvailableMessage string = "No available GPU found. Unable to %s the application."
SystemGPUPressureMessage string = "Available GPU is insufficient to %s this application. The requested GPU memory cannot exceed the maximum GPU memory of the node."
K8sRequestCPUPressureMessage string = "Available CPU is insufficient to %s this application. Please stop other applications to free up resources."
K8sRequestMemoryPressureMessage string = "Available memory is insufficient to %s this application. Please stop other applications to free up resources."
UserCPUPressureMessage string = "Insufficient user CPU. Unable to %s the application. Please stop other running applications to free up resources."
UserMemoryPressureMessage string = "Insufficient user memory. Unable to %s the application. Please stop other running applications to free up memory."
)
func (rct ResourceConditionType) String() string {
return string(rct)
}
type ResourceType string
const (
Disk ResourceType = "disk"
CPU ResourceType = "cpu"
Memory ResourceType = "memory"
GPU ResourceType = "gpu"
)
func (rt ResourceType) String() string {
return string(rt)
}
func init() {
flag.StringVar(&APIServerListenAddress, "listen", ":6755",
"app-service listening address")

View File

@@ -199,7 +199,7 @@ func (imc *ImageManagerClient) PollDownloadProgress(ctx context.Context, am *app
}
err = imc.updateProgress(ctx, am, &lastProgress, ret*100, am.Spec.OpType == appv1alpha1.UpgradeOp)
if err == nil && im.Status.State == "completed" {
if err == nil {
return nil
}

View File

@@ -242,6 +242,11 @@ func updateProgress(statuses []StatusInfo, ongoing *jobs, seen map[string]int64,
}
for _, status := range statuses {
klog.Infof("status: %s,ref: %v, offset: %v, Total: %v", status.Status, status.Ref, status.Offset, status.Total)
if !isLayerType(status.Ref) {
statusesLen--
continue
}
if status.Status == "exists" {
key := strings.Split(status.Ref, "-")[1]
offset += seen[key]
@@ -249,10 +254,6 @@ func updateProgress(statuses []StatusInfo, ongoing *jobs, seen map[string]int64,
continue
}
if !isLayerType(status.Ref) {
statusesLen--
continue
}
if status.Status == "done" {
offset += status.Total
doneLayer++
@@ -261,7 +262,7 @@ func updateProgress(statuses []StatusInfo, ongoing *jobs, seen map[string]int64,
offset += status.Offset
}
if doneLayer == statusesLen && doneLayer != 0 {
if doneLayer == statusesLen && statusesLen > 0 {
offset = imageSize
}
if imageSize != 0 {

View File

@@ -49,11 +49,14 @@ func Uninstall(ctx context.Context, kubeConfig *rest.Config, middleware *Middlew
return err
}
if installed, err := helmClient.IsInstalled(middleware.MiddlewareName); err != nil {
installed, err := helmClient.IsInstalled(middleware.MiddlewareName)
if err != nil {
klog.Errorf("Failed to get install history middlewareName=%s err=%v", middleware.MiddlewareName, err)
return err
} else if !installed {
return errors.New("middleware not installed")
}
if !installed {
klog.Infof("middleware %s is not installed", middleware.MiddlewareName)
return nil
}
err = helmClient.Uninstall(middleware.MiddlewareName)

View File

@@ -207,39 +207,41 @@ func CheckUserRole(appConfig *appcfg.ApplicationConfig, owner string) error {
}
// CheckAppRequirement check if the cluster has enough resources for application install/upgrade.
func CheckAppRequirement(token string, appConfig *appcfg.ApplicationConfig) (string, error) {
func CheckAppRequirement(token string, appConfig *appcfg.ApplicationConfig, op v1alpha1.OpType) (constants.ResourceType, constants.ResourceConditionType, error) {
metrics, _, err := GetClusterResource(token)
if err != nil {
return "", err
return "", "", err
}
klog.Infof("start to %s app %s", op, appConfig.AppName)
klog.Infof("Current resource=%s", utils.PrettyJSON(metrics))
klog.Infof("App required resource=%s", utils.PrettyJSON(appConfig.Requirement))
if appConfig.Requirement.Disk != nil &&
appConfig.Requirement.Disk.CmpInt64(int64(metrics.Disk.Total*0.9-metrics.Disk.Usage)) > 0 ||
int64(metrics.Disk.Total*0.9-metrics.Disk.Usage) < 5*1024*1024*1024 {
return "disk", errors.New("The app's DISK requirement cannot be satisfied")
return constants.Disk, constants.DiskPressure, fmt.Errorf(constants.DiskPressureMessage, op)
}
if appConfig.Requirement.Memory != nil &&
appConfig.Requirement.Memory.CmpInt64(int64(metrics.Memory.Total*0.9-metrics.Memory.Usage)) > 0 {
return "memory", errors.New("The app's MEMORY requirement cannot be satisfied")
return constants.Memory, constants.SystemMemoryPressure, fmt.Errorf(constants.SystemMemoryPressureMessage, op)
}
if appConfig.Requirement.CPU != nil {
availableCPU, _ := resource.ParseQuantity(strconv.FormatFloat(metrics.CPU.Total*0.9-metrics.CPU.Usage, 'f', -1, 64))
if appConfig.Requirement.CPU.Cmp(availableCPU) > 0 {
return "cpu", errors.New("The app's CPU requirement cannot be satisfied")
return constants.CPU, constants.SystemCPUPressure, fmt.Errorf(constants.SystemCPUPressureMessage, op)
}
}
if appConfig.Requirement.GPU != nil {
if !appConfig.Requirement.GPU.IsZero() && metrics.GPU.Total <= 0 {
return "gpu", errors.New("The app's GPU requirement cannot be satisfied")
return constants.GPU, constants.SystemGPUNotAvailable, fmt.Errorf(constants.SystemGPUNotAvailableMessage, op)
}
nodes, err := utils.GetNodeInfo(context.TODO())
if err != nil {
klog.Errorf("failed to get node info %v", err)
return "", err
return "", "", err
}
klog.Infof("nodes info: %#v", nodes)
var maxNodeGPUMem int64
@@ -254,13 +256,13 @@ func CheckAppRequirement(token string, appConfig *appcfg.ApplicationConfig) (str
}
if appConfig.Requirement.GPU.CmpInt64(maxNodeGPUMem) > 0 {
return "gpu", errors.New("The app's GPU requirement cannot found satisfied node")
return constants.GPU, constants.SystemGPUPressure, fmt.Errorf(constants.SystemGPUPressureMessage, op)
}
}
allocatedResources, err := getRequestResources()
if err != nil {
return "", err
return "", "", err
}
if len(allocatedResources) == 1 {
sufficientCPU, sufficientMemory := false, false
@@ -283,14 +285,14 @@ func CheckAppRequirement(token string, appConfig *appcfg.ApplicationConfig) (str
}
}
if !sufficientCPU {
return "cpu", errors.New("The app's CPU requirement specified in the kubernetes requests cannot be satisfied")
return constants.CPU, constants.K8sRequestCPUPressure, fmt.Errorf(constants.K8sRequestCPUPressureMessage, op)
}
if !sufficientMemory {
return "memory", errors.New("The app's MEMORY requirement specified in the kubernetes requests cannot be satisfied")
return constants.Memory, constants.K8sRequestMemoryPressure, fmt.Errorf(constants.K8sRequestMemoryPressureMessage, op)
}
}
return "", nil
return "", "", nil
}
func getRequestResources() (map[string]resources, error) {
@@ -450,22 +452,22 @@ func getValue(m *kubesphere.Metric) float64 {
}
// CheckUserResRequirement check if the user has enough resources for application install/upgrade.
func CheckUserResRequirement(ctx context.Context, appConfig *appcfg.ApplicationConfig, username string) (string, error) {
metrics, err := prometheus.GetCurUserResource(ctx, username)
func CheckUserResRequirement(ctx context.Context, appConfig *appcfg.ApplicationConfig, op v1alpha1.OpType) (constants.ResourceType, constants.ResourceConditionType, error) {
metrics, err := prometheus.GetCurUserResource(ctx, appConfig.OwnerName)
if err != nil {
return "", err
return "", "", err
}
switch {
case appConfig.Requirement.Memory != nil && metrics.Memory.Total != 0 &&
appConfig.Requirement.Memory.CmpInt64(int64(metrics.Memory.Total*0.9-metrics.Memory.Usage)) > 0:
return "memory", errors.New("The user's app MEMORY requirement cannot be satisfied")
return constants.Memory, constants.UserMemoryPressure, fmt.Errorf(constants.UserMemoryPressureMessage, op)
case appConfig.Requirement.CPU != nil && metrics.CPU.Total != 0:
availableCPU, _ := resource.ParseQuantity(strconv.FormatFloat(metrics.CPU.Total*0.9-metrics.CPU.Usage, 'f', -1, 64))
if appConfig.Requirement.CPU.Cmp(availableCPU) > 0 {
return "cpu", errors.New("The user's app CPU requirement cannot be satisfied")
return constants.CPU, constants.UserCPUPressure, fmt.Errorf(constants.UserCPUPressureMessage, op)
}
}
return "", nil
return "", "", nil
}
func CheckMiddlewareRequirement(ctx context.Context, ctrlClient client.Client, middleware *tapr.Middleware) (bool, error) {

View File

@@ -4,8 +4,11 @@ import (
"context"
"errors"
"fmt"
"github.com/go-resty/resty/v2"
"net"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
@@ -399,3 +402,69 @@ func GetNodeInfo(ctx context.Context) (ret []api.NodeInfo, err error) {
}
return
}
type SystemStatusResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data struct {
TerminusdState string `json:"terminusdState"`
TerminusState string `json:"terminusState"`
TerminusName string `json:"terminusName"`
TerminusVersion string `json:"terminusVersion"`
InstalledTime int64 `json:"installedTime"`
InitializedTime int64 `json:"initializedTime"`
OlaresdVersion string `json:"olaresdVersion"`
DeviceName string `json:"device_name"`
HostName string `json:"host_name"`
OsType string `json:"os_type"`
OsArch string `json:"os_arch"`
OsInfo string `json:"os_info"`
OsVersion string `json:"os_version"`
CpuInfo string `json:"cpu_info"`
GpuInfo string `json:"gpu_info"`
Memory string `json:"memory"`
Disk string `json:"disk"`
WifiConnected bool `json:"wifiConnected"`
WiredConnected bool `json:"wiredConnected"`
HostIp string `json:"hostIp"`
ExternalIp string `json:"externalIp"`
InstallingState string `json:"installingState"`
InstallingProgress string `json:"installingProgress"`
UninstallingState string `json:"uninstallingState"`
UninstallingProgress string `json:"uninstallingProgress"`
UpgradingTarget string `json:"upgradingTarget"`
UpgradingRetryNum int `json:"upgradingRetryNum"`
UpgradingState string `json:"upgradingState"`
UpgradingStep string `json:"upgradingStep"`
UpgradingProgress string `json:"upgradingProgress"`
UpgradingError string `json:"upgradingError"`
UpgradingDownloadState string `json:"upgradingDownloadState"`
UpgradingDownloadStep string `json:"upgradingDownloadStep"`
UpgradingDownloadProgress string `json:"upgradingDownloadProgress"`
UpgradingDownloadError string `json:"upgradingDownloadError"`
CollectingLogsState string `json:"collectingLogsState"`
CollectingLogsError string `json:"collectingLogsError"`
DefaultFrpServer string `json:"defaultFrpServer"`
FrpEnable string `json:"frpEnable"`
} `json:"data"`
}
func GetDeviceName() (string, error) {
url := fmt.Sprintf("http://%s/system/status", os.Getenv("OLARESD_HOST"))
var result SystemStatusResponse
client := resty.New()
resp, err := client.R().SetResult(&result).Get(url)
if err != nil {
klog.Errorf("failed to send request to olaresd %v", err)
return "", err
}
if resp.StatusCode() != http.StatusOK {
klog.Errorf("failed to get system status from olaresd %v", err)
return "", errors.New(string(resp.Body()))
}
if result.Code != http.StatusOK {
return "", fmt.Errorf("not exepcted result code: %v,message: %v", result.Code, result.Message)
}
klog.Infof("getDeviceName: %#v", result.Data)
return result.Data.DeviceName, nil
}

View File

@@ -29,7 +29,7 @@ spec:
name: check-auth
containers:
- name: auth-front
image: beclab/login:v1.6.24
image: beclab/login:v1.6.26
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80

View File

@@ -1,6 +1,6 @@
{{ $backupVersion := "0.3.59" }}
{{ $backupVersion := "0.3.60" }}
{{ $backup_server_rootpath := printf "%s%s" .Values.rootPath "/rootfs/backup-server" }}
{{- $backup_nats_secret := (lookup "v1" "Secret" .Release.Namespace "backup-nats-secret") -}}

View File

@@ -16,7 +16,7 @@ replace (
k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8
k8s.io/kubectl => k8s.io/kubectl v0.24.2
k8s.io/utils => k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
olares.com/backups-sdk => github.com/Above-Os/backups-sdk v0.1.37
olares.com/backups-sdk => github.com/Above-Os/backups-sdk v0.1.38
sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.12.2
sigs.k8s.io/json => sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2
sigs.k8s.io/kustomize/api => sigs.k8s.io/kustomize/api v0.11.4

View File

@@ -31,8 +31,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Above-Os/backups-sdk v0.1.37 h1:7LofG3HRTgGLv0YNjoCFdzSjIhF7PwsYxbmpT2KQkUs=
github.com/Above-Os/backups-sdk v0.1.37/go.mod h1:55igUShkfGaJZrWJ0z/jlkgkYniZEN/Qoav6njwyvbs=
github.com/Above-Os/backups-sdk v0.1.38 h1:GUq/sV7Tra0mDCgm9KXfVqsJV8m5rQtIpf7Zx7FFZVg=
github.com/Above-Os/backups-sdk v0.1.38/go.mod h1:55igUShkfGaJZrWJ0z/jlkgkYniZEN/Qoav6njwyvbs=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=

View File

@@ -6,6 +6,5 @@ const (
)
var (
FreeLimit float64 = 85.00
ConnectErrors = []string{"dial tcp", "connect:"}
ConnectErrors = []string{"dial tcp", "connect:"}
)

View File

@@ -25,6 +25,7 @@ import (
"olares.com/backup-server/pkg/util/pointer"
"olares.com/backup-server/pkg/watchers/notification"
backupssdk "olares.com/backups-sdk"
backupssdkconstants "olares.com/backups-sdk/pkg/constants"
backupssdkoptions "olares.com/backups-sdk/pkg/options"
backupssdkrestic "olares.com/backups-sdk/pkg/restic"
backupssdkstorage "olares.com/backups-sdk/pkg/storage"
@@ -322,12 +323,7 @@ func (s *StorageBackup) checkDiskSize() error {
return err
}
log.Infof("Backup %s,%s, check disk free space: %s, path: %s", backupName, snapshotId, usage.String(), target)
if usage.UsedPercent > FreeLimit {
log.Errorf("Backup %s,%s, disk usage has reached %.2f%%", backupName, snapshotId, FreeLimit)
return fmt.Errorf("Disk usage has reached %.2f%%. Please clean up disk space first.", FreeLimit)
}
log.Infof("Backup %s,%s, check disk free space: %s, path: %s, limit: %d", backupName, snapshotId, usage.String(), target, backupssdkconstants.FreeSpaceLimit)
backupSize, err := util.DirSize(s.Params.Path)
if err != nil {
@@ -335,8 +331,8 @@ func (s *StorageBackup) checkDiskSize() error {
return fmt.Errorf("get backup disk size error: %v, path: %s", err, s.Params.Path)
}
requiredSpace := uint64(float64(backupSize) * 1.05)
if usage.Free < requiredSpace {
requiredSpace := backupSize
if usage.Free < (requiredSpace + backupssdkconstants.FreeSpaceLimit) {
log.Errorf("not enough free space on target disk, required: %s, available: %s, location: %s", util.FormatBytes(requiredSpace), util.FormatBytes(usage.Free), s.Params.LocationInFileSystem)
return fmt.Errorf("Insufficient space on the target disk.")
}

View File

@@ -22,6 +22,7 @@ import (
"olares.com/backup-server/pkg/util/pointer"
"olares.com/backup-server/pkg/watchers/notification"
backupssdk "olares.com/backups-sdk"
backupssdkconstants "olares.com/backups-sdk/pkg/constants"
backupssdkoptions "olares.com/backups-sdk/pkg/options"
backupssdkrestic "olares.com/backups-sdk/pkg/restic"
@@ -292,15 +293,10 @@ func (s *StorageRestore) checkDiskSize() error {
return err
}
log.Infof("Restore %s, check disk free space: %s, path: %s", s.RestoreId, usage.String(), s.Params.RootPath)
log.Infof("Restore %s, check disk free space: %s, path: %s, limit: %d", s.RestoreId, usage.String(), s.Params.RootPath, backupssdkconstants.FreeSpaceLimit)
if usage.UsedPercent > FreeLimit {
log.Errorf("Restore %s, disk usage has reached %.2f%%", s.RestoreId, FreeLimit)
return fmt.Errorf("Disk usage has reached %.2f%%. Please clean up disk space first.", FreeLimit)
}
requiredSpace := uint64(float64(s.RestoreType.TotalBytesProcessed) * 1.05)
if usage.Free < requiredSpace {
requiredSpace := uint64(s.RestoreType.TotalBytesProcessed)
if usage.Free < (requiredSpace + backupssdkconstants.FreeSpaceLimit) {
log.Errorf("not enough free space on target disk, required: %s, available: %s, location: %s", util.FormatBytes(requiredSpace), util.FormatBytes(usage.Free), s.Params.RootPath)
return fmt.Errorf("Insufficient space on the target disk.")
}

View File

@@ -210,7 +210,7 @@ spec:
command:
- /samba_share
- name: files
image: beclab/files-server:v0.2.138
image: beclab/files-server:v0.2.140
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: true

View File

@@ -140,7 +140,7 @@ spec:
name: check-chart-repo
containers:
- name: appstore-backend
image: beclab/market-backend:v0.6.8
image: beclab/market-backend:v0.6.9
imagePullPolicy: IfNotPresent
ports:
- containerPort: 81

View File

@@ -226,7 +226,7 @@ spec:
spec:
initContainers:
- name: seahub-init
image: beclab/seahub-init:v0.0.3
image: beclab/seahub-init:v0.0.4
env:
- name: DB_HOST
value: citus-headless.os-platform
@@ -248,7 +248,7 @@ spec:
containers:
- name: seafile-server
image: beclab/pg_seafile_server:v0.0.15
image: beclab/pg_seafile_server:v0.0.16
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8082

View File

@@ -26,6 +26,8 @@ metadata:
rules:
- nonResourceURLs:
- "/document/*"
- "/document?*"
- "/document"
verbs: ["*"]
---

View File

@@ -240,7 +240,7 @@ spec:
value: os_framework_search3
containers:
- name: search3
image: beclab/search3:v0.0.93
image: beclab/search3:v0.0.95
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
@@ -301,7 +301,7 @@ spec:
priorityClassName: "system-cluster-critical"
containers:
- name: search3monitor
image: beclab/search3monitor:v0.0.93
image: beclab/search3monitor:v0.0.95
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8081

View File

@@ -64,7 +64,7 @@ spec:
operator: Exists
containers:
- name: search3-validation
image: beclab/search3validation:v0.0.92
image: beclab/search3validation:v0.0.95
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8443

View File

@@ -4,9 +4,9 @@ output:
binaries:
-
id: cuda-driver
name: cuda-driver-580.95.05.run
amd64: https://us.download.nvidia.com/XFree86/Linux-x86_64/580.95.05/NVIDIA-Linux-x86_64-580.95.05.run
arm64: https://us.download.nvidia.com/XFree86/aarch64/580.95.05/NVIDIA-Linux-aarch64-580.95.05.run
name: cuda-driver-590.44.01.run
amd64: https://us.download.nvidia.com/XFree86/Linux-x86_64/590.44.01/NVIDIA-Linux-x86_64-590.44.01.run
arm64: https://us.download.nvidia.com/XFree86/aarch64/590.44.01/NVIDIA-Linux-aarch64-590.44.01.run
-
id: libnvidia-gpgkey
name: libnvidia-gpgkey

View File

@@ -7,4 +7,4 @@ output:
-
name: beclab/apecloud-kubeblocks:1.0.1
-
name: beclab/kubeblock-addon-charts:v1.0.1
name: beclab/kubeblock-addon-charts:v1.0.1-ext

View File

@@ -11,7 +11,7 @@ spec:
or cluster of machines.
helm:
chartLocationURL: file:///minio-1.0.1.tgz
chartsImage: beclab/kubeblock-addon-charts:v1.0.1
chartsImage: beclab/kubeblock-addon-charts:v1.0.1-ext
chartsPathInImage: /charts
installValues: {}
valuesMapping:
@@ -44,7 +44,7 @@ spec:
and scaling.
helm:
chartLocationURL: file:///mongodb-1.0.1.tgz
chartsImage: beclab/kubeblock-addon-charts:v1.0.1
chartsImage: beclab/kubeblock-addon-charts:v1.0.1-ext
installable:
autoInstall: true
type: Helm
@@ -68,7 +68,7 @@ spec:
speed and relevance on production-scale workloads.
helm:
chartLocationURL: file:///elasticsearch-1.0.1.tgz
chartsImage: beclab/kubeblock-addon-charts:v1.0.1
chartsImage: beclab/kubeblock-addon-charts:v1.0.1-ext
installable:
autoInstall: true
type: Helm
@@ -90,7 +90,7 @@ spec:
description: RabbitMQ is a reliable and mature messaging and streaming broker.
helm:
chartLocationURL: file:///rabbitmq-1.0.1.tgz
chartsImage: beclab/kubeblock-addon-charts:v1.0.1
chartsImage: beclab/kubeblock-addon-charts:v1.0.1-ext
installable:
autoInstall: true
type: Helm
@@ -113,7 +113,7 @@ spec:
system that is widely used for web and application servers
helm:
chartLocationURL: file:///mariadb-1.0.1.tgz
chartsImage: beclab/kubeblock-addon-charts:v1.0.1
chartsImage: beclab/kubeblock-addon-charts:v1.0.1-ext
installable:
autoInstall: true
type: Helm
@@ -136,7 +136,7 @@ spec:
system (RDBMS)
helm:
chartLocationURL: file:///mysql-1.0.1.tgz
chartsImage: beclab/kubeblock-addon-charts:v1.0.1
chartsImage: beclab/kubeblock-addon-charts:v1.0.1-ext
installable:
autoInstall: true
type: Helm