Compare commits
28 Commits
fix/docume
...
appservice
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a822538fc3 | ||
|
|
c5610cbcb7 | ||
|
|
47e82908c4 | ||
|
|
4b79a7aefe | ||
|
|
dfd74239dd | ||
|
|
5a08f918c6 | ||
|
|
8fde456f74 | ||
|
|
b173f005cd | ||
|
|
ecf8849b55 | ||
|
|
c8f416c4c0 | ||
|
|
ed183b8e4e | ||
|
|
20595b72c7 | ||
|
|
04c9e8309b | ||
|
|
3cd388d83a | ||
|
|
8266fc6085 | ||
|
|
78fb8bcdca | ||
|
|
cdb7afafef | ||
|
|
c4e1c74538 | ||
|
|
07b9470e4e | ||
|
|
da11265189 | ||
|
|
f6d1addc7d | ||
|
|
3b644efa0a | ||
|
|
27d8463775 | ||
|
|
aa9b2aa243 | ||
|
|
1c4257065f | ||
|
|
40c0491925 | ||
|
|
a7f2d9c583 | ||
|
|
a72c760b07 |
2
.github/workflows/check.yaml
vendored
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
2
.github/workflows/release-daily.yaml
vendored
@@ -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:
|
||||
|
||||
@@ -53,6 +53,7 @@ rules:
|
||||
- "/seahub/api/*"
|
||||
- "/system/configuration/encoding"
|
||||
- "/api/search/get_directory/"
|
||||
- "/api/search/sync_search/"
|
||||
verbs: ["*"]
|
||||
|
||||
---
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ const (
|
||||
DefaultK3sVersion = "v1.33.3-k3s"
|
||||
DefaultKubernetesVersion = ""
|
||||
DefaultKubeSphereVersion = "v3.3.0"
|
||||
CurrentVerifiedCudaVersion = "13.0"
|
||||
CurrentVerifiedCudaVersion = "13.1"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
@@ -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{})
|
||||
}
|
||||
102
cli/pkg/upgrade/1_12_3_20251217.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
235
docs/manual/get-started/local-access.md
Normal 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.
|
||||
|
||||

|
||||
</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**.
|
||||
|
||||

|
||||
</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/) (zero‑configuration networking), which can resolve multi‑label 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
|
||||
```
|
||||

|
||||
|
||||
## 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
|
||||

|
||||
|
||||
2. Tap on the device card.
|
||||

|
||||
|
||||
3. Scroll down to the **Network** section. You can find the **Intranet IP** there.
|
||||

|
||||
|
||||
</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.
|
||||

|
||||
|
||||
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.
|
||||
{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.
|
||||

|
||||
|
||||
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.
|
||||
@@ -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.
|
||||
|
||||

|
||||
|
||||
:::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.
|
||||
@@ -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.
|
||||
{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.
|
||||
:::
|
||||
|
||||

|
||||
<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.
|
||||

|
||||
</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.
|
||||
:::
|
||||

|
||||
</template>
|
||||
</tabs>
|
||||
|
||||
## Understand connection status
|
||||
LarePass displays the connection status between your device and Olares, helping you understand or diagnose your current network connection.
|
||||
|
||||
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 278 KiB |
BIN
docs/public/images/manual/get-started/larepass-device-card.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
docs/public/images/manual/get-started/larepass-network.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
docs/public/images/manual/get-started/larepass-system.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
docs/public/images/manual/get-started/larepass-vpn-desktop.png
Normal file
|
After Width: | Height: | Size: 134 KiB |
BIN
docs/public/images/manual/get-started/larepass-vpn-mobile.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 890 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 58 KiB |
BIN
docs/public/images/zh/manual/get-started/larepass-network.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
docs/public/images/zh/manual/get-started/larepass-system.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 89 KiB |
BIN
docs/public/images/zh/manual/get-started/larepass-vpn-mobile.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
@@ -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 访问应用时,无需身份验证。
|
||||
|
||||
## 端点
|
||||
|
||||
|
||||
235
docs/zh/manual/get-started/local-access.md
Normal 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 开关。
|
||||
|
||||

|
||||
</template>
|
||||
<template #使用-LarePass-桌面端>
|
||||
|
||||
1. 打开 LarePass 应用,点击左上角的头像打开用户菜单。
|
||||
2. 打开**专用网络连接**开关。
|
||||
|
||||

|
||||
</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
|
||||
```
|
||||

|
||||
|
||||
## 方法 3:配置本地 DNS
|
||||
为了获得无缝体验(即标准 URL 自动解析为你的本地 IP 地址),你可以配置网络 DNS。此配置确保网络上所有设备的访问一致,无需单独设置客户端。
|
||||
|
||||
### 查找 Olares 设备的内网 IP
|
||||
要配置 DNS,首先需要找到 Olares 设备的内网 IP。
|
||||
<tabs>
|
||||
<template #通过-LarePass-手机端查看>
|
||||
|
||||
如果你的手机和 Olares 设备处于同一网络:
|
||||
1. 打开 LarePass 应用,点击**设置** > **系统**,进入 **Olares 管理**页面。
|
||||

|
||||
|
||||
2. 点击设备卡片。
|
||||

|
||||
|
||||
3. 并向下滚动到**网络**部分。可以在此处找到**内网 IP**。
|
||||

|
||||
|
||||
</template>
|
||||
<template #通过-Olares-终端查看>
|
||||
|
||||
控制面板提供了内置终端,允许你直接从浏览器运行系统命令,无需外部 SSH 客户端。
|
||||
1. 打开控制面板,在左侧导航栏的**终端**下选择 **Olares**。
|
||||

|
||||
|
||||
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,并开启开关。
|
||||
{width=400}
|
||||
|
||||
4. 重启 Chrome 并再次尝试访问本地 URL。
|
||||
|
||||
### 为什么在 Chrome 上使用 `.local` 域名时,应用无法在 iFrame 中加载 (macOS) ?
|
||||
使用本地域名时,Chrome 可能会默认使用 HTTPS,你可能会看到“连接不安全”的警告。
|
||||

|
||||
|
||||
要解决此问题,在 URL 开头显式添加 HTTP 协议头 (`http://`),告诉浏览器这是一个仅在本地网络中使用的链接。
|
||||
@@ -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. 点击**完成**。
|
||||
如果排列正确,即表示你已成功备份助记词。
|
||||
|
||||

|
||||
|
||||
## 常见问题
|
||||
@@ -49,8 +51,4 @@ description: 学习备份 Olares 账号助记词的正确方法,确保账号
|
||||
* **多设备备份**:使用 LarePass 的保险库功能加密保存助记词在多个设备上。只有当所有这些设备都丢失时,你才会失去助记词。
|
||||
|
||||
### 我已经激活了 Olares,为什么在 LarePass 中查看助记词时提示密码错误?
|
||||
如果遇到密码错误,可能是因为你还没有设置本地密码。打开 LarePass 应用,进入**设置** > **LarePass 设置** > **安全**,设置本地密码后再尝试备份。
|
||||
|
||||
## 恭喜!
|
||||
现在你已经准备好进一步探索 Olares:
|
||||
- [接下来做什么](../get-started/next-steps.md)
|
||||
如果遇到密码错误,可能是因为你还没有设置本地密码。打开 LarePass 应用,进入**设置** > **LarePass 设置** > **安全**,设置本地密码后再尝试备份。
|
||||
@@ -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 并开启旁边的开关。
|
||||
{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 配置文件。允许此操作以完成设置。
|
||||
:::
|
||||
|
||||

|
||||
<tabs>
|
||||
<template #使用-LarePass-移动端>
|
||||
|
||||
### LarePass 移动端
|
||||
1. 打开 LarePass 应用,进入**设置**。
|
||||
2. 在**我的 Olares** 卡片中,打开 VPN 开关。
|
||||
|
||||
1. 打开 LarePass,依次进入**设置** > **我的 Olares**。
|
||||
2. 打开 **VPN** 开关。
|
||||

|
||||
</template>
|
||||
<template #使用-LarePass-桌面端>
|
||||
|
||||
### LarePass 桌面端
|
||||
1. 打开 LarePass 应用,点击左上角的头像打开用户菜单。
|
||||
2. 打开**专用网络连接**开关。
|
||||
|
||||
1. 打开 LarePass,点击主界面左上角头像区域。
|
||||
2. 在弹出面板中打开**专用网络连接**开关。
|
||||
|
||||
启用专用网络后,无论使用 LarePass 客户端还是浏览器,设备都会通过专用网络访问 Olares。
|
||||
|
||||
::: info
|
||||
在 iOS 或 macOS 上首次开启专用网络时,系统会提示添加专用网络配置文件,请按指引完成。
|
||||
:::
|
||||

|
||||
</template>
|
||||
</tabs>
|
||||
|
||||
## 了解连接状态
|
||||
|
||||
LarePass 会展示设备到 Olares 的连接状态,帮助你判断或诊断网络情况。
|
||||
|
||||

|
||||
@@ -91,11 +49,10 @@ LarePass 会展示设备到 Olares 的连接状态,帮助你判断或诊断网
|
||||
| Offline mode | 当前离线,无法连接到 Olares |
|
||||
|
||||
::: info
|
||||
若在外网环境使用专用网络访问私有入口时,连接状态显示 **DERP**,说明专用网络无法直接通过 P2P 连接 Olares,需要借助 Tailscale 中继服务器,这可能影响连接质量。如长期如此,请联系 Olares 支持。
|
||||
若在外网环境使用专用网络访问私有入口时,连接状态显示 "DERP",说明专用网络无法直接通过 P2P 连接 Olares,需要借助 Tailscale 中继服务器,这可能影响连接质量。如长期如此,请联系 Olares 支持。
|
||||
:::
|
||||
|
||||
## 故障排查
|
||||
|
||||
出现连接问题时,LarePass 会显示诊断信息。常见提示及处理办法如下:
|
||||
|
||||

|
||||
|
||||
@@ -64,7 +64,7 @@ Vault 项目是存储敏感信息的独立安全容器。每个 Vault 项目包
|
||||

|
||||
|
||||
:::tip 提示
|
||||
如果你不知道助记词短语的位置,请参阅[备份助记词短语](../../larepass/back-up-mnemonics)。
|
||||
如果你不知道助记词短语的位置,请参阅[备份助记词短语](../../larepass/back-up-mnemonics.md)。
|
||||
:::
|
||||
|
||||
## 管理 Vault 项目
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ type ApplicationConfig struct {
|
||||
APIVersion APIVersion
|
||||
CfgFileVersion string
|
||||
Namespace string
|
||||
MiddlewareName string
|
||||
ChartsName string
|
||||
RepoURL string
|
||||
Title string
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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") -}}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -6,6 +6,5 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
FreeLimit float64 = 85.00
|
||||
ConnectErrors = []string{"dial tcp", "connect:"}
|
||||
ConnectErrors = []string{"dial tcp", "connect:"}
|
||||
)
|
||||
|
||||
@@ -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.")
|
||||
}
|
||||
|
||||
@@ -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.")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -26,6 +26,8 @@ metadata:
|
||||
rules:
|
||||
- nonResourceURLs:
|
||||
- "/document/*"
|
||||
- "/document?*"
|
||||
- "/document"
|
||||
verbs: ["*"]
|
||||
|
||||
---
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||