Compare commits
51 Commits
daemon/fix
...
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 | ||
|
|
5bf89ace05 | ||
|
|
fe9120edb4 | ||
|
|
2a77fe4c8a | ||
|
|
85a5e2dc4b | ||
|
|
cd88ade7ca | ||
|
|
0c3cedc1a5 | ||
|
|
4ed649bff7 | ||
|
|
e383c22fe5 | ||
|
|
ce15e2ce00 | ||
|
|
957dff10a6 | ||
|
|
da35df9280 | ||
|
|
14edf88acb | ||
|
|
939a9b5ba3 | ||
|
|
aa9b2aa243 | ||
|
|
3bd0705742 | ||
|
|
6662923b87 | ||
|
|
f39fec6c68 | ||
|
|
e1362a43f7 | ||
|
|
a7c611571f | ||
|
|
f0f2d4798c | ||
|
|
9d6fd7a276 | ||
|
|
02e45a7fb3 | ||
|
|
1c4257065f | ||
|
|
40c0491925 | ||
|
|
a7f2d9c583 | ||
|
|
a72c760b07 | ||
|
|
6ec7f214cb | ||
|
|
543328fa6e |
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
|
||||
|
||||
29
.github/workflows/module_kubesphere_build_main.yaml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: Kubesphere Build Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "module-kubesphere"
|
||||
paths:
|
||||
- 'infrastructure/kubesphere/**'
|
||||
- '!infrastructure/kubesphere/.olares/**'
|
||||
- '!infrastructure/kubesphere/README.md'
|
||||
pull_request:
|
||||
branches:
|
||||
- "module-kubesphere"
|
||||
paths:
|
||||
- 'infrastructure/kubesphere/**'
|
||||
- '!infrastructure/kubesphere/.olares/**'
|
||||
- '!infrastructure/kubesphere/README.md'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.24'
|
||||
- run: make binary
|
||||
working-directory: infrastructure/kubesphere
|
||||
36
.github/workflows/module_kubesphere_publish_docker.yaml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: Publish Kubesphere to Dockerhub
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tags:
|
||||
description: 'Release Tags'
|
||||
|
||||
jobs:
|
||||
publish_dockerhub:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
push: true
|
||||
tags: beclab/ks-apiserver:${{ github.event.inputs.tags }}
|
||||
file: infrastructure/kubesphere/build/ks-apiserver/Dockerfile
|
||||
context: infrastructure/kubesphere
|
||||
platforms: linux/amd64,linux/arm64
|
||||
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: ["*"]
|
||||
|
||||
---
|
||||
|
||||
@@ -239,7 +239,6 @@ metadata:
|
||||
applications.app.bytetrade.io/icon: https://app.cdn.olares.com/appstore/olaresapps/icon.png
|
||||
applications.app.bytetrade.io/title: 'Olares Apps'
|
||||
applications.app.bytetrade.io/version: '0.0.1'
|
||||
applications.app.bytetrade.io/policies: '{"policies":[{"entranceName":"dashboard","uriRegex":"/js/script.js", "level":"public"},{"entranceName":"dashboard","uriRegex":"/js/api/send", "level":"public"}]}'
|
||||
applications.app.bytetrade.io/entrances: '[{"name":"files", "host":"files-fe-service", "port":80,"title":"Files","windowPushState":true,"icon":"https://app.cdn.olares.com/appstore/files/icon.png"},{"name":"share","authLevel":"public", "host":"share-fe-service", "port":80,"title":"Share","windowPushState":true,"icon":"https://app.cdn.olares.com/appstore/files/icon.png","invisible":true},{"name":"vault", "host":"vault-service", "port":80,"title":"Vault","windowPushState":true,"icon":"https://app.cdn.olares.com/appstore/vault/icon.png"},{"name":"market", "host":"appstore-fe-service", "port":80,"title":"Market","windowPushState":true,"icon":"https://app.cdn.olares.com/appstore/appstore/icon.png"},{"name":"settings", "host":"settings-service", "port":80,"title":"Settings","icon":"https://app.cdn.olares.com/appstore/settings/icon.png"},{"name":"profile", "host":"profile-service", "port":80,"title":"Profile","windowPushState":true,"icon":"https://app.cdn.olares.com/appstore/profile/icon.png"},{"name":"dashboard","host":"dashboard-service","port":80,"title":"Dashboard","windowPushState":true,"icon":"https://app.cdn.olares.com/appstore/dashboard/icon.png"},{"name":"control-hub","host":"control-hub-service","port":80,"title":"Control Hub","windowPushState":true,"icon":"https://app.cdn.olares.com/appstore/control-hub/icon.png"},{"name":"headscale", "host":"headscale-svc", "port":80,"title":"Headscale","invisible": true,"icon":"https://app.cdn.olares.com/appstore/headscale/icon.png"}]'
|
||||
spec:
|
||||
replicas: 1
|
||||
@@ -318,7 +317,7 @@ spec:
|
||||
chown -R 1000:1000 /uploadstemp && \
|
||||
chown -R 1000:1000 /appdata
|
||||
- name: olares-app-init
|
||||
image: beclab/system-frontend:v1.6.21
|
||||
image: beclab/system-frontend:v1.6.27
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- /bin/sh
|
||||
@@ -440,7 +439,7 @@ spec:
|
||||
- name: NATS_SUBJECT_VAULT
|
||||
value: os.vault.{{ .Values.bfl.username}}
|
||||
- name: user-service
|
||||
image: beclab/user-service:v0.0.76
|
||||
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
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ for mod in "${PACKAGE_MODULE[@]}";do
|
||||
chart_path="${mod}/${app}"
|
||||
|
||||
if [ -d $chart_path ]; then
|
||||
find $chart_path -type f -name *.yaml | while read p; do
|
||||
find $chart_path -type f -path '*/.olares/*.yaml' | while read p; do
|
||||
bash ${BASE_DIR}/yaml2prop.sh -f $p | while read l;do
|
||||
if [[ "$l" == *".image = "* || "$l" == "output.containers."*".name"* ]]; then
|
||||
echo "$l"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -100,18 +100,9 @@ func (c *ConfigSystemModule) Init() {
|
||||
Retry: 0,
|
||||
}
|
||||
|
||||
configProxyTask := &task.RemoteTask{
|
||||
Name: "ConfigProxy",
|
||||
Hosts: c.Runtime.GetAllHosts(),
|
||||
Action: new(ConfigProxyTask),
|
||||
Parallel: false,
|
||||
Retry: 0,
|
||||
}
|
||||
|
||||
c.Tasks = []task.Interface{
|
||||
updateNtpDateTask,
|
||||
timeSyncTask,
|
||||
configProxyTask,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -204,24 +204,6 @@ exit 0`, setNTPCommand, hwclockCmd)
|
||||
return nil
|
||||
}
|
||||
|
||||
type ConfigProxyTask struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (t *ConfigProxyTask) Execute(runtime connector.Runtime) error {
|
||||
if common.ResolvProxy == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var cmd = fmt.Sprintf("echo nameserver %s > /etc/resolv.conf", common.ResolvProxy)
|
||||
if _, err := runtime.GetRunner().SudoCmd(cmd, false, true); err != nil {
|
||||
logger.Errorf("failed to execute %s: %v", cmd, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type NodeConfigureOS struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
@@ -275,9 +275,17 @@ func (t *DisableLocalDNSTask) configResolvConf(runtime connector.Runtime) error
|
||||
overrideOp := ">"
|
||||
appendOp := ">>"
|
||||
|
||||
if common.CloudVendor == common.CloudVendorAliYun {
|
||||
var internalDNS string
|
||||
if util.IsOnAliyunECS() {
|
||||
internalDNS = "100.100.10.12"
|
||||
} else if util.IsOnAWSEC2() {
|
||||
internalDNS = "169.254.169.253"
|
||||
} else if util.IsOnTencentCVM() {
|
||||
internalDNS = "183.60.83.19"
|
||||
}
|
||||
if internalDNS != "" {
|
||||
secondNameserverOp = appendOp
|
||||
cmd = `echo 'nameserver 100.100.2.136' > /etc/resolv.conf`
|
||||
cmd = fmt.Sprintf("echo 'nameserver %s' > /etc/resolv.conf", internalDNS)
|
||||
if _, err = runtime.GetRunner().SudoCmd(cmd, false, true); err != nil {
|
||||
logger.Errorf("exec %s error %v", cmd, err)
|
||||
return err
|
||||
|
||||
@@ -25,8 +25,7 @@ const (
|
||||
DefaultK3sVersion = "v1.33.3-k3s"
|
||||
DefaultKubernetesVersion = ""
|
||||
DefaultKubeSphereVersion = "v3.3.0"
|
||||
DefaultTokenMaxAge = 31536000
|
||||
CurrentVerifiedCudaVersion = "13.0"
|
||||
CurrentVerifiedCudaVersion = "13.1"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -170,21 +169,11 @@ const (
|
||||
ManagedMinIO = "managed-minio"
|
||||
)
|
||||
|
||||
var (
|
||||
CloudVendor = os.Getenv("CLOUD_VENDOR")
|
||||
ResolvProxy = os.Getenv("PROXY")
|
||||
)
|
||||
|
||||
const (
|
||||
OlaresRegistryMirrorHost = "mirrors.joinolares.cn"
|
||||
OlaresRegistryMirrorHostLegacy = "mirrors.jointerminus.cn"
|
||||
)
|
||||
|
||||
const (
|
||||
CloudVendorAliYun = "aliyun"
|
||||
CloudVendorAWS = "aws"
|
||||
)
|
||||
|
||||
const (
|
||||
RaspbianCmdlineFile = "/boot/cmdline.txt"
|
||||
RaspbianFirmwareFile = "/boot/firmware/cmdline.txt"
|
||||
@@ -236,7 +225,6 @@ const (
|
||||
CacheNodeNum = "node_num"
|
||||
CacheRedisPassword = "redis_password"
|
||||
CacheSecretsNum = "secrets_num"
|
||||
CacheJwtSecret = "jwt_secret"
|
||||
CacheCrdsNUm = "users_iam_num"
|
||||
|
||||
CacheMinioPath = "minio_binary_path"
|
||||
@@ -295,7 +283,6 @@ const (
|
||||
ENV_BACKUP_SECRET = "BACKUP_SECRET"
|
||||
ENV_CLUSTER_ID = "CLUSTER_ID"
|
||||
ENV_BACKUP_CLUSTER_BUCKET = "BACKUP_CLUSTER_BUCKET"
|
||||
ENV_TOKEN_MAX_AGE = "TOKEN_MAX_AGE"
|
||||
ENV_HOST_IP = "HOST_IP"
|
||||
ENV_PREINSTALL = "PREINSTALL"
|
||||
ENV_DISABLE_HOST_IP_PROMPT = "DISABLE_HOST_IP_PROMPT"
|
||||
|
||||
@@ -101,7 +101,6 @@ type Argument struct {
|
||||
Storage *Storage `json:"storage"`
|
||||
NetworkSettings *NetworkSettings `json:"network_settings"`
|
||||
GPU *GPU `json:"gpu"`
|
||||
TokenMaxAge int64 `json:"token_max_age"` // nanosecond
|
||||
|
||||
Request any `json:"-"`
|
||||
|
||||
@@ -353,15 +352,6 @@ func (a *Argument) SetOlaresCDNService(url string) {
|
||||
a.OlaresCDNService = u
|
||||
}
|
||||
|
||||
func (a *Argument) SetTokenMaxAge() {
|
||||
s := os.Getenv(ENV_TOKEN_MAX_AGE)
|
||||
age, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil || age == 0 {
|
||||
age = DefaultTokenMaxAge
|
||||
}
|
||||
a.TokenMaxAge = age
|
||||
}
|
||||
|
||||
func (a *Argument) SetGPU(enable bool) {
|
||||
if a.GPU == nil {
|
||||
a.GPU = new(GPU)
|
||||
|
||||
@@ -6,9 +6,9 @@ const (
|
||||
NamespaceKubePublic = "kube-public"
|
||||
NamespaceKubeSystem = "kube-system"
|
||||
NamespaceKubekeySystem = "kubekey-system"
|
||||
NamespaceKubesphereControlsSystem = "kubesphere-controls-system"
|
||||
NamespaceKubesphereMonitoringSystem = "kubesphere-monitoring-system"
|
||||
NamespaceKubesphereSystem = "kubesphere-system"
|
||||
NamespaceKubesphereControlsSystem = "kubesphere-controls-system"
|
||||
NamespaceOsFramework = "os-framework"
|
||||
NamespaceOsPlatform = "os-platform"
|
||||
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/core/connector"
|
||||
"github.com/beclab/Olares/cli/pkg/core/prepare"
|
||||
"github.com/beclab/Olares/cli/pkg/core/util"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Skip struct {
|
||||
KubePrepare
|
||||
Not bool
|
||||
}
|
||||
|
||||
func (p *Skip) PreCheck(runtime connector.Runtime) (bool, error) {
|
||||
return !p.Not, nil
|
||||
}
|
||||
|
||||
type Stop struct {
|
||||
prepare.BasePrepare
|
||||
}
|
||||
|
||||
func (p *Stop) PreCheck(runtime connector.Runtime) (bool, error) {
|
||||
return true, nil
|
||||
// return false, fmt.Errorf("STOP !!!!!!")
|
||||
}
|
||||
|
||||
type GetCommandKubectl struct {
|
||||
prepare.BasePrepare
|
||||
}
|
||||
|
||||
func (p *GetCommandKubectl) PreCheck(runtime connector.Runtime) (bool, error) {
|
||||
|
||||
cmd, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("command -v %s", CommandKubectl), false, false)
|
||||
if err != nil {
|
||||
return true, nil
|
||||
}
|
||||
if cmd != "" {
|
||||
p.PipelineCache.Set(CacheKubectlKey, cmd)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
type GetMasterNum struct {
|
||||
prepare.BasePrepare
|
||||
}
|
||||
|
||||
func (p *GetMasterNum) PreCheck(runtime connector.Runtime) (bool, error) {
|
||||
var kubectlpath, err = util.GetCommand(CommandKubectl)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("kubectl not found")
|
||||
}
|
||||
|
||||
var cmd = fmt.Sprintf("%s get node | awk '{if(NR>1){print $3}}' | grep master | wc -l", kubectlpath)
|
||||
stdout, err := runtime.GetRunner().SudoCmd(cmd, false, false)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(errors.WithStack(err), "get master num failed")
|
||||
}
|
||||
|
||||
masterNum, _ := strconv.ParseInt(stdout, 10, 64)
|
||||
|
||||
p.PipelineCache.Set(CacheMasterNum, masterNum)
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
type GetNodeNum struct {
|
||||
prepare.BasePrepare
|
||||
}
|
||||
|
||||
func (p *GetNodeNum) PreCheck(runtime connector.Runtime) (bool, error) {
|
||||
var kubectlpath, err = util.GetCommand(CommandKubectl)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("kubectl not found")
|
||||
}
|
||||
|
||||
var cmd = fmt.Sprintf("%s get node | wc -l", kubectlpath)
|
||||
stdout, err := runtime.GetRunner().SudoCmd(cmd, false, false)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(errors.WithStack(err), "get node num failed")
|
||||
}
|
||||
|
||||
nodeNum, _ := strconv.ParseInt(stdout, 10, 64)
|
||||
|
||||
p.PipelineCache.Set(CacheNodeNum, nodeNum)
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
type ClusterType struct {
|
||||
KubePrepare
|
||||
ClusterType string
|
||||
Not bool
|
||||
}
|
||||
|
||||
func (p *ClusterType) PreCheck(runtime connector.Runtime) (bool, error) {
|
||||
if p.KubeConf == nil || p.KubeConf.Cluster == nil {
|
||||
return false, nil
|
||||
}
|
||||
var isK3s = p.KubeConf.Cluster.Kubernetes.Type == p.ClusterType
|
||||
if p.Not {
|
||||
return !isK3s, nil
|
||||
}
|
||||
|
||||
return isK3s, nil
|
||||
}
|
||||
@@ -331,3 +331,46 @@ func GetPublicIPFromTencentIMDS() (net.IP, error) {
|
||||
logger.Debugf("retrieved public IP info from Tencent metadata service: %s", string(body))
|
||||
return net.ParseIP(strings.TrimSpace(string(body))), nil
|
||||
}
|
||||
|
||||
func GetPublicIPFromAliyunIMDS() (net.IP, error) {
|
||||
token, err := GetTokenFromAliyunIMDS()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get Aliyun ECS IMDS token: %v", err)
|
||||
}
|
||||
url := "http://100.100.100.200/latest/meta-data/public-ipv4"
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build http request: %v", err)
|
||||
}
|
||||
req.Header.Set("X-aliyun-ecs-metadata-token", token)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to reach Aliyun metadata service")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to read response from Aliyun metadata service")
|
||||
}
|
||||
logger.Debugf("retrieved public IP info from Aliyun metadata service: %s", string(body))
|
||||
return net.ParseIP(strings.TrimSpace(string(body))), nil
|
||||
}
|
||||
|
||||
func GetTokenFromAliyunIMDS() (string, error) {
|
||||
url := "http://100.100.100.200/latest/api/token"
|
||||
req, err := http.NewRequest(http.MethodPut, url, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to build http request: %v", err)
|
||||
}
|
||||
req.Header.Set("X-aliyun-ecs-metadata-token-ttl-seconds", "600")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to reach Aliyun metadata service")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to read response from Aliyun metadata service")
|
||||
}
|
||||
return strings.TrimSpace(string(body)), nil
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var header = JWTHeader{
|
||||
Alg: "HS256",
|
||||
Typ: "JWT",
|
||||
}
|
||||
|
||||
var payload = JWTPayload{
|
||||
Email: "admin@kubesphere.io",
|
||||
Username: "admin",
|
||||
TokenType: "static_token",
|
||||
}
|
||||
|
||||
type JWTHeader struct {
|
||||
Alg string `json:"alg"`
|
||||
Typ string `json:"typ"`
|
||||
}
|
||||
|
||||
type JWTPayload struct {
|
||||
Email string `json:"email"`
|
||||
Username string `json:"username"`
|
||||
TokenType string `json:"token_type"`
|
||||
}
|
||||
|
||||
func EncryptToken(secret string) (string, error) {
|
||||
headerJson, _ := json.Marshal(header)
|
||||
headerBase64 := Base64URLEncode(headerJson)
|
||||
|
||||
payloadJson, _ := json.Marshal(payload)
|
||||
payloadBase64 := Base64URLEncode(payloadJson)
|
||||
|
||||
headerPayload := fmt.Sprintf("%s.%s", headerBase64, payloadBase64)
|
||||
|
||||
var secretBytes = []byte(secret)
|
||||
|
||||
signature := HMACSHA256([]byte(headerPayload), secretBytes)
|
||||
|
||||
// Encode the signature to base64 URL encoding.
|
||||
signatureBase64 := Base64URLEncode(signature)
|
||||
|
||||
return fmt.Sprintf("%s.%s", headerPayload, signatureBase64), nil
|
||||
}
|
||||
|
||||
func Base64URLEncode(data []byte) string {
|
||||
return strings.TrimRight(base64.URLEncoding.EncodeToString(data), "=")
|
||||
|
||||
}
|
||||
|
||||
// HMACSHA256 signs a message using a secret key with HMAC SHA256.
|
||||
func HMACSHA256(message, secret []byte) []byte {
|
||||
h := hmac.New(sha256.New, secret)
|
||||
h.Write(message)
|
||||
return h.Sum(nil)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestToken(t *testing.T) {
|
||||
var a = "n7X2dggXApH91fnVUzgPr1Fr1vAO0Upo"
|
||||
// var b = `{"email": "admin@kubesphere.io","username": "admin","token_type": "static_token"}`
|
||||
|
||||
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFkbWluQGt1YmVzcGhlcmUuaW8iLCJ1c2VybmFtZSI6ImFkbWluIiwidG9rZW5fdHlwZSI6InN0YXRpY190b2tlbiJ9.iwsRH37tcqE8HyI_S98AEM6KUH7bVdxDasR3V8QasXI
|
||||
var data, _ = EncryptToken(a)
|
||||
|
||||
fmt.Println("---data---", data)
|
||||
}
|
||||
@@ -264,3 +264,41 @@ func IsOnTencentCVM() bool {
|
||||
defer resp.Body.Close()
|
||||
return resp.StatusCode == http.StatusOK
|
||||
}
|
||||
|
||||
func IsOnAliyunECS() bool {
|
||||
vendorFiles := []string{
|
||||
"/sys/class/dmi/id/sys_vendor",
|
||||
"/sys/class/dmi/id/board_vendor",
|
||||
"/sys/class/dmi/id/bios_vendor",
|
||||
"/sys/class/dmi/id/product_name",
|
||||
}
|
||||
for _, p := range vendorFiles {
|
||||
if b, err := os.ReadFile(p); err == nil {
|
||||
s := strings.ToLower(strings.TrimSpace(string(b)))
|
||||
if strings.Contains(s, "alibaba") || strings.Contains(s, "aliyun") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if IsExist("/etc/aliyun-release") {
|
||||
return true
|
||||
}
|
||||
|
||||
reqCtx, cancel := context.WithTimeout(context.Background(), 400*time.Millisecond)
|
||||
defer cancel()
|
||||
req, _ := http.NewRequestWithContext(reqCtx, http.MethodGet, "http://100.100.100.200/latest/meta-data/instance-id", nil)
|
||||
|
||||
tr := &http.Transport{
|
||||
Proxy: nil,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 250 * time.Millisecond,
|
||||
}).DialContext,
|
||||
}
|
||||
resp, err := (&http.Client{Transport: tr}).Do(req)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return resp.StatusCode == http.StatusOK
|
||||
}
|
||||
|
||||
@@ -74,7 +74,6 @@ func (g *GenerateTerminusdServiceEnv) Execute(runtime connector.Runtime) error {
|
||||
"RegistryMirrors": g.KubeConf.Arg.RegistryMirrors,
|
||||
"BaseDir": baseDir,
|
||||
"GpuEnable": utils.FormatBoolToInt(g.KubeConf.Arg.GPU.Enable),
|
||||
"TokenMaxAge": g.KubeConf.Arg.TokenMaxAge,
|
||||
},
|
||||
PrintContent: true,
|
||||
}
|
||||
|
||||
@@ -14,5 +14,4 @@ KUBE_TYPE={{ .KubeType }}
|
||||
REGISTRY_MIRRORS={{ .RegistryMirrors }}
|
||||
BASE_DIR={{ .BaseDir }}
|
||||
LOCAL_GPU_ENABLE={{ .GpuEnable }}
|
||||
TOKEN_MAX_AGE={{ .TokenMaxAge }}
|
||||
`)))
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -3,13 +3,14 @@ package kubesphere
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/beclab/Olares/cli/pkg/storage"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/storage"
|
||||
|
||||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/pelletier/go-toml"
|
||||
|
||||
@@ -470,6 +471,7 @@ func (t *InitMinikubeNs) Execute(runtime connector.Runtime) error {
|
||||
common.NamespaceKubekeySystem,
|
||||
common.NamespaceKubesphereSystem,
|
||||
common.NamespaceKubesphereMonitoringSystem,
|
||||
common.NamespaceKubesphereControlsSystem,
|
||||
}
|
||||
|
||||
for _, ns := range allNs {
|
||||
|
||||
@@ -17,12 +17,9 @@
|
||||
package kubesphere
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/core/action"
|
||||
"github.com/beclab/Olares/cli/pkg/core/logger"
|
||||
"github.com/beclab/Olares/cli/pkg/version/kubesphere/templates"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
"github.com/beclab/Olares/cli/pkg/core/prepare"
|
||||
@@ -61,81 +58,19 @@ func (d *DeployModule) Init() {
|
||||
d.Name = "DeployKubeSphereModule"
|
||||
d.Desc = "Deploy KubeSphere"
|
||||
|
||||
generateManifests := &task.RemoteTask{
|
||||
Name: "GenerateKsInstallerCRD",
|
||||
Desc: "Generate KubeSphere ks-installer crd manifests",
|
||||
Hosts: d.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: &action.Template{
|
||||
Name: "GenerateKsInstallerCRD",
|
||||
Template: templates.KsInstaller,
|
||||
Dst: filepath.Join(common.KubeAddonsDir, templates.KsInstaller.Name()),
|
||||
},
|
||||
Parallel: false,
|
||||
}
|
||||
|
||||
addConfig := &task.RemoteTask{
|
||||
Name: "AddKsInstallerConfig",
|
||||
Desc: "Add config to ks-installer manifests",
|
||||
Hosts: d.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: new(AddInstallerConfig),
|
||||
Parallel: false,
|
||||
}
|
||||
|
||||
createNamespace := &task.RemoteTask{
|
||||
Name: "CreateKubeSphereNamespace",
|
||||
Desc: "Create the kubesphere namespace",
|
||||
Hosts: d.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: new(CreateNamespace),
|
||||
Parallel: false,
|
||||
}
|
||||
|
||||
setup := &task.RemoteTask{
|
||||
Name: "SetupKsInstallerConfig",
|
||||
Desc: "Setup ks-installer config",
|
||||
Hosts: d.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: new(Setup), // todo
|
||||
Parallel: false,
|
||||
Retry: 1,
|
||||
}
|
||||
|
||||
apply := &task.RemoteTask{
|
||||
Name: "ApplyKsInstaller",
|
||||
Desc: "Apply ks-installer",
|
||||
Hosts: d.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: new(Apply),
|
||||
Parallel: false,
|
||||
Retry: 10,
|
||||
Delay: 5 * time.Second,
|
||||
}
|
||||
|
||||
d.Tasks = []task.Interface{
|
||||
generateManifests,
|
||||
// apply crd installer.kubesphere.io/v1alpha1
|
||||
// apply,
|
||||
addConfig,
|
||||
createNamespace,
|
||||
setup,
|
||||
apply,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +92,6 @@ func (c *CheckResultModule) Init() {
|
||||
Hosts: c.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: new(Check),
|
||||
Parallel: false,
|
||||
@@ -170,7 +104,6 @@ func (c *CheckResultModule) Init() {
|
||||
Hosts: c.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: new(GetKubeCommand),
|
||||
Parallel: false,
|
||||
|
||||
@@ -1,243 +0,0 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: system:kubesphere-router-clusterrole
|
||||
annotations:
|
||||
kubernetes.io/created-by: kubesphere.io/ks-router
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
- endpoints
|
||||
- nodes
|
||||
- pods
|
||||
- secrets
|
||||
- namespaces
|
||||
verbs:
|
||||
- list
|
||||
- watch
|
||||
- get
|
||||
- update
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- nodes
|
||||
verbs:
|
||||
- get
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- services
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- "extensions"
|
||||
resources:
|
||||
- ingresses
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- "networking.k8s.io"
|
||||
resources:
|
||||
- ingresses
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
- apiGroups:
|
||||
- "extensions"
|
||||
resources:
|
||||
- ingresses/status
|
||||
verbs:
|
||||
- update
|
||||
- apiGroups:
|
||||
- "networking.k8s.io"
|
||||
resources:
|
||||
- ingresses/status
|
||||
verbs:
|
||||
- update
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: system:kubesphere-router-role
|
||||
namespace: kubesphere-controls-system
|
||||
annotations:
|
||||
kubernetes.io/created-by: kubesphere.io/ks-router
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
- pods
|
||||
- secrets
|
||||
- namespaces
|
||||
verbs:
|
||||
- get
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
resourceNames:
|
||||
# Defaults to "<election-id>-<ingress-class>"
|
||||
# Here: "<ingress-controller-leader>-<nginx>"
|
||||
# This has to be adapted if you change either parameter
|
||||
# when launching the nginx-ingress-controller.
|
||||
- "ingress-controller-leader-nginx"
|
||||
verbs:
|
||||
- get
|
||||
- update
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- create
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- endpoints
|
||||
verbs:
|
||||
- get
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: kubesphere-router-serviceaccount
|
||||
namespace: kubesphere-controls-system
|
||||
annotations:
|
||||
kubernetes.io/created-by: kubesphere.io/ks-router
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: system:nginx-ingress-clusterrole-nisa-binding
|
||||
annotations:
|
||||
kubernetes.io/created-by: kubesphere.io/ks-router
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:kubesphere-router-clusterrole
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kubesphere-router-serviceaccount
|
||||
namespace: kubesphere-controls-system
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: nginx-ingress-role-nisa-binding
|
||||
namespace: kubesphere-controls-system
|
||||
annotations:
|
||||
kubernetes.io/created-by: kubesphere.io/ks-router
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: system:kubesphere-router-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kubesphere-router-serviceaccount
|
||||
namespace: kubesphere-controls-system
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: default-http-backend
|
||||
namespace: kubesphere-controls-system
|
||||
labels:
|
||||
app: kubesphere
|
||||
component: kubesphere-router
|
||||
version: express-1.0.alpha
|
||||
annotations:
|
||||
kubernetes.io/created-by: kubesphere.io/ks-router
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: kubesphere
|
||||
component: kubesphere-router
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: kubesphere
|
||||
component: kubesphere-router
|
||||
spec:
|
||||
terminationGracePeriodSeconds: 60
|
||||
containers:
|
||||
- name: default-http-backend
|
||||
# Any image is permissible as long as:
|
||||
# 1. It serves a 404 page at /
|
||||
# 2. It serves 200 on a /healthz endpoint
|
||||
image: {{ .Values.image.defaultbackend_repo }}:{{ .Values.image.defaultbackend_tag | default "latest" }}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8080
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 30
|
||||
timeoutSeconds: 5
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
resources:
|
||||
limits:
|
||||
cpu: 10m
|
||||
memory: 20Mi
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 20Mi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: default-http-backend
|
||||
namespace: kubesphere-controls-system
|
||||
labels:
|
||||
app: kubesphere
|
||||
component: kubesphere-router
|
||||
annotations:
|
||||
kubernetes.io/created-by: kubesphere.io/ks-router
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8080
|
||||
selector:
|
||||
app: kubesphere
|
||||
component: kubesphere-router
|
||||
|
||||
---
|
||||
# create a seviceaccount for kubectl pod
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: kubesphere-cluster-admin
|
||||
namespace: kubesphere-controls-system
|
||||
annotations:
|
||||
kubernetes.io/created-by: kubesphere.io/kubectl
|
||||
---
|
||||
# bind kubesphere-cluster-admin sa to clusterrole cluster-admin
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: system:kubesphere-cluster-admin
|
||||
annotations:
|
||||
kubernetes.io/created-by: kubesphere.io/kubectl
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: cluster-admin
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kubesphere-cluster-admin
|
||||
namespace: kubesphere-controls-system
|
||||
@@ -1,466 +0,0 @@
|
||||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.6.1
|
||||
creationTimestamp: null
|
||||
name: clusterdashboards.monitoring.kubesphere.io
|
||||
spec:
|
||||
group: monitoring.kubesphere.io
|
||||
names:
|
||||
kind: ClusterDashboard
|
||||
listKind: ClusterDashboardList
|
||||
plural: clusterdashboards
|
||||
singular: clusterdashboard
|
||||
scope: Cluster
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: ClusterDashboard is the Schema for the culsterdashboards API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: DashboardSpec defines the desired state of Dashboard
|
||||
properties:
|
||||
datasource:
|
||||
description: Dashboard datasource
|
||||
type: string
|
||||
description:
|
||||
description: Dashboard description
|
||||
type: string
|
||||
panels:
|
||||
description: Collection of panels. Panel is one of [Row](row.md),
|
||||
[Singlestat](#singlestat.md) or [Graph](graph.md)
|
||||
items:
|
||||
description: Supported panel
|
||||
properties:
|
||||
bars:
|
||||
description: A collection of queries Targets []Target `json:"targets,omitempty"`
|
||||
Display as a bar chart
|
||||
type: boolean
|
||||
colors:
|
||||
description: Set series color
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
decimals:
|
||||
description: Name of the signlestat panel Title string `json:"title,omitempty"`
|
||||
Must be `singlestat` Type string `json:"type"` Panel ID Id
|
||||
int64 `json:"id,omitempty"` A collection of queries Targets
|
||||
[]Target `json:"targets,omitempty"` Limit the decimal numbers
|
||||
format: int64
|
||||
type: integer
|
||||
description:
|
||||
description: Name of the graph panel Title string `json:"title,omitempty"`
|
||||
Must be `graph` Type string `json:"type"` Panel ID Id int64
|
||||
`json:"id,omitempty"` Panel description
|
||||
type: string
|
||||
format:
|
||||
description: Display unit
|
||||
type: string
|
||||
id:
|
||||
description: Panel ID
|
||||
format: int64
|
||||
type: integer
|
||||
lines:
|
||||
description: Display as a line chart
|
||||
type: boolean
|
||||
stack:
|
||||
description: Display as a stacked chart
|
||||
type: boolean
|
||||
targets:
|
||||
description: A collection of queries Only for panels with `graph`
|
||||
or `singlestat` type
|
||||
items:
|
||||
description: Query editor options
|
||||
properties:
|
||||
expr:
|
||||
description: Input for fetching metrics.
|
||||
type: string
|
||||
legendFormat:
|
||||
description: Legend format for outputs. You can make a
|
||||
dynamic legend with templating variables.
|
||||
type: string
|
||||
refId:
|
||||
description: Reference ID
|
||||
format: int64
|
||||
type: integer
|
||||
step:
|
||||
description: Set series time interval
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
title:
|
||||
description: Name of the panel
|
||||
type: string
|
||||
type:
|
||||
description: Panel Type, one of `row`, `graph`, `singlestat`
|
||||
type: string
|
||||
yaxes:
|
||||
description: Y-axis options
|
||||
items:
|
||||
properties:
|
||||
decimals:
|
||||
description: Limit the decimal numbers
|
||||
format: int64
|
||||
type: integer
|
||||
format:
|
||||
description: Display unit
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
templating:
|
||||
description: Templating variables
|
||||
items:
|
||||
description: Templating defines a variable, which can be used as
|
||||
a placeholder in query
|
||||
properties:
|
||||
name:
|
||||
description: Variable name
|
||||
type: string
|
||||
query:
|
||||
description: Set variable values to be the return result of
|
||||
the query
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
time:
|
||||
description: Time range for display
|
||||
properties:
|
||||
from:
|
||||
description: Start time in the format of `^now([+-][0-9]+[smhdwMy])?$`,
|
||||
eg. `now-1M`. It denotes the end time is set to the last month
|
||||
since now.
|
||||
type: string
|
||||
to:
|
||||
description: End time in the format of `^now([+-][0-9]+[smhdwMy])?$`,
|
||||
eg. `now-1M`. It denotes the start time is set to the last month
|
||||
since now.
|
||||
type: string
|
||||
type: object
|
||||
title:
|
||||
description: Dashboard title
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: false
|
||||
- name: v1alpha2
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: ClusterDashboard is the Schema for the culsterdashboards API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: DashboardSpec defines the desired state of Dashboard
|
||||
properties:
|
||||
annotations:
|
||||
description: Annotations
|
||||
items:
|
||||
properties:
|
||||
datasource:
|
||||
type: string
|
||||
enable:
|
||||
type: boolean
|
||||
expr:
|
||||
type: string
|
||||
iconColor:
|
||||
type: string
|
||||
iconSize:
|
||||
type: integer
|
||||
lineColor:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
query:
|
||||
type: string
|
||||
showLine:
|
||||
type: boolean
|
||||
step:
|
||||
type: string
|
||||
tagKeys:
|
||||
type: string
|
||||
tags:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
tagsField:
|
||||
type: string
|
||||
textField:
|
||||
type: string
|
||||
textFormat:
|
||||
type: string
|
||||
titleFormat:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
auto_refresh:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
editable:
|
||||
type: boolean
|
||||
id:
|
||||
type: integer
|
||||
panels:
|
||||
items:
|
||||
properties:
|
||||
bars:
|
||||
description: Display as a bar chart
|
||||
type: boolean
|
||||
colors:
|
||||
description: Set series color
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
content:
|
||||
type: string
|
||||
datasource:
|
||||
description: Datasource
|
||||
type: string
|
||||
decimals:
|
||||
format: int64
|
||||
type: integer
|
||||
description:
|
||||
description: Description
|
||||
type: string
|
||||
format:
|
||||
description: Display unit
|
||||
type: string
|
||||
gauge:
|
||||
description: gauge
|
||||
properties:
|
||||
maxValue:
|
||||
format: int64
|
||||
type: integer
|
||||
minValue:
|
||||
format: int64
|
||||
type: integer
|
||||
show:
|
||||
type: boolean
|
||||
thresholdLabels:
|
||||
type: boolean
|
||||
thresholdMarkers:
|
||||
type: boolean
|
||||
type: object
|
||||
height:
|
||||
description: Height
|
||||
type: string
|
||||
id:
|
||||
description: Panel ID
|
||||
format: int64
|
||||
type: integer
|
||||
legend:
|
||||
description: legend
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
lines:
|
||||
description: Display as a line chart
|
||||
type: boolean
|
||||
mode:
|
||||
type: string
|
||||
options:
|
||||
properties:
|
||||
colorMode:
|
||||
type: string
|
||||
content:
|
||||
type: string
|
||||
displayMode:
|
||||
type: string
|
||||
graphMode:
|
||||
type: string
|
||||
justifyMode:
|
||||
type: string
|
||||
mode:
|
||||
type: string
|
||||
orientation:
|
||||
type: string
|
||||
textMode:
|
||||
type: string
|
||||
type: object
|
||||
scroll:
|
||||
type: boolean
|
||||
sort:
|
||||
properties:
|
||||
col:
|
||||
type: integer
|
||||
desc:
|
||||
type: boolean
|
||||
type: object
|
||||
sparkline:
|
||||
description: 'spark line: full or bottom'
|
||||
type: string
|
||||
stack:
|
||||
description: Display as a stacked chart
|
||||
type: boolean
|
||||
targets:
|
||||
description: A collection of queries
|
||||
items:
|
||||
description: Query editor options Referers to https://pkg.go.dev/github.com/grafana-tools/sdk#Target
|
||||
properties:
|
||||
expr:
|
||||
description: 'only support prometheus,and the corresponding
|
||||
fields are as follows: Input for fetching metrics.'
|
||||
type: string
|
||||
legendFormat:
|
||||
description: Legend format for outputs. You can make a
|
||||
dynamic legend with templating variables.
|
||||
type: string
|
||||
refId:
|
||||
description: Reference ID
|
||||
format: int64
|
||||
type: integer
|
||||
step:
|
||||
description: Set series time interval
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
title:
|
||||
description: Name of the panel
|
||||
type: string
|
||||
type:
|
||||
description: Type of the panel
|
||||
type: string
|
||||
valueName:
|
||||
description: value name
|
||||
type: string
|
||||
xaxis:
|
||||
properties:
|
||||
decimals:
|
||||
description: Limit the decimal numbers
|
||||
format: int64
|
||||
type: integer
|
||||
format:
|
||||
description: Display unit
|
||||
type: string
|
||||
type: object
|
||||
yaxes:
|
||||
description: Y-axis options
|
||||
items:
|
||||
properties:
|
||||
decimals:
|
||||
description: Limit the decimal numbers
|
||||
format: int64
|
||||
type: integer
|
||||
format:
|
||||
description: Display unit
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
type: array
|
||||
shared_crosshair:
|
||||
type: boolean
|
||||
tags:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
templatings:
|
||||
description: // Templating variables
|
||||
items:
|
||||
properties:
|
||||
allFormat:
|
||||
type: string
|
||||
allValue:
|
||||
type: string
|
||||
auto:
|
||||
type: boolean
|
||||
auto_count:
|
||||
type: integer
|
||||
datasource:
|
||||
type: string
|
||||
hide:
|
||||
type: integer
|
||||
includeAll:
|
||||
type: boolean
|
||||
label:
|
||||
type: string
|
||||
multi:
|
||||
type: boolean
|
||||
multiFormat:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
options:
|
||||
items:
|
||||
properties:
|
||||
selected:
|
||||
type: boolean
|
||||
text:
|
||||
type: string
|
||||
value:
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
query:
|
||||
type: string
|
||||
regex:
|
||||
type: string
|
||||
sort:
|
||||
type: integer
|
||||
type:
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
time:
|
||||
description: Time range
|
||||
properties:
|
||||
from:
|
||||
description: Start time in the format of `^now([+-][0-9]+[smhdwMy])?$`,
|
||||
eg. `now-1M`. It denotes the end time is set to the last month
|
||||
since now.
|
||||
type: string
|
||||
to:
|
||||
description: End time in the format of `^now([+-][0-9]+[smhdwMy])?$`,
|
||||
eg. `now-1M`. It denotes the start time is set to the last month
|
||||
since now.
|
||||
type: string
|
||||
type: object
|
||||
timezone:
|
||||
type: string
|
||||
title:
|
||||
type: string
|
||||
uid:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
@@ -1,470 +0,0 @@
|
||||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.6.1
|
||||
creationTimestamp: null
|
||||
name: dashboards.monitoring.kubesphere.io
|
||||
spec:
|
||||
group: monitoring.kubesphere.io
|
||||
names:
|
||||
kind: Dashboard
|
||||
listKind: DashboardList
|
||||
plural: dashboards
|
||||
singular: dashboard
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Dashboard is the Schema for the dashboards API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: DashboardSpec defines the desired state of Dashboard
|
||||
properties:
|
||||
datasource:
|
||||
description: Dashboard datasource
|
||||
type: string
|
||||
description:
|
||||
description: Dashboard description
|
||||
type: string
|
||||
panels:
|
||||
description: Collection of panels. Panel is one of [Row](row.md),
|
||||
[Singlestat](#singlestat.md) or [Graph](graph.md)
|
||||
items:
|
||||
description: Supported panel
|
||||
properties:
|
||||
bars:
|
||||
description: A collection of queries Targets []Target `json:"targets,omitempty"`
|
||||
Display as a bar chart
|
||||
type: boolean
|
||||
colors:
|
||||
description: Set series color
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
decimals:
|
||||
description: Name of the signlestat panel Title string `json:"title,omitempty"`
|
||||
Must be `singlestat` Type string `json:"type"` Panel ID Id
|
||||
int64 `json:"id,omitempty"` A collection of queries Targets
|
||||
[]Target `json:"targets,omitempty"` Limit the decimal numbers
|
||||
format: int64
|
||||
type: integer
|
||||
description:
|
||||
description: Name of the graph panel Title string `json:"title,omitempty"`
|
||||
Must be `graph` Type string `json:"type"` Panel ID Id int64
|
||||
`json:"id,omitempty"` Panel description
|
||||
type: string
|
||||
format:
|
||||
description: Display unit
|
||||
type: string
|
||||
id:
|
||||
description: Panel ID
|
||||
format: int64
|
||||
type: integer
|
||||
lines:
|
||||
description: Display as a line chart
|
||||
type: boolean
|
||||
stack:
|
||||
description: Display as a stacked chart
|
||||
type: boolean
|
||||
targets:
|
||||
description: A collection of queries Only for panels with `graph`
|
||||
or `singlestat` type
|
||||
items:
|
||||
description: Query editor options
|
||||
properties:
|
||||
expr:
|
||||
description: Input for fetching metrics.
|
||||
type: string
|
||||
legendFormat:
|
||||
description: Legend format for outputs. You can make a
|
||||
dynamic legend with templating variables.
|
||||
type: string
|
||||
refId:
|
||||
description: Reference ID
|
||||
format: int64
|
||||
type: integer
|
||||
step:
|
||||
description: Set series time interval
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
title:
|
||||
description: Name of the panel
|
||||
type: string
|
||||
type:
|
||||
description: Panel Type, one of `row`, `graph`, `singlestat`
|
||||
type: string
|
||||
yaxes:
|
||||
description: Y-axis options
|
||||
items:
|
||||
properties:
|
||||
decimals:
|
||||
description: Limit the decimal numbers
|
||||
format: int64
|
||||
type: integer
|
||||
format:
|
||||
description: Display unit
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
templating:
|
||||
description: Templating variables
|
||||
items:
|
||||
description: Templating defines a variable, which can be used as
|
||||
a placeholder in query
|
||||
properties:
|
||||
name:
|
||||
description: Variable name
|
||||
type: string
|
||||
query:
|
||||
description: Set variable values to be the return result of
|
||||
the query
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
time:
|
||||
description: Time range for display
|
||||
properties:
|
||||
from:
|
||||
description: Start time in the format of `^now([+-][0-9]+[smhdwMy])?$`,
|
||||
eg. `now-1M`. It denotes the end time is set to the last month
|
||||
since now.
|
||||
type: string
|
||||
to:
|
||||
description: End time in the format of `^now([+-][0-9]+[smhdwMy])?$`,
|
||||
eg. `now-1M`. It denotes the start time is set to the last month
|
||||
since now.
|
||||
type: string
|
||||
type: object
|
||||
title:
|
||||
description: Dashboard title
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: false
|
||||
subresources:
|
||||
status: {}
|
||||
- name: v1alpha2
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Dashboard is the Schema for the dashboards API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: DashboardSpec defines the desired state of Dashboard
|
||||
properties:
|
||||
annotations:
|
||||
description: Annotations
|
||||
items:
|
||||
properties:
|
||||
datasource:
|
||||
type: string
|
||||
enable:
|
||||
type: boolean
|
||||
expr:
|
||||
type: string
|
||||
iconColor:
|
||||
type: string
|
||||
iconSize:
|
||||
type: integer
|
||||
lineColor:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
query:
|
||||
type: string
|
||||
showLine:
|
||||
type: boolean
|
||||
step:
|
||||
type: string
|
||||
tagKeys:
|
||||
type: string
|
||||
tags:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
tagsField:
|
||||
type: string
|
||||
textField:
|
||||
type: string
|
||||
textFormat:
|
||||
type: string
|
||||
titleFormat:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
auto_refresh:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
editable:
|
||||
type: boolean
|
||||
id:
|
||||
type: integer
|
||||
panels:
|
||||
items:
|
||||
properties:
|
||||
bars:
|
||||
description: Display as a bar chart
|
||||
type: boolean
|
||||
colors:
|
||||
description: Set series color
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
content:
|
||||
type: string
|
||||
datasource:
|
||||
description: Datasource
|
||||
type: string
|
||||
decimals:
|
||||
format: int64
|
||||
type: integer
|
||||
description:
|
||||
description: Description
|
||||
type: string
|
||||
format:
|
||||
description: Display unit
|
||||
type: string
|
||||
gauge:
|
||||
description: gauge
|
||||
properties:
|
||||
maxValue:
|
||||
format: int64
|
||||
type: integer
|
||||
minValue:
|
||||
format: int64
|
||||
type: integer
|
||||
show:
|
||||
type: boolean
|
||||
thresholdLabels:
|
||||
type: boolean
|
||||
thresholdMarkers:
|
||||
type: boolean
|
||||
type: object
|
||||
height:
|
||||
description: Height
|
||||
type: string
|
||||
id:
|
||||
description: Panel ID
|
||||
format: int64
|
||||
type: integer
|
||||
legend:
|
||||
description: legend
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
lines:
|
||||
description: Display as a line chart
|
||||
type: boolean
|
||||
mode:
|
||||
type: string
|
||||
options:
|
||||
properties:
|
||||
colorMode:
|
||||
type: string
|
||||
content:
|
||||
type: string
|
||||
displayMode:
|
||||
type: string
|
||||
graphMode:
|
||||
type: string
|
||||
justifyMode:
|
||||
type: string
|
||||
mode:
|
||||
type: string
|
||||
orientation:
|
||||
type: string
|
||||
textMode:
|
||||
type: string
|
||||
type: object
|
||||
scroll:
|
||||
type: boolean
|
||||
sort:
|
||||
properties:
|
||||
col:
|
||||
type: integer
|
||||
desc:
|
||||
type: boolean
|
||||
type: object
|
||||
sparkline:
|
||||
description: 'spark line: full or bottom'
|
||||
type: string
|
||||
stack:
|
||||
description: Display as a stacked chart
|
||||
type: boolean
|
||||
targets:
|
||||
description: A collection of queries
|
||||
items:
|
||||
description: Query editor options Referers to https://pkg.go.dev/github.com/grafana-tools/sdk#Target
|
||||
properties:
|
||||
expr:
|
||||
description: 'only support prometheus,and the corresponding
|
||||
fields are as follows: Input for fetching metrics.'
|
||||
type: string
|
||||
legendFormat:
|
||||
description: Legend format for outputs. You can make a
|
||||
dynamic legend with templating variables.
|
||||
type: string
|
||||
refId:
|
||||
description: Reference ID
|
||||
format: int64
|
||||
type: integer
|
||||
step:
|
||||
description: Set series time interval
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
title:
|
||||
description: Name of the panel
|
||||
type: string
|
||||
type:
|
||||
description: Type of the panel
|
||||
type: string
|
||||
valueName:
|
||||
description: value name
|
||||
type: string
|
||||
xaxis:
|
||||
properties:
|
||||
decimals:
|
||||
description: Limit the decimal numbers
|
||||
format: int64
|
||||
type: integer
|
||||
format:
|
||||
description: Display unit
|
||||
type: string
|
||||
type: object
|
||||
yaxes:
|
||||
description: Y-axis options
|
||||
items:
|
||||
properties:
|
||||
decimals:
|
||||
description: Limit the decimal numbers
|
||||
format: int64
|
||||
type: integer
|
||||
format:
|
||||
description: Display unit
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
type: array
|
||||
shared_crosshair:
|
||||
type: boolean
|
||||
tags:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
templatings:
|
||||
description: // Templating variables
|
||||
items:
|
||||
properties:
|
||||
allFormat:
|
||||
type: string
|
||||
allValue:
|
||||
type: string
|
||||
auto:
|
||||
type: boolean
|
||||
auto_count:
|
||||
type: integer
|
||||
datasource:
|
||||
type: string
|
||||
hide:
|
||||
type: integer
|
||||
includeAll:
|
||||
type: boolean
|
||||
label:
|
||||
type: string
|
||||
multi:
|
||||
type: boolean
|
||||
multiFormat:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
options:
|
||||
items:
|
||||
properties:
|
||||
selected:
|
||||
type: boolean
|
||||
text:
|
||||
type: string
|
||||
value:
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
query:
|
||||
type: string
|
||||
regex:
|
||||
type: string
|
||||
sort:
|
||||
type: integer
|
||||
type:
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
time:
|
||||
description: Time range
|
||||
properties:
|
||||
from:
|
||||
description: Start time in the format of `^now([+-][0-9]+[smhdwMy])?$`,
|
||||
eg. `now-1M`. It denotes the end time is set to the last month
|
||||
since now.
|
||||
type: string
|
||||
to:
|
||||
description: End time in the format of `^now([+-][0-9]+[smhdwMy])?$`,
|
||||
eg. `now-1M`. It denotes the start time is set to the last month
|
||||
since now.
|
||||
type: string
|
||||
type: object
|
||||
timezone:
|
||||
type: string
|
||||
title:
|
||||
type: string
|
||||
uid:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
@@ -21,24 +21,6 @@ type CreateKsCore struct {
|
||||
}
|
||||
|
||||
func (t *CreateKsCore) Execute(runtime connector.Runtime) error {
|
||||
//var kubectlpath, err = util.GetCommand(common.CommandKubectl)
|
||||
//if err != nil {
|
||||
// return fmt.Errorf("kubectl not found")
|
||||
//}
|
||||
|
||||
//var cmd = fmt.Sprintf("%s get pod -n %s -l 'app=redis,tier=database,version=redis-4.0' -o jsonpath='{.items[0].status.phase}'", kubectlpath,
|
||||
// common.NamespaceKubesphereSystem)
|
||||
//rphase, err := runtime.GetRunner().Host.SudoCmd(cmd, false, false)
|
||||
//if rphase != "Running" {
|
||||
// return fmt.Errorf("Redis State %s", rphase)
|
||||
//}
|
||||
|
||||
masterNumIf, ok := t.PipelineCache.Get(common.CacheMasterNum)
|
||||
if !ok || masterNumIf == nil {
|
||||
return fmt.Errorf("failed to get master num")
|
||||
}
|
||||
masterNum := masterNumIf.(int64)
|
||||
|
||||
config, err := ctrl.GetConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -55,7 +37,7 @@ func (t *CreateKsCore) Execute(runtime connector.Runtime) error {
|
||||
var values = make(map[string]interface{})
|
||||
values["Release"] = map[string]string{
|
||||
"Namespace": common.NamespaceKubesphereSystem,
|
||||
"ReplicaCount": fmt.Sprintf("%d", masterNum),
|
||||
"ReplicaCount": fmt.Sprintf("%d", 1),
|
||||
}
|
||||
if err := utils.UpgradeCharts(context.Background(), actionConfig, settings, appKsCoreName,
|
||||
appPath, "", common.NamespaceKubesphereSystem, values, false); err != nil {
|
||||
@@ -78,7 +60,6 @@ func (m *DeployKsCoreModule) Init() {
|
||||
Hosts: m.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: new(CreateKsCore),
|
||||
Parallel: false,
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
@@ -21,129 +20,6 @@ import (
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
)
|
||||
|
||||
var kscorecrds = []map[string]string{
|
||||
{
|
||||
"ns": "kubesphere-controls-system",
|
||||
"kind": "serviceaccounts",
|
||||
"resource": "kubesphere-cluster-admin",
|
||||
"release": "ks-core",
|
||||
},
|
||||
{
|
||||
"ns": "kubesphere-controls-system",
|
||||
"kind": "serviceaccounts",
|
||||
"resource": "kubesphere-router-serviceaccount",
|
||||
"release": "ks-core",
|
||||
},
|
||||
{
|
||||
"ns": "kubesphere-controls-system",
|
||||
"kind": "role",
|
||||
"resource": "system:kubesphere-router-role",
|
||||
"release": "ks-core",
|
||||
},
|
||||
{
|
||||
"ns": "kubesphere-controls-system",
|
||||
"kind": "rolebinding",
|
||||
"resource": "nginx-ingress-role-nisa-binding",
|
||||
"release": "ks-core",
|
||||
},
|
||||
{
|
||||
"ns": "kubesphere-controls-system",
|
||||
"kind": "deployment",
|
||||
"resource": "default-http-backend",
|
||||
"release": "ks-core",
|
||||
},
|
||||
{
|
||||
"ns": "kubesphere-controls-system",
|
||||
"kind": "service",
|
||||
"resource": "default-http-backend",
|
||||
"release": "ks-core",
|
||||
},
|
||||
//{
|
||||
// "ns": "kubesphere-system",
|
||||
// "kind": "secrets",
|
||||
// "resource": "ks-controller-manager-webhook-cert",
|
||||
// "release": "ks-core",
|
||||
//},
|
||||
{
|
||||
"ns": "kubesphere-system",
|
||||
"kind": "serviceaccounts",
|
||||
"resource": "kubesphere",
|
||||
"release": "ks-core",
|
||||
},
|
||||
{
|
||||
"ns": "kubesphere-system",
|
||||
"kind": "clusterroles",
|
||||
"resource": "system:kubesphere-router-clusterrole",
|
||||
"release": "ks-core",
|
||||
},
|
||||
{
|
||||
"ns": "kubesphere-system",
|
||||
"kind": "clusterrolebindings",
|
||||
"resource": "system:nginx-ingress-clusterrole-nisa-binding",
|
||||
"release": "ks-core",
|
||||
},
|
||||
{
|
||||
"ns": "kubesphere-system",
|
||||
"kind": "clusterrolebindings",
|
||||
"resource": "system:kubesphere-cluster-admin",
|
||||
"release": "ks-core",
|
||||
},
|
||||
{
|
||||
"ns": "kubesphere-system",
|
||||
"kind": "clusterrolebindings",
|
||||
"resource": "kubesphere",
|
||||
"release": "ks-core",
|
||||
},
|
||||
{
|
||||
"ns": "kubesphere-system",
|
||||
"kind": "services",
|
||||
"resource": "ks-apiserver",
|
||||
"release": "ks-core",
|
||||
},
|
||||
//{
|
||||
// "ns": "kubesphere-system",
|
||||
// "kind": "services",
|
||||
// "resource": "ks-controller-manager",
|
||||
// "release": "ks-core",
|
||||
//},
|
||||
{
|
||||
"ns": "kubesphere-system",
|
||||
"kind": "deployments",
|
||||
"resource": "ks-apiserver",
|
||||
"release": "ks-core",
|
||||
},
|
||||
//{
|
||||
// "ns": "kubesphere-system",
|
||||
// "kind": "deployments",
|
||||
// "resource": "ks-controller-manager",
|
||||
// "release": "ks-core",
|
||||
//},
|
||||
//{
|
||||
// "ns": "kubesphere-system",
|
||||
// "kind": "validatingwebhookconfigurations",
|
||||
// "resource": "users.iam.kubesphere.io",
|
||||
// "release": "ks-core",
|
||||
//},
|
||||
{
|
||||
"ns": "kubesphere-system",
|
||||
"kind": "validatingwebhookconfigurations",
|
||||
"resource": "resourcesquotas.quota.kubesphere.io",
|
||||
"release": "ks-core",
|
||||
},
|
||||
{
|
||||
"ns": "kubesphere-system",
|
||||
"kind": "validatingwebhookconfigurations",
|
||||
"resource": "network.kubesphere.io",
|
||||
"release": "ks-core",
|
||||
},
|
||||
{
|
||||
"ns": "kubesphere-system",
|
||||
"kind": "users.iam.kubesphere.io",
|
||||
"resource": "admin",
|
||||
"release": "ks-core",
|
||||
},
|
||||
}
|
||||
|
||||
type CreateKsRole struct {
|
||||
common.KubeAction
|
||||
}
|
||||
@@ -167,42 +43,11 @@ func (t *CreateKsRole) Execute(runtime connector.Runtime) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type PatchKsCoreStatus struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (t *PatchKsCoreStatus) Execute(runtime connector.Runtime) error {
|
||||
//var kubectlpath, _ = t.PipelineCache.GetMustString(common.CacheCommandKubectlPath)
|
||||
//if kubectlpath == "" {
|
||||
// kubectlpath = path.Join(common.BinDir, common.CommandKubectl)
|
||||
//}
|
||||
//
|
||||
//var jsonPath = fmt.Sprintf(`{\"status\": {\"core\": {\"status\": \"enabled\", \"enabledTime\": \"%s\"}}}`, time.Now().Format("2006-01-02T15:04:05Z"))
|
||||
//var cmd = fmt.Sprintf("%s patch cc ks-installer --type merge -p '%s' -n %s", kubectlpath, jsonPath, common.NamespaceKubesphereSystem)
|
||||
//
|
||||
//_, err := runtime.GetRunner().Host.SudoCmd(cmd, false, true)
|
||||
//if err != nil {
|
||||
// return errors.Wrap(errors.WithStack(err), "patch ks-core status failed")
|
||||
//}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type CreateKsCoreConfig struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (t *CreateKsCoreConfig) Execute(runtime connector.Runtime) error {
|
||||
jwtSecretIf, ok := t.PipelineCache.Get(common.CacheJwtSecret)
|
||||
if !ok || jwtSecretIf == nil {
|
||||
return fmt.Errorf("failed to get jwt secret")
|
||||
}
|
||||
|
||||
kubeVersionIf, ok := t.PipelineCache.Get(common.CacheKubeletVersion)
|
||||
if !ok || kubeVersionIf == nil {
|
||||
return fmt.Errorf("failed to get kubelet version")
|
||||
}
|
||||
|
||||
config, err := ctrl.GetConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -230,13 +75,8 @@ func (t *CreateKsCoreConfig) Execute(runtime connector.Runtime) error {
|
||||
// create ks-config
|
||||
var appKsConfigName = common.ChartNameKsConfig
|
||||
appPath = path.Join(runtime.GetInstallerDir(), cc.BuildFilesCacheDir, cc.BuildDir, appKsConfigName)
|
||||
values = make(map[string]interface{})
|
||||
values["Release"] = map[string]interface{}{
|
||||
"JwtSecret": jwtSecretIf.(string),
|
||||
"TokenMaxAge": t.KubeConf.Arg.TokenMaxAge * int64(time.Second),
|
||||
}
|
||||
if err := utils.UpgradeCharts(context.Background(), actionConfig, settings, appKsConfigName,
|
||||
appPath, "", common.NamespaceKubesphereSystem, values, false); err != nil {
|
||||
appPath, "", common.NamespaceKubesphereSystem, nil, false); err != nil {
|
||||
logger.Errorf("failed to install %s chart: %v", appKsConfigName, err)
|
||||
return err
|
||||
}
|
||||
@@ -273,82 +113,6 @@ func (t *CreateKsCoreConfigManifests) Execute(runtime connector.Runtime) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type PacthKsCore struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (t *PacthKsCore) Execute(runtime connector.Runtime) error {
|
||||
var secretsNum int64
|
||||
var crdNum int64
|
||||
var secretsNumIf, ok = t.PipelineCache.Get(common.CacheSecretsNum)
|
||||
if ok && secretsNumIf != nil {
|
||||
secretsNum = secretsNumIf.(int64)
|
||||
}
|
||||
|
||||
crdNumIf, ok := t.PipelineCache.Get(common.CacheCrdsNUm)
|
||||
if ok && crdNumIf != nil {
|
||||
crdNum = crdNumIf.(int64)
|
||||
}
|
||||
|
||||
var kubectlpath, err = util.GetCommand(common.CommandKubectl)
|
||||
if err != nil {
|
||||
return fmt.Errorf("kubectl not found")
|
||||
}
|
||||
|
||||
if secretsNum == 0 && crdNum != 0 {
|
||||
for _, item := range kscorecrds {
|
||||
var cmd = fmt.Sprintf("%s -n %s annotate --overwrite %s %s meta.helm.sh/release-name=%s && %s -n %s annotate --overwrite %s %s meta.helm.sh/release-namespace=%s && %s -n %s label --overwrite %s %s app.kubernetes.io/managed-by=Helm",
|
||||
kubectlpath, item["ns"], item["kind"], item["resource"], item["release"],
|
||||
kubectlpath, item["ns"], item["kind"], item["resource"], common.NamespaceKubesphereSystem,
|
||||
kubectlpath, item["ns"], item["kind"], item["resource"])
|
||||
|
||||
if _, err := runtime.GetRunner().SudoCmd(cmd, false, true); err != nil {
|
||||
return errors.Wrap(errors.WithStack(err), "patch ks-core crd")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type CheckKsCoreExist struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (t *CheckKsCoreExist) Execute(runtime connector.Runtime) error {
|
||||
var kubectlpath, err = util.GetCommand(common.CommandKubectl)
|
||||
if err != nil {
|
||||
return fmt.Errorf("kubectl not found")
|
||||
}
|
||||
|
||||
var cmd string
|
||||
|
||||
cmd = fmt.Sprintf("%s -n %s get secrets --field-selector=type=helm.sh/release.v1 | grep ks-core |wc -l",
|
||||
kubectlpath,
|
||||
common.NamespaceKubesphereSystem)
|
||||
stdout, _ := runtime.GetRunner().SudoCmd(cmd, false, false)
|
||||
|
||||
secretNum, err := strconv.ParseInt(stdout, 10, 64)
|
||||
if err != nil {
|
||||
secretNum = 0
|
||||
}
|
||||
|
||||
cmd = fmt.Sprintf("%s get crd users.iam.kubesphere.io | grep 'users.iam.kubesphere.io' |wc -l", kubectlpath)
|
||||
stdout, _ = runtime.GetRunner().SudoCmd(cmd, false, false)
|
||||
|
||||
usersCrdNum, err := strconv.ParseInt(stdout, 10, 64)
|
||||
if err != nil {
|
||||
usersCrdNum = 0
|
||||
}
|
||||
|
||||
logger.Debugf("secretNum: %d, usersCrdNum: %d", secretNum, usersCrdNum)
|
||||
|
||||
t.ModuleCache.Set(common.CacheSecretsNum, secretNum)
|
||||
t.ModuleCache.Set(common.CacheCrdsNUm, usersCrdNum)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type DeployKsCoreConfigModule struct {
|
||||
common.KubeModule
|
||||
}
|
||||
@@ -356,37 +120,11 @@ type DeployKsCoreConfigModule struct {
|
||||
func (m *DeployKsCoreConfigModule) Init() {
|
||||
m.Name = "DeployKsCoreConfig"
|
||||
|
||||
checkKsCoreExist := &task.RemoteTask{
|
||||
Name: "CheckKsCoreExist",
|
||||
Hosts: m.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
new(common.GetMasterNum),
|
||||
},
|
||||
Action: new(CheckKsCoreExist),
|
||||
Parallel: false,
|
||||
Retry: 0,
|
||||
}
|
||||
|
||||
pacthKsCore := &task.RemoteTask{
|
||||
Name: "PacthKsCore",
|
||||
Hosts: m.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: new(PacthKsCore),
|
||||
Parallel: false,
|
||||
Retry: 0,
|
||||
}
|
||||
|
||||
createKsCoreConfigManifests := &task.RemoteTask{
|
||||
Name: "CreateKsCoreConfigManifests",
|
||||
Hosts: m.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: new(CreateKsCoreConfigManifests),
|
||||
Parallel: false,
|
||||
@@ -399,31 +137,17 @@ func (m *DeployKsCoreConfigModule) Init() {
|
||||
Hosts: m.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: new(CreateKsCoreConfig),
|
||||
Parallel: true,
|
||||
Retry: 0,
|
||||
}
|
||||
|
||||
patchKsCoreStatus := &task.RemoteTask{
|
||||
Name: "PatchKsCoreStatus",
|
||||
Hosts: m.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: new(PatchKsCoreStatus),
|
||||
Parallel: true,
|
||||
Retry: 0,
|
||||
}
|
||||
|
||||
createKsRole := &task.RemoteTask{
|
||||
Name: "CreateKsRole",
|
||||
Hosts: m.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: new(CreateKsRole),
|
||||
Parallel: true,
|
||||
@@ -431,11 +155,8 @@ func (m *DeployKsCoreConfigModule) Init() {
|
||||
}
|
||||
|
||||
m.Tasks = []task.Interface{
|
||||
checkKsCoreExist,
|
||||
pacthKsCore,
|
||||
createKsCoreConfigManifests,
|
||||
createKsCoreConfig,
|
||||
patchKsCoreStatus,
|
||||
createKsRole,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package plugins
|
||||
|
||||
// ! Y ./ks-monitor/files/federated --> notification-manager
|
||||
// ~ N ./ks-monitor/files/gpu-monitoring
|
||||
// ~ Y ./ks-monitor/files/ks-istio-monitoring
|
||||
// ! Y ./ks-monitor/files/monitoring-dashboard
|
||||
// ! Y ./ks-monitor/files/notification-manager
|
||||
// ! Y ./ks-monitor/files/prometheus/alertmanager
|
||||
// ~ N ./ks-monitor/files/prometheus/etcd
|
||||
// ~ N ./ks-monitor/files/prometheus/grafana
|
||||
// ~ N ./ks-monitor/files/prometheus/kube-prometheus
|
||||
// ! Y ./ks-monitor/files/prometheus/kube-state-metrics
|
||||
// ! Y ./ks-monitor/files/prometheus/kubernetes
|
||||
// ! Y ./ks-monitor/files/prometheus/node-exporter
|
||||
// ! Y ./ks-monitor/files/prometheus/prometheus
|
||||
// ! Y ./ks-monitor/files/prometheus/prometheus-operator
|
||||
// ~ N ./ks-monitor/files/prometheus/thanos-ruler
|
||||
@@ -1,10 +1,7 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
"github.com/beclab/Olares/cli/pkg/core/prepare"
|
||||
"github.com/beclab/Olares/cli/pkg/core/task"
|
||||
)
|
||||
|
||||
@@ -24,40 +21,3 @@ func (t *CopyEmbed) Init() {
|
||||
copyEmbed,
|
||||
}
|
||||
}
|
||||
|
||||
type DeployKsPluginsModule struct {
|
||||
common.KubeModule
|
||||
}
|
||||
|
||||
func (t *DeployKsPluginsModule) Init() {
|
||||
t.Name = "DeployKsPlugins"
|
||||
|
||||
checkNodeState := &task.RemoteTask{
|
||||
Name: "CheckNodeState",
|
||||
Hosts: t.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: new(CheckNodeState),
|
||||
Parallel: false,
|
||||
Retry: 20,
|
||||
Delay: 10 * time.Second,
|
||||
}
|
||||
|
||||
initNs := &task.RemoteTask{
|
||||
Name: "InitKsNamespace",
|
||||
Hosts: t.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: new(InitNamespace),
|
||||
Parallel: false,
|
||||
}
|
||||
|
||||
t.Tasks = []task.Interface{
|
||||
checkNodeState,
|
||||
initNs,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
cc "github.com/beclab/Olares/cli/pkg/core/common"
|
||||
"github.com/beclab/Olares/cli/pkg/core/connector"
|
||||
"github.com/beclab/Olares/cli/pkg/core/prepare"
|
||||
"github.com/beclab/Olares/cli/pkg/core/task"
|
||||
"github.com/beclab/Olares/cli/pkg/core/util"
|
||||
)
|
||||
|
||||
type InstallMonitorDashboardCrd struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (t *InstallMonitorDashboardCrd) Execute(runtime connector.Runtime) error {
|
||||
var kubectlpath, err = util.GetCommand(common.CommandKubectl)
|
||||
if err != nil {
|
||||
return fmt.Errorf("kubectl not found")
|
||||
}
|
||||
|
||||
var p = path.Join(runtime.GetInstallerDir(), cc.BuildFilesCacheDir, cc.BuildDir, "ks-monitor", "monitoring-dashboard")
|
||||
var cmd = fmt.Sprintf("%s apply -f %s", kubectlpath, p)
|
||||
if _, err := runtime.GetRunner().SudoCmd(cmd, false, true); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type CreateMonitorDashboardModule struct {
|
||||
common.KubeModule
|
||||
}
|
||||
|
||||
func (m *CreateMonitorDashboardModule) Init() {
|
||||
m.Name = "CreateMonitorDashboardModule"
|
||||
|
||||
installMonitorDashboardCrd := &task.RemoteTask{
|
||||
Name: "InstallMonitorDashboardCrd",
|
||||
Hosts: m.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: new(InstallMonitorDashboardCrd),
|
||||
Parallel: false,
|
||||
Retry: 0,
|
||||
}
|
||||
|
||||
m.Tasks = []task.Interface{
|
||||
installMonitorDashboardCrd,
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,14 +17,8 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
"github.com/beclab/Olares/cli/pkg/core/connector"
|
||||
"github.com/beclab/Olares/cli/pkg/utils"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type IsCloudInstance struct {
|
||||
@@ -43,72 +37,3 @@ func (p *IsCloudInstance) PreCheck(runtime connector.Runtime) (bool, error) {
|
||||
}
|
||||
return p.Not, nil
|
||||
}
|
||||
|
||||
type CheckStorageClass struct {
|
||||
common.KubePrepare
|
||||
}
|
||||
|
||||
func (p *CheckStorageClass) PreCheck(runtime connector.Runtime) (bool, error) {
|
||||
var kubectlpath, _ = p.PipelineCache.GetMustString(common.CacheCommandKubectlPath)
|
||||
if kubectlpath == "" {
|
||||
kubectlpath = path.Join(common.BinDir, common.CommandKubectl)
|
||||
}
|
||||
|
||||
var cmd = fmt.Sprintf("%s get sc | awk '{if(NR>1){print $1}}'", kubectlpath)
|
||||
stdout, err := runtime.GetRunner().SudoCmd(cmd, false, true)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(errors.WithStack(err), "get storageclass failed")
|
||||
}
|
||||
if stdout == "" {
|
||||
return false, fmt.Errorf("no storageclass found")
|
||||
}
|
||||
|
||||
cmd = fmt.Sprintf("%s get sc --no-headers", kubectlpath)
|
||||
stdout, err = runtime.GetRunner().SudoCmd(cmd, false, true)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(errors.WithStack(err), "get storageclass failed")
|
||||
}
|
||||
|
||||
if stdout == "" {
|
||||
return false, fmt.Errorf("no storageclass found")
|
||||
}
|
||||
|
||||
if !strings.Contains(stdout, "(default)") {
|
||||
return false, fmt.Errorf("default storageclass was not found")
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
type GenerateRedisPassword struct {
|
||||
common.KubePrepare
|
||||
}
|
||||
|
||||
func (p *GenerateRedisPassword) PreCheck(runtime connector.Runtime) (bool, error) {
|
||||
pass, err := utils.GeneratePassword(15)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if pass == "" {
|
||||
return false, fmt.Errorf("failed to generate redis password")
|
||||
}
|
||||
|
||||
p.PipelineCache.Set(common.CacheRedisPassword, pass)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
type NotEqualDesiredVersion struct {
|
||||
common.KubePrepare
|
||||
}
|
||||
|
||||
func (n *NotEqualDesiredVersion) PreCheck(runtime connector.Runtime) (bool, error) {
|
||||
ksVersion, ok := n.PipelineCache.GetMustString(common.KubeSphereVersion)
|
||||
if !ok {
|
||||
ksVersion = ""
|
||||
}
|
||||
|
||||
if n.KubeConf.Cluster.KubeSphere.Version == ksVersion {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -118,7 +118,6 @@ func (m *DeployPrometheusModule) Init() {
|
||||
Hosts: m.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: new(CreateOperator),
|
||||
Parallel: false,
|
||||
@@ -130,7 +129,6 @@ func (m *DeployPrometheusModule) Init() {
|
||||
Hosts: m.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: &CreatePrometheusComponent{
|
||||
Component: "node-exporter",
|
||||
@@ -145,7 +143,6 @@ func (m *DeployPrometheusModule) Init() {
|
||||
Hosts: m.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: &CreatePrometheusComponent{
|
||||
Component: "kube-state-metrics",
|
||||
@@ -160,7 +157,6 @@ func (m *DeployPrometheusModule) Init() {
|
||||
Hosts: m.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: &CreatePrometheusComponent{
|
||||
Component: "prometheus",
|
||||
@@ -173,7 +169,6 @@ func (m *DeployPrometheusModule) Init() {
|
||||
Hosts: m.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: &CreatePrometheusComponent{
|
||||
Component: "kubernetes",
|
||||
@@ -182,26 +177,12 @@ func (m *DeployPrometheusModule) Init() {
|
||||
Parallel: false,
|
||||
}
|
||||
|
||||
//createAlertManager := &task.RemoteTask{
|
||||
// Name: "CreateAlertManager",
|
||||
// Hosts: m.Runtime.GetHostsByRole(common.Master),
|
||||
// Prepare: &prepare.PrepareCollection{
|
||||
// new(common.OnlyFirstMaster),
|
||||
// new(NotEqualDesiredVersion),
|
||||
// },
|
||||
// Action: &CreatePrometheusComponent{
|
||||
// Component: "alertmanager",
|
||||
// },
|
||||
// Parallel: false,
|
||||
//}
|
||||
|
||||
m.Tasks = []task.Interface{
|
||||
createOperator,
|
||||
createNodeExporter,
|
||||
createKubeStateMetrics,
|
||||
createPrometheus,
|
||||
createKubeMonitor,
|
||||
//createAlertManager,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
cc "github.com/beclab/Olares/cli/pkg/core/common"
|
||||
"github.com/beclab/Olares/cli/pkg/core/connector"
|
||||
"github.com/beclab/Olares/cli/pkg/core/logger"
|
||||
"github.com/beclab/Olares/cli/pkg/core/prepare"
|
||||
"github.com/beclab/Olares/cli/pkg/core/task"
|
||||
"github.com/beclab/Olares/cli/pkg/core/util"
|
||||
"github.com/beclab/Olares/cli/pkg/utils"
|
||||
"github.com/pkg/errors"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
)
|
||||
|
||||
type CreateRedisSecret struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (t *CreateRedisSecret) Execute(runtime connector.Runtime) error {
|
||||
kubectlpath, err := util.GetCommand(common.CommandKubectl)
|
||||
if err != nil {
|
||||
return fmt.Errorf("kubectl not found")
|
||||
}
|
||||
|
||||
redisPwd, ok := t.PipelineCache.Get(common.CacheRedisPassword)
|
||||
if !ok {
|
||||
return fmt.Errorf("get redis password from module cache failed")
|
||||
}
|
||||
|
||||
if stdout, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("%s -n %s create secret generic redis-secret --from-literal=auth=%s", kubectlpath, common.NamespaceKubesphereSystem, redisPwd), false, true); err != nil {
|
||||
if err != nil && !strings.Contains(stdout, "already exists") {
|
||||
return errors.Wrap(errors.WithStack(err), "create redis secret failed")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type BackupRedisManifests struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (t *BackupRedisManifests) Execute(runtime connector.Runtime) error {
|
||||
kubectlpath, err := util.GetCommand(common.CommandKubectl)
|
||||
if err != nil {
|
||||
return fmt.Errorf("kubectl not found")
|
||||
}
|
||||
|
||||
rver, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("%s get pod -n %s -l app=%s,tier=database,version=%s-4.0 | wc -l",
|
||||
kubectlpath, common.NamespaceKubesphereSystem, common.ChartNameRedis, common.ChartNameRedis), false, false)
|
||||
|
||||
if err != nil || strings.Contains(rver, "No resources found") {
|
||||
return nil
|
||||
}
|
||||
rver = strings.ReplaceAll(rver, "No resources found in kubesphere-system namespace.", "")
|
||||
rver = strings.ReplaceAll(rver, "\r\n", "")
|
||||
rver = strings.ReplaceAll(rver, "\n", "")
|
||||
if rver != "0" {
|
||||
var cmd = fmt.Sprintf("%s get svc -n %s %s -o yaml > %s/redis-svc-backup.yaml && %s delete svc -n %s %s",
|
||||
kubectlpath,
|
||||
common.NamespaceKubesphereSystem, common.ChartNameRedis,
|
||||
common.KubeManifestDir, // todo need fix cross platforms
|
||||
kubectlpath,
|
||||
common.NamespaceKubesphereSystem, common.ChartNameRedis)
|
||||
|
||||
if _, err := runtime.GetRunner().SudoCmd(cmd, false, true); err != nil {
|
||||
logger.Errorf("failed to backup %s svc: %v", common.ChartNameRedis, err)
|
||||
return errors.Wrap(errors.WithStack(err), "backup redis svc failed")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DeployRedis struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (t *DeployRedis) Execute(runtime connector.Runtime) error {
|
||||
config, err := ctrl.GetConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var appName = common.ChartNameRedis
|
||||
var appPath = path.Join(runtime.GetInstallerDir(), cc.BuildFilesCacheDir, cc.BuildDir, appName)
|
||||
|
||||
actionConfig, settings, err := utils.InitConfig(config, common.NamespaceKubesphereSystem)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ctx, cancel = context.WithTimeout(context.Background(), 3*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
if err := utils.UpgradeCharts(ctx, actionConfig, settings, appName, appPath, "", common.NamespaceKubesphereSystem, nil, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// +++++
|
||||
|
||||
type DeployRedisModule struct {
|
||||
common.KubeModule
|
||||
}
|
||||
|
||||
func (m *DeployRedisModule) Init() {
|
||||
m.Name = "DeployRedis"
|
||||
|
||||
createRedisSecret := &task.RemoteTask{
|
||||
Name: "CreateRedisSecret",
|
||||
Hosts: m.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
new(GenerateRedisPassword),
|
||||
},
|
||||
Action: new(CreateRedisSecret),
|
||||
Parallel: false,
|
||||
Retry: 0,
|
||||
}
|
||||
|
||||
backupRedisManifests := &task.RemoteTask{
|
||||
Name: "BackupRedisManifests",
|
||||
Hosts: m.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: new(BackupRedisManifests),
|
||||
Parallel: false,
|
||||
Retry: 0,
|
||||
}
|
||||
|
||||
deployRedis := &task.RemoteTask{
|
||||
Name: "DeployRedis",
|
||||
Hosts: m.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
new(CheckStorageClass),
|
||||
},
|
||||
Action: new(DeployRedis),
|
||||
Parallel: false,
|
||||
Retry: 0,
|
||||
}
|
||||
|
||||
m.Tasks = []task.Interface{
|
||||
createRedisSecret,
|
||||
backupRedisManifests,
|
||||
deployRedis,
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,12 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
cc "github.com/beclab/Olares/cli/pkg/core/common"
|
||||
"github.com/beclab/Olares/cli/pkg/core/connector"
|
||||
"github.com/beclab/Olares/cli/pkg/core/logger"
|
||||
"github.com/beclab/Olares/cli/pkg/utils"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type CopyEmbedFiles struct {
|
||||
@@ -21,97 +17,3 @@ func (t *CopyEmbedFiles) Execute(runtime connector.Runtime) error {
|
||||
var dst = path.Join(runtime.GetInstallerDir(), cc.BuildFilesCacheDir)
|
||||
return utils.CopyEmbed(assets, ".", dst)
|
||||
}
|
||||
|
||||
type CheckNodeState struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (t *CheckNodeState) Execute(runtime connector.Runtime) error {
|
||||
var kubectlpath, _ = t.PipelineCache.GetMustString(common.CacheCommandKubectlPath)
|
||||
if kubectlpath == "" {
|
||||
kubectlpath = path.Join(common.BinDir, common.CommandKubectl)
|
||||
}
|
||||
var cmd = fmt.Sprintf("%s get node --no-headers", kubectlpath)
|
||||
stdout, err := runtime.GetRunner().SudoCmd(cmd, false, false)
|
||||
|
||||
if err != nil || stdout == "" {
|
||||
return fmt.Errorf("Node Pending")
|
||||
}
|
||||
|
||||
var nodeInfo = strings.Fields(stdout)
|
||||
if len(nodeInfo) != 5 {
|
||||
logger.Errorf("node info invalid: %s", stdout)
|
||||
return fmt.Errorf("Node Pending")
|
||||
}
|
||||
|
||||
var state = nodeInfo[1]
|
||||
var version = nodeInfo[4]
|
||||
|
||||
if state != "Ready" {
|
||||
return fmt.Errorf("Node Pending")
|
||||
}
|
||||
t.PipelineCache.Set(common.CacheKubeletVersion, version)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type InitNamespace struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (t *InitNamespace) Execute(runtime connector.Runtime) error {
|
||||
var kubectlpath, _ = t.PipelineCache.GetMustString(common.CacheCommandKubectlPath)
|
||||
if kubectlpath == "" {
|
||||
kubectlpath = path.Join(common.BinDir, common.CommandKubectl)
|
||||
}
|
||||
|
||||
for _, ns := range []string{common.NamespaceKubesphereControlsSystem} {
|
||||
if stdout, err := runtime.GetRunner().Cmd(fmt.Sprintf("%s create ns %s", kubectlpath, ns), false, true); err != nil {
|
||||
if !strings.Contains(stdout, "already exists") {
|
||||
logger.Errorf("create ns %s failed: %v", ns, err)
|
||||
return errors.Wrap(errors.WithStack(err), fmt.Sprintf("create namespace %s failed: %v", ns, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
// _, err := runtime.GetRunner().SudoCmd(
|
||||
// fmt.Sprintf(`cat <<EOF | /usr/local/bin/kubectl apply -f -
|
||||
// apiVersion: v1
|
||||
// kind: Namespace
|
||||
// metadata:
|
||||
// name: %s
|
||||
// ---
|
||||
// apiVersion: v1
|
||||
// kind: Namespace
|
||||
// metadata:
|
||||
// name: %s
|
||||
// EOF
|
||||
// `, common.NamespaceKubesphereControlsSystem, common.NamespaceKubesphereMonitoringFederated), false, true)
|
||||
// if err != nil {
|
||||
// return errors.Wrap(errors.WithStack(err), fmt.Sprintf("create namespace: %s and %s",
|
||||
// common.NamespaceKubesphereControlsSystem, common.NamespaceKubesphereMonitoringFederated))
|
||||
// }
|
||||
|
||||
var allNs = []string{
|
||||
common.NamespaceDefault,
|
||||
common.NamespaceKubeNodeLease,
|
||||
common.NamespaceKubePublic,
|
||||
common.NamespaceKubeSystem,
|
||||
common.NamespaceKubekeySystem,
|
||||
common.NamespaceKubesphereControlsSystem,
|
||||
common.NamespaceKubesphereSystem,
|
||||
}
|
||||
|
||||
for _, ns := range allNs {
|
||||
if _, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("%s label ns %s kubesphere.io/workspace=system-workspace --overwrite", kubectlpath, ns), false, true); err != nil {
|
||||
logger.Errorf("label ns %s kubesphere.io/workspace=system-workspace failed: %v", ns, err)
|
||||
return errors.Wrap(errors.WithStack(err), fmt.Sprintf("label namespace %s kubesphere.io/workspace=system-workspace failed: %v", ns, err))
|
||||
}
|
||||
|
||||
if _, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("%s label ns %s kubesphere.io/namespace=%s --overwrite", kubectlpath, ns, ns), false, true); err != nil {
|
||||
logger.Errorf("label ns %s kubesphere.io/namespace=%s failed: %v", ns, ns, err)
|
||||
return errors.Wrap(errors.WithStack(err), fmt.Sprintf("label namespace %s kubesphere.io/namespace=%s failed: %v", ns, ns, err))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
"github.com/beclab/Olares/cli/pkg/core/connector"
|
||||
"github.com/beclab/Olares/cli/pkg/core/logger"
|
||||
"github.com/beclab/Olares/cli/pkg/core/prepare"
|
||||
"github.com/beclab/Olares/cli/pkg/core/task"
|
||||
"github.com/beclab/Olares/cli/pkg/core/util"
|
||||
"github.com/beclab/Olares/cli/pkg/utils"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type GenerateKubeSphereToken struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (t *GenerateKubeSphereToken) Execute(runtime connector.Runtime) error {
|
||||
var kubectlpath, _ = t.PipelineCache.GetMustString(common.CacheCommandKubectlPath)
|
||||
if kubectlpath == "" {
|
||||
kubectlpath = path.Join(common.BinDir, common.CommandKubectl)
|
||||
}
|
||||
|
||||
var random, err = utils.GeneratePassword(32)
|
||||
if err != nil {
|
||||
logger.Errorf("failed to generate password: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
token, err := util.EncryptToken(random)
|
||||
if err != nil {
|
||||
return errors.Wrap(errors.WithStack(err), "create kubesphere token failed")
|
||||
}
|
||||
|
||||
var cmd = fmt.Sprintf("%s get secrets -n %s --no-headers", kubectlpath, common.NamespaceKubesphereSystem)
|
||||
stdout, _ := runtime.GetRunner().SudoCmd(cmd, false, false)
|
||||
if strings.Contains(stdout, "kubesphere-secret") {
|
||||
cmd = fmt.Sprintf("%s delete secrets -n %s kubesphere-secret", kubectlpath, common.NamespaceKubesphereSystem)
|
||||
runtime.GetRunner().SudoCmd(cmd, false, true)
|
||||
}
|
||||
|
||||
cmd = fmt.Sprintf("%s create secret generic kubesphere-secret --from-literal=token=%s --from-literal=secret=%s -n %s", kubectlpath,
|
||||
token, random, common.NamespaceKubesphereSystem)
|
||||
if _, err := runtime.GetRunner().SudoCmd(cmd, false, true); err != nil {
|
||||
return errors.Wrap(errors.WithStack(err), "create kubesphere token failed")
|
||||
}
|
||||
|
||||
t.PipelineCache.Set(common.CacheJwtSecret, random)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// +++++
|
||||
|
||||
type CreateKubeSphereSecretModule struct {
|
||||
common.KubeModule
|
||||
}
|
||||
|
||||
func (m *CreateKubeSphereSecretModule) Init() {
|
||||
m.Name = "CreateKubeSphereSecret"
|
||||
|
||||
generateKubeSphereToken := &task.RemoteTask{
|
||||
Name: "GenerateKubeSphereToken",
|
||||
Hosts: m.Runtime.GetHostsByRole(common.Master),
|
||||
Prepare: &prepare.PrepareCollection{
|
||||
new(common.OnlyFirstMaster),
|
||||
new(NotEqualDesiredVersion),
|
||||
},
|
||||
Action: new(GenerateKubeSphereToken),
|
||||
Parallel: false,
|
||||
Retry: 0,
|
||||
}
|
||||
|
||||
m.Tasks = []task.Interface{generateKubeSphereToken}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 The KubeSphere Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package kubesphere
|
||||
|
||||
import (
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
"github.com/beclab/Olares/cli/pkg/core/connector"
|
||||
"github.com/pkg/errors"
|
||||
versionutil "k8s.io/apimachinery/pkg/util/version"
|
||||
)
|
||||
|
||||
type VersionBelowV3 struct {
|
||||
common.KubePrepare
|
||||
}
|
||||
|
||||
func (v *VersionBelowV3) PreCheck(runtime connector.Runtime) (bool, error) {
|
||||
versionStr, ok := v.PipelineCache.GetMustString(common.KubeSphereVersion)
|
||||
if !ok {
|
||||
return false, errors.New("get current kubesphere version failed by pipeline cache")
|
||||
}
|
||||
version := versionutil.MustParseSemantic(versionStr)
|
||||
v300 := versionutil.MustParseSemantic("v3.0.0")
|
||||
if v.KubeConf.Cluster.KubeSphere.Enabled && v.KubeConf.Cluster.KubeSphere.Version == "v3.0.0" && version.LessThan(v300) {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
type NotEqualDesiredVersion struct {
|
||||
common.KubePrepare
|
||||
}
|
||||
|
||||
func (n *NotEqualDesiredVersion) PreCheck(runtime connector.Runtime) (bool, error) {
|
||||
ksVersion, ok := n.PipelineCache.GetMustString(common.KubeSphereVersion)
|
||||
if !ok {
|
||||
ksVersion = ""
|
||||
}
|
||||
|
||||
if n.KubeConf.Cluster.KubeSphere.Version == ksVersion {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@@ -18,18 +18,12 @@ package kubesphere
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
kubekeyapiv1alpha2 "github.com/beclab/Olares/cli/apis/kubekey/v1alpha2"
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
"github.com/beclab/Olares/cli/pkg/core/connector"
|
||||
"github.com/beclab/Olares/cli/pkg/core/logger"
|
||||
"github.com/beclab/Olares/cli/pkg/core/util"
|
||||
"github.com/beclab/Olares/cli/pkg/version/kubesphere"
|
||||
"github.com/beclab/Olares/cli/pkg/version/kubesphere/templates"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -52,27 +46,6 @@ func (d *DeleteKubeSphereCaches) Execute(runtime connector.Runtime) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type AddInstallerConfig struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (a *AddInstallerConfig) Execute(runtime connector.Runtime) error {
|
||||
//var ksFilename string
|
||||
|
||||
// if runtime.GetSystemInfo().IsDarwin() {
|
||||
// ksFilename = path.Join(common.TmpDir, "/etc/kubernetes/addons/kubesphere.yaml")
|
||||
// } else {
|
||||
//ksFilename = "/etc/kubernetes/addons/kubesphere.yaml"
|
||||
//// }
|
||||
//configurationBase64 := base64.StdEncoding.EncodeToString([]byte(a.KubeConf.Cluster.KubeSphere.Configurations))
|
||||
//if _, err := runtime.GetRunner().SudoCmd(
|
||||
// fmt.Sprintf("echo %s | base64 -d >> %s", configurationBase64, ksFilename),
|
||||
// false, false); err != nil {
|
||||
// return errors.Wrap(errors.WithStack(err), "add config to ks-installer manifests failed")
|
||||
//}
|
||||
return nil
|
||||
}
|
||||
|
||||
type CreateNamespace struct {
|
||||
common.KubeAction
|
||||
}
|
||||
@@ -91,6 +64,11 @@ metadata:
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: kubesphere-controls-system
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: kubesphere-monitoring-system
|
||||
EOF`, kubectl)
|
||||
@@ -101,235 +79,6 @@ EOF`, kubectl)
|
||||
return nil
|
||||
}
|
||||
|
||||
type Setup struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (s *Setup) Execute(runtime connector.Runtime) error {
|
||||
nodeIp, _ := s.PipelineCache.GetMustString(common.CacheMinikubeNodeIp)
|
||||
filePath := filepath.Join(common.KubeAddonsDir, templates.KsInstaller.Name())
|
||||
|
||||
var minikubepath, ok = s.PipelineCache.GetMustString(common.CacheCommandMinikubePath)
|
||||
if !ok || minikubepath == "" {
|
||||
minikubepath = path.Join(common.BinDir, common.CommandMinikube)
|
||||
}
|
||||
|
||||
kubectlpath, ok := s.PipelineCache.GetMustString(common.CacheCommandKubectlPath)
|
||||
if !ok || kubectlpath == "" {
|
||||
kubectlpath = path.Join(common.BinDir, common.CommandKubectl)
|
||||
}
|
||||
|
||||
var addrList []string
|
||||
var tlsDisable bool
|
||||
var port string
|
||||
switch s.KubeConf.Cluster.Etcd.Type {
|
||||
case kubekeyapiv1alpha2.KubeKey:
|
||||
for _, host := range runtime.GetHostsByRole(common.ETCD) {
|
||||
addrList = append(addrList, host.GetInternalAddress())
|
||||
}
|
||||
|
||||
caFile := "/etc/ssl/etcd/ssl/ca.pem"
|
||||
certFile := fmt.Sprintf("/etc/ssl/etcd/ssl/node-%s.pem", runtime.RemoteHost().GetName())
|
||||
keyFile := fmt.Sprintf("/etc/ssl/etcd/ssl/node-%s-key.pem", runtime.RemoteHost().GetName())
|
||||
if output, err := runtime.GetRunner().SudoCmd(
|
||||
fmt.Sprintf("/usr/local/bin/kubectl -n kubesphere-monitoring-system create secret generic kube-etcd-client-certs "+
|
||||
"--from-file=etcd-client-ca.crt=%s "+
|
||||
"--from-file=etcd-client.crt=%s "+
|
||||
"--from-file=etcd-client.key=%s", caFile, certFile, keyFile), false, false); err != nil {
|
||||
if !strings.Contains(output, "exists") {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case kubekeyapiv1alpha2.MiniKube:
|
||||
var etcdPath = common.KubeEtcdCertDir // path.Join(common.TmpDir, common.KubeEtcdCertDir)
|
||||
if !util.IsExist(etcdPath) {
|
||||
if _, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("mkdir -p %s", etcdPath), false, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var certfiles = []string{
|
||||
"ca.crt",
|
||||
"server.crt",
|
||||
"server.key",
|
||||
}
|
||||
|
||||
for _, certfile := range certfiles {
|
||||
var cfile = path.Join(common.MinikubeEtcdCertDir, certfile)
|
||||
var cmd = fmt.Sprintf("%s -p %s ssh sudo chmod 644 %s && minikube -p %s cp %s:%s %s", minikubepath,
|
||||
runtime.RemoteHost().GetMinikubeProfile(), cfile,
|
||||
runtime.RemoteHost().GetMinikubeProfile(), runtime.RemoteHost().GetMinikubeProfile(),
|
||||
cfile, path.Join(etcdPath, certfile))
|
||||
if _, err := runtime.GetRunner().SudoCmd(cmd, false, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
caFile := path.Join(etcdPath, "ca.crt")
|
||||
certFile := path.Join(etcdPath, "server.crt")
|
||||
keyFile := path.Join(etcdPath, "server.key")
|
||||
|
||||
addrList = append(addrList, nodeIp)
|
||||
if output, err := runtime.GetRunner().SudoCmd(
|
||||
fmt.Sprintf("%s -n kubesphere-monitoring-system create secret generic kube-etcd-client-certs "+
|
||||
"--from-file=%s "+
|
||||
"--from-file=%s "+
|
||||
"--from-file=%s", kubectlpath, caFile, certFile, keyFile), false, false); err != nil {
|
||||
if !strings.Contains(output, "already exists") {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
//path.Join(common.TmpDir, filepath.Join(common.KubeAddonsDir, templates.KsInstaller.Name()))
|
||||
filePath = path.Join(filepath.Join(common.KubeAddonsDir, templates.KsInstaller.Name()))
|
||||
case kubekeyapiv1alpha2.Kubeadm:
|
||||
for _, host := range runtime.GetHostsByRole(common.Master) {
|
||||
addrList = append(addrList, host.GetInternalAddress())
|
||||
}
|
||||
|
||||
caFile := "/etc/kubernetes/pki/etcd/ca.crt"
|
||||
certFile := "/etc/kubernetes/pki/etcd/healthcheck-client.crt"
|
||||
keyFile := "/etc/kubernetes/pki/etcd/healthcheck-client.key"
|
||||
if output, err := runtime.GetRunner().SudoCmd(
|
||||
fmt.Sprintf("/usr/local/bin/kubectl -n kubesphere-monitoring-system create secret generic kube-etcd-client-certs "+
|
||||
"--from-file=etcd-client-ca.crt=%s "+
|
||||
"--from-file=etcd-client.crt=%s "+
|
||||
"--from-file=etcd-client.key=%s", caFile, certFile, keyFile), false, false); err != nil {
|
||||
if !strings.Contains(output, "exists") {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case kubekeyapiv1alpha2.External:
|
||||
for _, endpoint := range s.KubeConf.Cluster.Etcd.External.Endpoints {
|
||||
e := strings.Split(strings.TrimSpace(endpoint), "://")
|
||||
s := strings.Split(e[1], ":")
|
||||
port = s[1]
|
||||
addrList = append(addrList, s[0])
|
||||
if e[0] == "http" {
|
||||
tlsDisable = true
|
||||
}
|
||||
}
|
||||
if tlsDisable {
|
||||
if output, err := runtime.GetRunner().SudoCmd("/usr/local/bin/kubectl -n kubesphere-monitoring-system create secret generic kube-etcd-client-certs", true, false); err != nil {
|
||||
if !strings.Contains(output, "exists") {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
caFile := fmt.Sprintf("/etc/ssl/etcd/ssl/%s", filepath.Base(s.KubeConf.Cluster.Etcd.External.CAFile))
|
||||
certFile := fmt.Sprintf("/etc/ssl/etcd/ssl/%s", filepath.Base(s.KubeConf.Cluster.Etcd.External.CertFile))
|
||||
keyFile := fmt.Sprintf("/etc/ssl/etcd/ssl/%s", filepath.Base(s.KubeConf.Cluster.Etcd.External.KeyFile))
|
||||
if output, err := runtime.GetRunner().SudoCmd(
|
||||
fmt.Sprintf("/usr/local/bin/kubectl -n kubesphere-monitoring-system create secret generic kube-etcd-client-certs "+
|
||||
"--from-file=etcd-client-ca.crt=%s "+
|
||||
"--from-file=etcd-client.crt=%s "+
|
||||
"--from-file=etcd-client.key=%s", caFile, certFile, keyFile), true, false); err != nil {
|
||||
if !strings.Contains(output, "exists") {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var sedCommand = runtime.GetCommandSed()
|
||||
etcdEndPoint := strings.Join(addrList, ",")
|
||||
var cmdEndpoint = fmt.Sprintf("%s '/endpointIps/s/\\:.*/\\: %s/g' %s", sedCommand, etcdEndPoint, filePath)
|
||||
if _, err := runtime.GetRunner().SudoCmd(cmdEndpoint, false, false); err != nil {
|
||||
return errors.Wrap(errors.WithStack(err), fmt.Sprintf("update etcd endpoint failed"))
|
||||
}
|
||||
|
||||
if tlsDisable {
|
||||
if _, err := runtime.GetRunner().SudoCmd(
|
||||
fmt.Sprintf("%s '/tlsEnable/s/\\:.*/\\: false/g' %s", sedCommand, filePath),
|
||||
false, false); err != nil {
|
||||
return errors.Wrap(errors.WithStack(err), fmt.Sprintf("update etcd tls failed"))
|
||||
}
|
||||
}
|
||||
|
||||
if len(port) != 0 {
|
||||
if _, err := runtime.GetRunner().SudoCmd(
|
||||
fmt.Sprintf("%s 's/2379/%s/g' %s", sedCommand, port, filePath),
|
||||
false, false); err != nil {
|
||||
return errors.Wrap(errors.WithStack(err), fmt.Sprintf("update etcd tls failed"))
|
||||
}
|
||||
}
|
||||
|
||||
if s.KubeConf.Cluster.Registry.PrivateRegistry != "" {
|
||||
PrivateRegistry := strings.Replace(s.KubeConf.Cluster.Registry.PrivateRegistry, "/", "\\/", -1)
|
||||
if _, err := runtime.GetRunner().SudoCmd(
|
||||
fmt.Sprintf("%s '/local_registry/s/\\:.*/\\: %s/g' %s", sedCommand, PrivateRegistry, filePath),
|
||||
false, false); err != nil {
|
||||
return errors.Wrap(errors.WithStack(err), fmt.Sprintf("add private registry: %s failed", s.KubeConf.Cluster.Registry.PrivateRegistry))
|
||||
}
|
||||
} else {
|
||||
if _, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("%s '/local_registry/d' %s", sedCommand, filePath), false, false); err != nil {
|
||||
return errors.Wrap(errors.WithStack(err), fmt.Sprintf("remove private registry failed"))
|
||||
}
|
||||
}
|
||||
|
||||
if s.KubeConf.Cluster.Registry.NamespaceOverride != "" {
|
||||
if _, err := runtime.GetRunner().SudoCmd(
|
||||
fmt.Sprintf("%s '/namespace_override/s/\\:.*/\\: %s/g' %s", sedCommand, s.KubeConf.Cluster.Registry.NamespaceOverride, filePath),
|
||||
false, false); err != nil {
|
||||
return errors.Wrap(errors.WithStack(err), fmt.Sprintf("add namespace override: %s failed", s.KubeConf.Cluster.Registry.NamespaceOverride))
|
||||
}
|
||||
} else {
|
||||
if _, err := runtime.GetRunner().SudoCmd(fmt.Sprintf("%s '/namespace_override/d' %s", sedCommand, filePath), false, false); err != nil {
|
||||
return errors.Wrap(errors.WithStack(err), fmt.Sprintf("remove namespace override failed"))
|
||||
}
|
||||
}
|
||||
|
||||
_, ok = kubesphere.CNSource[s.KubeConf.Cluster.KubeSphere.Version]
|
||||
if ok && (os.Getenv("KKZONE") == "cn" || s.KubeConf.Cluster.Registry.PrivateRegistry == "registry.cn-beijing.aliyuncs.com") {
|
||||
if _, err := runtime.GetRunner().SudoCmd(
|
||||
fmt.Sprintf("%s '/zone/s/\\:.*/\\: %s/g' %s", sedCommand, "cn", filePath),
|
||||
false, false); err != nil {
|
||||
return errors.Wrap(errors.WithStack(err), fmt.Sprintf("add kubekey zone: %s failed", s.KubeConf.Cluster.Registry.PrivateRegistry))
|
||||
}
|
||||
} else {
|
||||
if _, err := runtime.GetRunner().SudoCmd(
|
||||
fmt.Sprintf("%s '/zone/d' %s", sedCommand, filePath),
|
||||
false, false); err != nil {
|
||||
return errors.Wrap(errors.WithStack(err), fmt.Sprintf("remove kubekey zone failed"))
|
||||
}
|
||||
}
|
||||
|
||||
switch s.KubeConf.Cluster.Kubernetes.ContainerManager {
|
||||
case "docker", "containerd", "crio":
|
||||
if _, err := runtime.GetRunner().SudoCmd(
|
||||
fmt.Sprintf("%s '/containerruntime/s/\\:.*/\\: %s/g' %s", sedCommand, s.KubeConf.Cluster.Kubernetes.ContainerManager, filePath), false, false); err != nil {
|
||||
return errors.Wrap(errors.WithStack(err), fmt.Sprintf("set container runtime: %s failed", s.KubeConf.Cluster.Kubernetes.ContainerManager))
|
||||
}
|
||||
default:
|
||||
logger.Infof(
|
||||
fmt.Sprintf("%s Currently, the logging module of KubeSphere does not support %s. If %s is used, the logging module will be unavailable.", runtime.RemoteHost().GetName(),
|
||||
s.KubeConf.Cluster.Kubernetes.ContainerManager, s.KubeConf.Cluster.Kubernetes.ContainerManager))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Apply struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
func (a *Apply) Execute(runtime connector.Runtime) error {
|
||||
var kubectlpath, ok = a.PipelineCache.GetMustString(common.CacheCommandKubectlPath)
|
||||
if !ok || kubectlpath == "" {
|
||||
kubectlpath = path.Join(common.BinDir, common.CommandKubectl)
|
||||
}
|
||||
|
||||
filePath := filepath.Join(common.KubeAddonsDir, templates.KsInstaller.Name())
|
||||
// if runtime.GetSystemInfo().IsDarwin() {
|
||||
// filePath = path.Join(common.TmpDir, filePath)
|
||||
// }
|
||||
|
||||
deployKubesphereCmd := fmt.Sprintf("%s apply -f %s --force", kubectlpath, filePath)
|
||||
if _, err := runtime.GetRunner().Cmd(deployKubesphereCmd, false, true); err != nil {
|
||||
return errors.Wrapf(errors.WithStack(err), "deploy %s failed", filePath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type GetKubeCommand struct {
|
||||
common.KubeAction
|
||||
}
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 The KubeSphere Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v2
|
||||
|
||||
type V2 struct {
|
||||
Persistence Persistence `yaml:"persistence"`
|
||||
Common Common `yaml:"common"`
|
||||
Etcd Etcd `yaml:"etcd"`
|
||||
MetricsServerOld MetricsServerOld `yaml:"metrics-server"`
|
||||
MetricsServerNew MetricsServerNew `yaml:"metrics_server"`
|
||||
Console Console `yaml:"console"`
|
||||
Monitoring Monitoring `yaml:"monitoring"`
|
||||
Logging Logging `yaml:"logging"`
|
||||
Openpitrix Openpitrix `yaml:"openpitrix"`
|
||||
Devops Devops `yaml:"devops"`
|
||||
Servicemesh Servicemesh `yaml:"servicemesh"`
|
||||
Notification Notification `yaml:"notification"`
|
||||
Alerting Alerting `yaml:"alerting"`
|
||||
LocalRegistry string `yaml:"local_registry"`
|
||||
}
|
||||
|
||||
type Persistence struct {
|
||||
StorageClass string `yaml:"storageClass"`
|
||||
}
|
||||
type Etcd struct {
|
||||
Monitoring bool `yaml:"monitoring"`
|
||||
EndpointIps string `yaml:"endpointIps"`
|
||||
Port int `yaml:"port"`
|
||||
TlsEnable bool `yaml:"tlsEnable"`
|
||||
}
|
||||
|
||||
type Common struct {
|
||||
MysqlVolumeSize string `yaml:"mysqlVolumeSize"`
|
||||
MinioVolumeSize string `yaml:"minioVolumeSize"`
|
||||
EtcdVolumeSize string `yaml:"etcdVolumeSize"`
|
||||
OpenldapVolumeSize string `yaml:"openldapVolumeSize"`
|
||||
RedisVolumSize string `yaml:"redisVolumSize"`
|
||||
}
|
||||
|
||||
type MetricsServerOld struct {
|
||||
Enabled string `yaml:"enabled"`
|
||||
}
|
||||
|
||||
type MetricsServerNew struct {
|
||||
Enabled string `yaml:"enabled"`
|
||||
}
|
||||
|
||||
type Console struct {
|
||||
EnableMultiLogin bool `yaml:"enableMultiLogin"`
|
||||
Port int `yaml:"port"`
|
||||
}
|
||||
|
||||
type Monitoring struct {
|
||||
PrometheusReplicas int `yaml:"prometheusReplicas"`
|
||||
PrometheusMemoryRequest string `yaml:"prometheusMemoryRequest"`
|
||||
PrometheusVolumeSize string `yaml:"prometheusVolumeSize"`
|
||||
}
|
||||
|
||||
type Logging struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
ElasticsearchMasterReplicas int `yaml:"elasticsearchMasterReplicas"`
|
||||
ElasticsearchDataReplicas int `yaml:"elasticsearchDataReplicas"`
|
||||
LogsidecarReplicas int `yaml:"logsidecarReplicas"`
|
||||
ElasticsearchVolumeSize string `yaml:"elasticsearchVolumeSize"`
|
||||
ElasticsearchMasterVolumeSize string `yaml:"elasticsearchMasterVolumeSize"`
|
||||
ElasticsearchDataVolumeSize string `yaml:"elasticsearchDataVolumeSize"`
|
||||
LogMaxAge int `yaml:"logMaxAge"`
|
||||
ElkPrefix string `yaml:"elkPrefix"`
|
||||
}
|
||||
|
||||
type Openpitrix struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
}
|
||||
|
||||
type Devops struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
JenkinsMemoryLim string `yaml:"jenkinsMemoryLim"`
|
||||
JenkinsMemoryReq string `yaml:"jenkinsMemoryReq"`
|
||||
JenkinsVolumeSize string `yaml:"jenkinsVolumeSize"`
|
||||
JenkinsjavaoptsXms string `yaml:"jenkinsJavaOpts_Xms"`
|
||||
JenkinsjavaoptsXmx string `yaml:"jenkinsJavaOpts_Xmx"`
|
||||
JenkinsjavaoptsMaxram string `yaml:"jenkinsJavaOpts_MaxRAM"`
|
||||
}
|
||||
|
||||
type Servicemesh struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
}
|
||||
|
||||
type Notification struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
}
|
||||
|
||||
type Alerting struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 The KubeSphere Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v3
|
||||
|
||||
type ClusterConfig struct {
|
||||
ApiVersion string `yaml:"apiVersion"`
|
||||
Kind string `yaml:"kind"`
|
||||
Metadata Metadata `yaml:"metadata"`
|
||||
Spec *V3 `yaml:"spec"`
|
||||
}
|
||||
|
||||
type Metadata struct {
|
||||
Name string `yaml:"name"`
|
||||
Namespace string `yaml:"namespace"`
|
||||
Label Label `yaml:"labels"`
|
||||
}
|
||||
|
||||
type Label struct {
|
||||
Version string `yaml:"version"`
|
||||
}
|
||||
|
||||
type V3 struct {
|
||||
Persistence Persistence `yaml:"persistence"`
|
||||
Authentication Authentication `yaml:"authentication"`
|
||||
Common Common `yaml:"common"`
|
||||
Etcd Etcd `yaml:"etcd"`
|
||||
MetricsServer MetricsServer `yaml:"metrics_server"`
|
||||
Console Console `yaml:"console"`
|
||||
Monitoring Monitoring `yaml:"monitoring"`
|
||||
Logging Logging `yaml:"logging"`
|
||||
Openpitrix Openpitrix `yaml:"openpitrix"`
|
||||
Devops Devops `yaml:"devops"`
|
||||
Servicemesh Servicemesh `yaml:"servicemesh"`
|
||||
Notification Notification `yaml:"notification"`
|
||||
Alerting Alerting `yaml:"alerting"`
|
||||
Auditing Auditing `yaml:"auditing"`
|
||||
Events Events `yaml:"events"`
|
||||
Multicluster Multicluster `yaml:"multicluster"`
|
||||
Networkpolicy Networkpolicy `yaml:"networkpolicy"`
|
||||
LocalRegistry string `yaml:"local_registry"`
|
||||
}
|
||||
|
||||
type Persistence struct {
|
||||
StorageClass string `yaml:"storageClass"`
|
||||
}
|
||||
|
||||
type MetricsServer struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
}
|
||||
|
||||
type Authentication struct {
|
||||
JwtSecret string `yaml:"jwtSecret"`
|
||||
}
|
||||
type Etcd struct {
|
||||
Monitoring bool `yaml:"monitoring"`
|
||||
EndpointIps string `yaml:"endpointIps"`
|
||||
Port int `yaml:"port"`
|
||||
TlsEnable bool `yaml:"tlsEnable"`
|
||||
}
|
||||
|
||||
type Common struct {
|
||||
MysqlVolumeSize string `yaml:"mysqlVolumeSize"`
|
||||
MinioVolumeSize string `yaml:"minioVolumeSize"`
|
||||
EtcdVolumeSize string `yaml:"etcdVolumeSize"`
|
||||
OpenldapVolumeSize string `yaml:"openldapVolumeSize"`
|
||||
RedisVolumSize string `yaml:"redisVolumSize"`
|
||||
ES ES `yaml:"es"`
|
||||
}
|
||||
|
||||
type ES struct {
|
||||
//ElasticsearchMasterReplicas int `yaml:"elasticsearchMasterReplicas"`
|
||||
//ElasticsearchDataReplicas int `yaml:"elasticsearchDataReplicas"`
|
||||
ElasticsearchMasterVolumeSize string `yaml:"elasticsearchMasterVolumeSize"`
|
||||
ElasticsearchDataVolumeSize string `yaml:"elasticsearchDataVolumeSize"`
|
||||
LogMaxAge int `yaml:"logMaxAge"`
|
||||
ElkPrefix string `yaml:"elkPrefix"`
|
||||
}
|
||||
|
||||
type Console struct {
|
||||
EnableMultiLogin bool `yaml:"enableMultiLogin"`
|
||||
Port int `yaml:"port"`
|
||||
}
|
||||
|
||||
type Alerting struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
}
|
||||
|
||||
type Auditing struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
}
|
||||
|
||||
type Devops struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
JenkinsMemoryLim string `yaml:"jenkinsMemoryLim"`
|
||||
JenkinsMemoryReq string `yaml:"jenkinsMemoryReq"`
|
||||
JenkinsVolumeSize string `yaml:"jenkinsVolumeSize"`
|
||||
JenkinsjavaoptsXms string `yaml:"jenkinsJavaOpts_Xms"`
|
||||
JenkinsjavaoptsXmx string `yaml:"jenkinsJavaOpts_Xmx"`
|
||||
JenkinsjavaoptsMaxram string `yaml:"jenkinsJavaOpts_MaxRAM"`
|
||||
}
|
||||
|
||||
type Events struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Ruler Ruler `yaml:"ruler"`
|
||||
}
|
||||
|
||||
type Ruler struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Replicas int `yaml:"replicas"`
|
||||
}
|
||||
|
||||
type Logging struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
LogsidecarReplicas int `yaml:"logsidecarReplicas"`
|
||||
}
|
||||
|
||||
type Metrics struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
}
|
||||
|
||||
type Monitoring struct {
|
||||
//AlertmanagerReplicas int `yaml:"alertmanagerReplicas"`
|
||||
//PrometheusReplicas int `yaml:"prometheusReplicas"`
|
||||
PrometheusMemoryRequest string `yaml:"prometheusMemoryRequest"`
|
||||
PrometheusVolumeSize string `yaml:"prometheusVolumeSize"`
|
||||
}
|
||||
|
||||
type Multicluster struct {
|
||||
ClusterRole string `yaml:"clusterRole"`
|
||||
}
|
||||
|
||||
type Networkpolicy struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
}
|
||||
|
||||
type Notification struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
}
|
||||
|
||||
type Openpitrix struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
}
|
||||
|
||||
type Servicemesh struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
}
|
||||
@@ -37,12 +37,7 @@ func NewDarwinClusterPhase(runtime *common.KubeRuntime, manifestMap manifest.Ins
|
||||
},
|
||||
&kubesphere.DeployMiniKubeModule{},
|
||||
&kubesphere.DeployModule{Skip: !runtime.Cluster.KubeSphere.Enabled},
|
||||
&ksplugins.DeployKsPluginsModule{},
|
||||
//&ksplugins.DeployRedisModule{},
|
||||
&ksplugins.CreateKubeSphereSecretModule{},
|
||||
&ksplugins.DeployKsCoreConfigModule{}, // ks-core-config
|
||||
&ksplugins.CreateMonitorDashboardModule{},
|
||||
//&ksplugins.CreateNotificationModule{},
|
||||
&ksplugins.DeployPrometheusModule{},
|
||||
&ksplugins.DeployKsCoreModule{},
|
||||
&kubesphere.CheckResultModule{Skip: !runtime.Cluster.KubeSphere.Enabled},
|
||||
@@ -94,13 +89,8 @@ func NewK3sCreateClusterPhase(runtime *common.KubeRuntime, manifestMap manifest.
|
||||
&certs.AutoRenewCertsModule{Skip: !runtime.Cluster.Kubernetes.EnableAutoRenewCerts()},
|
||||
&k3s.SaveKubeConfigModule{},
|
||||
&storage.DeployLocalVolumeModule{Skip: skipLocalStorage},
|
||||
&kubesphere.DeployModule{Skip: !runtime.Cluster.KubeSphere.Enabled}, //
|
||||
&ksplugins.DeployKsPluginsModule{},
|
||||
//&ksplugins.DeployRedisModule{},
|
||||
&ksplugins.CreateKubeSphereSecretModule{},
|
||||
&ksplugins.DeployKsCoreConfigModule{}, // ks-core-config
|
||||
&ksplugins.CreateMonitorDashboardModule{},
|
||||
//&ksplugins.CreateNotificationModule{},
|
||||
&kubesphere.DeployModule{Skip: !runtime.Cluster.KubeSphere.Enabled},
|
||||
&ksplugins.DeployKsCoreConfigModule{},
|
||||
&ksplugins.DeployPrometheusModule{},
|
||||
&ksplugins.DeployKsCoreModule{},
|
||||
&kubesphere.CheckResultModule{Skip: !runtime.Cluster.KubeSphere.Enabled},
|
||||
@@ -157,12 +147,7 @@ func NewCreateClusterPhase(runtime *common.KubeRuntime, manifestMap manifest.Ins
|
||||
&kubernetes.SaveKubeConfigModule{},
|
||||
&storage.DeployLocalVolumeModule{Skip: skipLocalStorage},
|
||||
&kubesphere.DeployModule{Skip: !runtime.Cluster.KubeSphere.Enabled},
|
||||
&ksplugins.DeployKsPluginsModule{},
|
||||
//&ksplugins.DeployRedisModule{},
|
||||
&ksplugins.CreateKubeSphereSecretModule{},
|
||||
&ksplugins.DeployKsCoreConfigModule{}, // ! ks-core-config
|
||||
&ksplugins.CreateMonitorDashboardModule{},
|
||||
//&ksplugins.CreateNotificationModule{},
|
||||
&ksplugins.DeployPrometheusModule{},
|
||||
&ksplugins.DeployKsCoreModule{},
|
||||
&kubesphere.CheckResultModule{Skip: !runtime.Cluster.KubeSphere.Enabled}, // check ks-apiserver phase
|
||||
|
||||
@@ -25,7 +25,6 @@ func CliInstallTerminusPipeline(opts *options.CliTerminusInstallOptions) error {
|
||||
arg.SetOlaresVersion(opts.Version)
|
||||
arg.SetMinikubeProfile(opts.MiniKubeProfile)
|
||||
arg.SetStorage(getStorageValueFromEnv())
|
||||
arg.SetTokenMaxAge()
|
||||
arg.SetSwapConfig(opts.SwapConfig)
|
||||
if err := arg.SwapConfig.Validate(); err != nil {
|
||||
return err
|
||||
|
||||
@@ -35,7 +35,6 @@ func PrepareSystemPipeline(opts *options.CliPrepareSystemOptions, components []s
|
||||
arg.SetOlaresVersion(opts.Version)
|
||||
arg.SetRegistryMirrors(opts.RegistryMirrors)
|
||||
arg.SetStorage(getStorageValueFromEnv())
|
||||
arg.SetTokenMaxAge()
|
||||
|
||||
runtime, err := common.NewKubeRuntime(common.AllInOne, *arg)
|
||||
if err != nil {
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/beclab/Olares/cli/pkg/core/logger"
|
||||
"github.com/beclab/Olares/cli/pkg/storage"
|
||||
|
||||
"github.com/beclab/Olares/cli/pkg/clientset"
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
cc "github.com/beclab/Olares/cli/pkg/core/common"
|
||||
"github.com/beclab/Olares/cli/pkg/core/connector"
|
||||
@@ -235,50 +234,6 @@ func (p *Patch) Execute(runtime connector.Runtime) error {
|
||||
return errors.Wrap(errors.WithStack(err), "patch globalrole workspace manager failed")
|
||||
}
|
||||
|
||||
//var notificationManager = path.Join(runtime.GetInstallerDir(), "deploy", "patch-notification-manager.yaml")
|
||||
//if _, err = runtime.GetRunner().SudoCmd(fmt.Sprintf("%s apply -f %s", kubectl, notificationManager), false, true); err != nil {
|
||||
// return errors.Wrap(errors.WithStack(err), "patch notification manager failed")
|
||||
//}
|
||||
//var notificationManager = path.Join(runtime.GetInstallerDir(), "deploy", "patch-notification-manager.yaml")
|
||||
//if _, err = runtime.GetRunner().Host.SudoCmd(fmt.Sprintf("%s apply -f %s", kubectl, notificationManager), false, true); err != nil {
|
||||
// return errors.Wrap(errors.WithStack(err), "patch notification manager failed")
|
||||
//}
|
||||
//
|
||||
//patchAdminContent := `{"metadata":{"finalizers":["finalizers.kubesphere.io/users"]}}`
|
||||
//patchAdminCMD := fmt.Sprintf(
|
||||
// "%s patch user admin -p '%s' --type='merge' ",
|
||||
// kubectl,
|
||||
// patchAdminContent)
|
||||
//_, err = runtime.GetRunner().SudoCmd(patchAdminCMD, false, true)
|
||||
//if err != nil {
|
||||
// return errors.Wrap(errors.WithStack(err), "patch user admin failed")
|
||||
//}
|
||||
//patchAdminContent := "{\\\"metadata\\\":{\\\"finalizers\\\":[\\\"finalizers.kubesphere.io/users\\\"]}}"
|
||||
//patchAdminCMD := fmt.Sprintf(
|
||||
// "%s patch user admin -p '%s' --type='merge' ",
|
||||
// kubectl,
|
||||
// patchAdminContent)
|
||||
//_, err = runtime.GetRunner().Host.SudoCmd(patchAdminCMD, false, true)
|
||||
//if err != nil {
|
||||
// return errors.Wrap(errors.WithStack(err), "patch user admin failed")
|
||||
//}
|
||||
|
||||
//deleteAdminCMD := fmt.Sprintf("%s delete user admin --ignore-not-found", kubectl)
|
||||
//_, err = runtime.GetRunner().SudoCmd(deleteAdminCMD, false, true)
|
||||
//if err != nil {
|
||||
// return errors.Wrap(errors.WithStack(err), "failed to delete ks admin user")
|
||||
//}
|
||||
deleteKubectlAdminCMD := fmt.Sprintf("%s -n kubesphere-controls-system delete deploy kubectl-admin --ignore-not-found", kubectl)
|
||||
_, err = runtime.GetRunner().SudoCmd(deleteKubectlAdminCMD, false, true)
|
||||
if err != nil {
|
||||
return errors.Wrap(errors.WithStack(err), "failed to delete ks kubectl admin deployment")
|
||||
}
|
||||
deleteHTTPBackendCMD := fmt.Sprintf("%s -n kubesphere-controls-system delete deploy default-http-backend --ignore-not-found", kubectl)
|
||||
_, err = runtime.GetRunner().SudoCmd(deleteHTTPBackendCMD, false, true)
|
||||
if err != nil {
|
||||
return errors.Wrap(errors.WithStack(err), "failed to delete ks default http backend")
|
||||
}
|
||||
|
||||
patchFelixConfigContent := `{"spec":{"featureDetectOverride": "SNATFullyRandom=false,MASQFullyRandom=false"}}`
|
||||
patchFelixConfigCMD := fmt.Sprintf(
|
||||
"%s patch felixconfiguration default -p '%s' --type='merge'",
|
||||
@@ -466,19 +421,6 @@ func cloudValue(cloudInstance bool) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func getRedisPassword(client clientset.Client, runtime connector.Runtime) (string, error) {
|
||||
secret, err := client.Kubernetes().CoreV1().Secrets(common.NamespaceKubesphereSystem).Get(context.Background(), "redis-secret", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return "", errors.Wrap(errors.WithStack(err), "get redis secret failed")
|
||||
}
|
||||
if secret == nil || secret.Data == nil || secret.Data["auth"] == nil {
|
||||
return "", fmt.Errorf("redis secret not found")
|
||||
}
|
||||
|
||||
return string(secret.Data["auth"]), nil
|
||||
|
||||
}
|
||||
|
||||
type UserEnvConfig struct {
|
||||
APIVersion string `yaml:"apiVersion"`
|
||||
UserEnvs []v1alpha1.EnvVarSpec `yaml:"userEnvs"`
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package terminus
|
||||
|
||||
import (
|
||||
"github.com/beclab/Olares/cli/pkg/common"
|
||||
"github.com/beclab/Olares/cli/pkg/core/connector"
|
||||
)
|
||||
|
||||
type NotEqualDesiredVersion struct {
|
||||
common.KubePrepare
|
||||
}
|
||||
|
||||
func (n *NotEqualDesiredVersion) PreCheck(runtime connector.Runtime) (bool, error) {
|
||||
ksVersion, ok := n.PipelineCache.GetMustString(common.KubeSphereVersion)
|
||||
if !ok {
|
||||
ksVersion = ""
|
||||
}
|
||||
|
||||
if n.KubeConf.Cluster.KubeSphere.Version == ksVersion {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@@ -738,6 +738,19 @@ func (p *DetectPublicIPAddress) Execute(runtime connector.Runtime) error {
|
||||
}
|
||||
}
|
||||
|
||||
if util.IsOnAliyunECS() {
|
||||
logger.Info("on Aliyun ECS instance, will try to check if a public IP address is bound")
|
||||
aliyunPublicIP, err := util.GetPublicIPFromAliyunIMDS()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get public IP from Aliyun")
|
||||
}
|
||||
if aliyunPublicIP != nil {
|
||||
logger.Info("retrieved public IP addresses from IMDS")
|
||||
p.KubeConf.Arg.NetworkSettings.CloudProviderPublicIP = aliyunPublicIP
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
osPublicIPs, err := util.GetPublicIPsFromOS()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get public IPs from OS")
|
||||
|
||||
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
|
||||
}
|
||||
@@ -71,11 +71,10 @@ func upgradeKSCore() []task.Interface {
|
||||
Action: new(plugins.CopyEmbedFiles),
|
||||
},
|
||||
&task.LocalTask{
|
||||
Name: "UpgradeKSCore",
|
||||
Prepare: new(common.GetMasterNum),
|
||||
Action: new(plugins.CreateKsCore),
|
||||
Retry: 10,
|
||||
Delay: 10 * time.Second,
|
||||
Name: "UpgradeKSCore",
|
||||
Action: new(plugins.CreateKsCore),
|
||||
Retry: 10,
|
||||
Delay: 10 * time.Second,
|
||||
},
|
||||
&task.LocalTask{
|
||||
Name: "CheckKSCoreRunning",
|
||||
|
||||
@@ -192,7 +192,8 @@ func CheckJWS(jws string, duration int64) (*CheckJWSResult, error) {
|
||||
|
||||
// Check timestamp
|
||||
now := time.Now().UnixMilli()
|
||||
if now-timestamp > duration {
|
||||
diff := now - timestamp
|
||||
if max(diff, -diff) > duration {
|
||||
return nil, fmt.Errorf("timestamp is out of range")
|
||||
}
|
||||
|
||||
|
||||
@@ -488,7 +488,6 @@ func (i *InstallTerminus) Execute(runtime connector.Runtime) error {
|
||||
var envs = []string{
|
||||
fmt.Sprintf("export %s=%s", common.ENV_KUBE_TYPE, i.KubeConf.Arg.Kubetype),
|
||||
fmt.Sprintf("export %s=%s", common.ENV_REGISTRY_MIRRORS, i.KubeConf.Arg.RegistryMirrors),
|
||||
fmt.Sprintf("export %s=%d", common.ENV_TOKEN_MAX_AGE, i.KubeConf.Arg.TokenMaxAge),
|
||||
fmt.Sprintf("export %s=%s", common.ENV_PREINSTALL, os.Getenv(common.ENV_PREINSTALL)),
|
||||
fmt.Sprintf("export %s=%s", common.ENV_HOST_IP, systemInfo.GetLocalIp()),
|
||||
fmt.Sprintf("export %s=%s", common.ENV_DISABLE_HOST_IP_PROMPT, os.Getenv(common.ENV_DISABLE_HOST_IP_PROMPT)),
|
||||
|
||||
@@ -30,7 +30,6 @@ type proxyServer struct {
|
||||
|
||||
func NewProxyServer() (*proxyServer, error) {
|
||||
p := &proxyServer{
|
||||
proxy: echo.New(),
|
||||
dnsServer: "10.233.0.3:53", // default k8s dns service
|
||||
}
|
||||
return p, nil
|
||||
@@ -38,6 +37,18 @@ func NewProxyServer() (*proxyServer, error) {
|
||||
|
||||
func (p *proxyServer) Start() error {
|
||||
klog.Info("Starting intranet proxy server...")
|
||||
if p.proxy != nil {
|
||||
err := p.proxy.Close()
|
||||
if err != nil {
|
||||
klog.Error("close intranet proxy server error, ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
p.proxy = nil
|
||||
}
|
||||
|
||||
// closed echo proxy server cannot be restarted, so create a new one
|
||||
p.proxy = echo.New()
|
||||
config := middleware.DefaultProxyConfig
|
||||
config.Balancer = p
|
||||
config.Transport = p.initTransport()
|
||||
@@ -109,8 +120,12 @@ func (p *proxyServer) Start() error {
|
||||
|
||||
func (p *proxyServer) Close() error {
|
||||
if p.proxy != nil {
|
||||
return p.proxy.Close()
|
||||
err := p.proxy.Close()
|
||||
if err != nil {
|
||||
klog.Error("close intranet proxy server error, ", err)
|
||||
}
|
||||
}
|
||||
p.proxy = nil
|
||||
p.stopped = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,12 +5,14 @@ import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/beclab/Olares/daemon/internel/intranet"
|
||||
"github.com/beclab/Olares/daemon/internel/watcher"
|
||||
"github.com/beclab/Olares/daemon/pkg/cluster/state"
|
||||
"github.com/beclab/Olares/daemon/pkg/nets"
|
||||
"github.com/beclab/Olares/daemon/pkg/utils"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/vishvananda/netlink"
|
||||
"golang.org/x/sys/unix"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -181,6 +183,9 @@ func (w *applicationWatcher) loadServerConfig(ctx context.Context, nodeIp string
|
||||
return options, nil
|
||||
}
|
||||
|
||||
var adguardDnsPodIp string
|
||||
var adguardHealth bool
|
||||
|
||||
func (w *applicationWatcher) loadDnsPodConfig(ctx context.Context, o *intranet.ServerOptions) error {
|
||||
// try to find adguard dns pod ip and mac
|
||||
k8sClient, err := utils.GetKubeClient()
|
||||
@@ -199,7 +204,36 @@ func (w *applicationWatcher) loadDnsPodConfig(ctx context.Context, o *intranet.S
|
||||
const adguardDnsAppLabel = "applications.app.bytetrade.io/name"
|
||||
for _, pod := range dnsPods.Items {
|
||||
switch {
|
||||
case pod.Labels[adguardDnsAppLabel] == "adguardhome", pod.Labels["k8s-app"] == "kube-dns":
|
||||
case pod.Labels[adguardDnsAppLabel] == "adguardhome":
|
||||
dnsPodIp = pod.Status.PodIP
|
||||
|
||||
// try to connect adguard dns pod port 53 to verify it's running
|
||||
if adguardDnsPodIp != dnsPodIp || !adguardHealth {
|
||||
adguardDnsPodIp = dnsPodIp
|
||||
err := checkHealth(dnsPodIp)
|
||||
if err != nil {
|
||||
klog.Warning("dial adguard dns pod tcp 53 error, ", err)
|
||||
adguardHealth = false
|
||||
} else {
|
||||
adguardHealth = true
|
||||
}
|
||||
}
|
||||
|
||||
if adguardHealth {
|
||||
dnsPodMac, calicoRouteIface, err = getPodNeighborInfo(dnsPodIp)
|
||||
if err != nil {
|
||||
klog.Error("get adguard dns pod mac by ip error, ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// found adguard dns pod
|
||||
o.DnsPodIp = dnsPodIp
|
||||
o.DnsPodMac = dnsPodMac
|
||||
o.DnsPodCalicoIface = calicoRouteIface
|
||||
return nil
|
||||
}
|
||||
|
||||
case pod.Labels["k8s-app"] == "kube-dns":
|
||||
dnsPodIp = pod.Status.PodIP
|
||||
dnsPodMac, calicoRouteIface, err = getPodNeighborInfo(dnsPodIp)
|
||||
if err != nil {
|
||||
@@ -208,13 +242,7 @@ func (w *applicationWatcher) loadDnsPodConfig(ctx context.Context, o *intranet.S
|
||||
}
|
||||
}
|
||||
|
||||
if pod.Labels[adguardDnsAppLabel] == "adguardhome" {
|
||||
o.DnsPodIp = dnsPodIp
|
||||
o.DnsPodMac = dnsPodMac
|
||||
o.DnsPodCalicoIface = calicoRouteIface
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} // end for pods
|
||||
|
||||
// not found adguard dns pod, but core dns pod exists
|
||||
if dnsPodIp != "" {
|
||||
@@ -261,3 +289,15 @@ func getPodNeighborInfo(podIp string) (mac, iface string, err error) {
|
||||
|
||||
return "", "", fmt.Errorf("not found pod neighbor info for ip %s", podIp)
|
||||
}
|
||||
|
||||
func checkHealth(server string) error {
|
||||
c := new(dns.Client)
|
||||
c.Timeout = time.Second
|
||||
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(dns.Fqdn("coredns.kube-system.svc.cluster.local."), dns.TypeA)
|
||||
msg.RecursionDesired = true
|
||||
|
||||
_, _, err := c.Exchange(msg, server+":53")
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ func NewUmountWatcher() *umountWatcher {
|
||||
}
|
||||
|
||||
func (w *umountWatcher) Watch(ctx context.Context) {
|
||||
if err := utils.UmountBrokenUsbMount(ctx, commands.MOUNT_BASE_DIR); err != nil {
|
||||
if err := utils.UmountBrokenMount(ctx, commands.MOUNT_BASE_DIR); err != nil {
|
||||
klog.Error("umount broken mount point error, ", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -383,17 +383,7 @@ func UmountUsbDevice(ctx context.Context, path string) error {
|
||||
return errors.New("not a mounted usb path")
|
||||
}
|
||||
|
||||
func UmountBrokenUsbMount(ctx context.Context, baseDir string) error {
|
||||
usbdevs, err := DetectdUsbDevices(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hdddevs, err := DetectdHddDevices(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func UmountBrokenMount(ctx context.Context, baseDir string) error {
|
||||
mounter := mountutils.New("")
|
||||
list, err := mounter.List()
|
||||
if err != nil {
|
||||
@@ -403,16 +393,9 @@ func UmountBrokenUsbMount(ctx context.Context, baseDir string) error {
|
||||
|
||||
for _, m := range list {
|
||||
if strings.HasPrefix(m.Path, baseDir) && !strings.HasPrefix(m.Path, path.Join(baseDir, "ai")) {
|
||||
if !slices.ContainsFunc(usbdevs, func(u storageDevice) bool { return u.DevPath == m.Device }) &&
|
||||
!slices.ContainsFunc(hdddevs, func(u storageDevice) bool { return u.DevPath == m.Device }) {
|
||||
if r := checkMount(m.Path, time.Second); r.Broken {
|
||||
|
||||
if m.Type == "cifs" {
|
||||
if broken, _ := cifsBroken(&m); !broken {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
klog.Infof("broken mountpoint: %v, %v, %v", m.Path, m.Device, usbdevs)
|
||||
klog.Infof("broken mountpoint: %v, %v, %v", m.Path, m.Device, r.Reason)
|
||||
|
||||
if err = umountAndRemovePath(ctx, m.Path); err != nil {
|
||||
return err
|
||||
@@ -608,3 +591,71 @@ func MountedPath(ctx context.Context) ([]mountedPath, error) {
|
||||
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
type result struct {
|
||||
Mount string `json:"mount"`
|
||||
Broken bool `json:"broken"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
Elapsed string `json:"elapsed,omitempty"`
|
||||
}
|
||||
|
||||
func checkMount(mountPoint string, timeout time.Duration) result {
|
||||
start := time.Now()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
|
||||
// Use /usr/bin/stat if exists, else fallback to ls -ld
|
||||
statPath := "/usr/bin/stat"
|
||||
if _, err := os.Stat(statPath); os.IsNotExist(err) {
|
||||
statPath = "/bin/ls"
|
||||
}
|
||||
|
||||
var cmd *exec.Cmd
|
||||
// if stat exists, call stat <mountpoint>, else ls -ld <mountpoint>
|
||||
if strings.HasSuffix(statPath, "stat") {
|
||||
cmd = exec.CommandContext(ctx, statPath, mountPoint)
|
||||
} else {
|
||||
cmd = exec.CommandContext(ctx, statPath, "-ld", mountPoint)
|
||||
}
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
elapsed := time.Since(start)
|
||||
|
||||
res := result{
|
||||
Mount: mountPoint,
|
||||
Broken: false,
|
||||
Reason: "",
|
||||
Elapsed: elapsed.String(),
|
||||
}
|
||||
|
||||
if ctx.Err() == context.DeadlineExceeded {
|
||||
res.Broken = true
|
||||
res.Reason = "timeout"
|
||||
return res
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// check output or error for common broken indicators
|
||||
outStr := strings.ToLower(string(out) + " " + err.Error())
|
||||
switch {
|
||||
case strings.Contains(outStr, "stale"):
|
||||
res.Broken = true
|
||||
res.Reason = "stale file handle"
|
||||
case strings.Contains(outStr, "input/output error") || strings.Contains(outStr, "i/o error"):
|
||||
res.Broken = true
|
||||
res.Reason = "input/output error"
|
||||
case strings.Contains(outStr, "transport endpoint is not connected"):
|
||||
res.Broken = true
|
||||
res.Reason = "transport endpoint not connected"
|
||||
case strings.Contains(outStr, "permission denied"):
|
||||
// permission denied doesn't mean broken; mark as not broken but note reason
|
||||
res.Broken = false
|
||||
res.Reason = "permission denied"
|
||||
default:
|
||||
// Unknown error - mark as broken (conservative), include text
|
||||
res.Broken = true
|
||||
res.Reason = "error: " + strings.TrimSpace(outStr)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func UmountUsbDevice(ctx context.Context, path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func UmountBrokenUsbMount(ctx context.Context, baseDir string) error {
|
||||
func UmountBrokenMount(ctx context.Context, baseDir string) error {
|
||||
klog.Warning("not implement")
|
||||
return 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,
|
||||
@@ -340,29 +344,6 @@ const side = {
|
||||
},
|
||||
{text: "Dashboard", link: "/manual/olares/resources-usage"},
|
||||
{text: "Profile", link: "/manual/olares/profile"},
|
||||
{
|
||||
text: "Studio",
|
||||
collapsed: true,
|
||||
link: "/manual/olares/studio/",
|
||||
items: [
|
||||
{
|
||||
text: "Deploy an app",
|
||||
link: "/manual/olares/studio/deploy",
|
||||
},
|
||||
{
|
||||
text: "Develop in a dev container",
|
||||
link: "/manual/olares/studio/develop",
|
||||
},
|
||||
{
|
||||
text: "Package and upload",
|
||||
link: "/manual/olares/studio/package-upload",
|
||||
},
|
||||
{
|
||||
text: "Add app assets",
|
||||
link: "/manual/olares/studio/assets",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -647,36 +628,29 @@ const side = {
|
||||
],
|
||||
},
|
||||
{
|
||||
text: "Develop Olares app",
|
||||
text: "Develop Olares apps",
|
||||
link: "/developer/develop/",
|
||||
items: [
|
||||
{
|
||||
text: "Tutorial",
|
||||
text: "Develop with Studio",
|
||||
collapsed: true,
|
||||
link: "/developer/develop/tutorial/",
|
||||
items: [
|
||||
{
|
||||
text: "Learn Studio",
|
||||
link: "/developer/develop/tutorial/studio",
|
||||
text: "Deploy an app",
|
||||
link: "/developer/develop/tutorial/deploy",
|
||||
},
|
||||
{
|
||||
text: "Create your first app",
|
||||
collapsed: true,
|
||||
link: "/developer/develop/tutorial/note/",
|
||||
items: [
|
||||
{
|
||||
text: "1. Create app",
|
||||
link: "/developer/develop/tutorial/note/create",
|
||||
},
|
||||
{
|
||||
text: "2. Develop backend",
|
||||
link: "/developer/develop/tutorial/note/backend",
|
||||
},
|
||||
{
|
||||
text: "3. Develop frontend",
|
||||
link: "/developer/develop/tutorial/note/frontend",
|
||||
},
|
||||
],
|
||||
text: "Develop in a dev container",
|
||||
link: "/developer/develop/tutorial/develop",
|
||||
},
|
||||
{
|
||||
text: "Package and upload",
|
||||
link: "/developer/develop/tutorial/package-upload",
|
||||
},
|
||||
{
|
||||
text: "Add app assets",
|
||||
link: "/developer/develop/tutorial/assets",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -13,6 +13,7 @@ import mediumZoom from "medium-zoom";
|
||||
import OSTabs from "./components/OStabs.vue";
|
||||
import VersionSwitcher from "./components/VersionSwitcher.vue";
|
||||
import _ from "lodash";
|
||||
import { redirects } from './redirects';
|
||||
|
||||
const LANGUAGE_LOCAL_KEY = "language";
|
||||
let isMenuChange = false;
|
||||
@@ -20,14 +21,27 @@ let isMenuChange = false;
|
||||
export default {
|
||||
extends: DefaultTheme,
|
||||
Layout,
|
||||
enhanceApp({ app }: { app: App }) {
|
||||
enhanceApp({ app, router }: { app: App; router: Router }) {
|
||||
app.component("Tabs", Tabs);
|
||||
app.component("LaunchCard", LaunchCard);
|
||||
app.component("FilterableList", FilterableList);
|
||||
app.component("OSTabs", OSTabs);
|
||||
app.component("VersionSwitcher", VersionSwitcher);
|
||||
|
||||
router.onBeforeRouteChange = (to: string) => {
|
||||
const path = to.replace(/\.html$/i, ''),
|
||||
toPath = redirects[path];
|
||||
|
||||
if (toPath) {
|
||||
setTimeout(() => { router.go(toPath); })
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
setup() {
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
12
docs/.vitepress/theme/redirects.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export const redirects = {
|
||||
// Refactor studio
|
||||
|
||||
// index page
|
||||
'/manual/olares/studio/': '/developer/develop/tutorial/',
|
||||
'/manual/olares/studio/deploy': '/developer/develop/tutorial/deploy',
|
||||
'/manual/olares/studio/develop': '/developer/develop/tutorial/develop',
|
||||
'/manual/olares/studio/package-upload': '/developer/develop/tutorial/package-upload',
|
||||
'/manual/olares/studio/assets': '/developer/develop/tutorial/assets',
|
||||
'/developer/develop/tutorial/studio': '/developer/develop/tutorial',
|
||||
'/zh/developer/develop/tutorial/studio': '/zh/developer/develop/tutorial',
|
||||
}
|
||||
@@ -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,
|
||||
@@ -616,32 +620,25 @@ const side = {
|
||||
link: "/zh/developer/develop/",
|
||||
items: [
|
||||
{
|
||||
text: "教程",
|
||||
text: "使用 Studio 开发",
|
||||
collapsed: true,
|
||||
link: "/zh/developer/develop/tutorial/",
|
||||
items: [
|
||||
{
|
||||
text: "了解 Studio",
|
||||
link: "/zh/developer/develop/tutorial/studio",
|
||||
text: "部署应用",
|
||||
link: "/zh/developer/develop/tutorial/deploy",
|
||||
},
|
||||
{
|
||||
text: "创建首个应用",
|
||||
collapsed: true,
|
||||
link: "/zh/developer/develop/tutorial/note/",
|
||||
items: [
|
||||
{
|
||||
text: "1. 创建应用",
|
||||
link: "/zh/developer/develop/tutorial/note/create",
|
||||
},
|
||||
{
|
||||
text: "2. 开发后端",
|
||||
link: "/zh/developer/develop/tutorial/note/backend",
|
||||
},
|
||||
{
|
||||
text: "3. 开发前端",
|
||||
link: "/zh/developer/develop/tutorial/note/frontend",
|
||||
},
|
||||
],
|
||||
text: "使用开发容器",
|
||||
link: "/zh/developer/develop/tutorial/develop",
|
||||
},
|
||||
{
|
||||
text: "打包与上传",
|
||||
link: "/zh/developer/develop/tutorial/package-upload",
|
||||
},
|
||||
{
|
||||
text: "添加应用素材",
|
||||
link: "/zh/developer/develop/tutorial/assets",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,15 +1,30 @@
|
||||
# Develop Olares application
|
||||
# Develop Olares applications
|
||||
|
||||
Developing applications on Olares is not much different from regular website development. Once you learn a few basic Olares concepts, you can start creating applications on his platform.
|
||||
Developing applications on Olares leverages standard web technologies and containerization. If you are familiar with building web applications or Docker containers, you already have the skills needed to build for Olares.
|
||||
|
||||
- [Core Concepts of Olares](../concepts/index.md)
|
||||
- [Understanding the Format of Olares Application Chart](./package/chart.md)
|
||||
- [The structure of the Olares Application Chart](./package/chart.md)
|
||||
- [Configuration guide and field descriptions of `OlaresManifest.yaml`](./package/manifest.md)
|
||||
- [Extensions field to Helm in Olares](./package/extension.md)
|
||||
This guide takes you through the complete lifecycle of an Olares application, from your first line of code in Studio to publishing on the Market.
|
||||
|
||||
- [Exploring Our Tutorials](./tutorial/)
|
||||
- [Learn about Studio, an Olares Development Tool](./tutorial/studio)
|
||||
- [Creating your first application](./tutorial/note/)
|
||||
- [Exploring Advanced Concepts](./advanced/)
|
||||
- [Submitting Applications to the Olares Market](./submit/)
|
||||
## Before you begin
|
||||
Before getting started, it's helpful to review some concepts:
|
||||
- [Application](../concepts/application.md)
|
||||
- [Network](../concepts/network.md)
|
||||
|
||||
## Step 1: Develop with Studio
|
||||
Olares Studio is a development platform that accelerates your build cycle. It provides a pre-configured workspace to build, debug, and test your applications directly on the platform.
|
||||
|
||||
* **[Deploy an app](./tutorial/deploy.md)**: Learn how to quickly deploy an app from an existing Docker image, configure it, and test it in Studio.
|
||||
* **[Develop in a dev container](./tutorial/develop.md)**: Spin up a remote development environment (Dev Container) and connect it to VS Code for a seamless coding experience.
|
||||
* **[Package and upload](./tutorial/package-upload.md)**: Convert your running application into an Olares-compatible package and upload it for testing.
|
||||
* **[Add app assets](./tutorial/assets.md)**: Configure icons, screenshots, and descriptions to make your application store-ready.
|
||||
|
||||
## Step 2: Package your application
|
||||
To publish your application to the Olares Market, you must structure it according to the Olares Application Chart (OAC) specification. This format extends Helm Charts to support Olares-specific features like permission management and sandboxing.
|
||||
|
||||
* **[Understand the Olares Application Chart](./package/chart.md)**: Understand the file structure and requirements of an application package.
|
||||
* **[Understand `OlaresManifest.yaml`](./package/manifest.md)**: A comprehensive guide to the `OlaresManifest.yaml` file, which defines your app's metadata, permissions, and system integration points.
|
||||
* **[Understand Helm extensions](./package/extension.md)**: Learn about the custom fields and capabilities Olares adds to standard Helm deployments.
|
||||
|
||||
## Step 3: Submit your application
|
||||
Once your application is built and packaged, the final step is to share it with the Olares community.
|
||||
|
||||
* **[Submit to Market](./submit/index.md)**: Learn how to submit your application to the Olares Market for review and distribution.
|
||||
@@ -17,8 +17,8 @@ outline: [2, 3]
|
||||
### 1. Develop and test your application
|
||||
|
||||
Before submitting an application, please ensure that it has been thoroughly tested on your Olares.
|
||||
- Use DevBox's dev-container to test and debug your application in a real online environment. [Learn more about DevBox](../tutorial/studio).
|
||||
- Use the [custom installation](/manual/olares/market.md#install-custom-applications) in the Market app for user testing.
|
||||
- Use Studio's dev-container to test and debug your application in a real online environment. [Learn more about Studio](../tutorial/).
|
||||
- Use the [custom installation](../tutorial/package-upload.md) in the Market app for user testing.
|
||||
|
||||
### 2. Submit an application
|
||||
The submission of the application needs to be completed through a **Pull Request**. Here's how:
|
||||
|
||||
@@ -6,7 +6,7 @@ description: Deploy a single-container Docker app to Olares using Studio.
|
||||
This guide explains how to deploy a single-container Docker app to Olares using Studio.
|
||||
|
||||
:::info For single-container apps
|
||||
This method supports apps that run from a single container image. For multi-container apps (for example, a web service plus a separate database), use the workflow in the [developer documentation](../../../developer/develop/tutorial/index.md) instead.
|
||||
This method supports apps that run from a single container image.
|
||||
:::
|
||||
:::tip Recommended for testing
|
||||
Studio-created deployments are best suited for development, testing, or temporary use. Upgrades and long-term data persistence can be limited compared to installing a packaged app from the Market. For production use, consider [packaging and uploading the app](package-upload.md) and installing it via the Market.
|
||||
@@ -44,7 +44,6 @@ services:
|
||||
- "8282:80/tcp"
|
||||
environment:
|
||||
TZ: 'America/Toronto'
|
||||
# Volumes store your data between container upgrades
|
||||
volumes:
|
||||
- './db:/var/www/html/db'
|
||||
- './logos:/var/www/html/images/uploads/logos'
|
||||
@@ -67,7 +66,7 @@ These fields define the app's core components. You can find this information as
|
||||
:::
|
||||
3. For **Instance Specifications**, enter the minimum CPU and memory requirements. For example:
|
||||
- **CPU**: 2 core
|
||||
- **Memory**: 1 G
|
||||
- **Memory**: 1 Gi
|
||||

|
||||
|
||||
### Add environment variables
|
||||
@@ -83,7 +82,7 @@ Environment variables are used to pass configuration settings to your app. In th
|
||||
Volumes connect storage on your Olares device to a path inside the app's container, which is essential for saving data permanently. These are defined using the `-v` flag or in the `volumes:` section.
|
||||
|
||||
:::info Host path options
|
||||
The host path is where Olares stores the data, and the mount path is the path inside the container. Olares provides three managed host path prefixes:
|
||||
The host path is where Olares stores the data, and the mount path is the path inside the container. Studio provides three managed host path prefixes:
|
||||
|
||||
- `/app/data`: App data directory. Data can be accessed across nodes and is not deleted when the app is uninstalled. Appears under `/Data/studio` in Files.
|
||||
- `/app/cache`: App cache directory. Data is stored in the node's local disk and is deleted when the app is uninstalled. Appears under `/Cache/<device-name>/studio` in Files.
|
||||
@@ -96,15 +95,23 @@ The host path is where Olares stores the data, and the mount path is the path in
|
||||
|
||||
This app requires two volumes. You will add them one by one.
|
||||
1. Add the database volume. This data is for high-frequency I/O and does not need to be saved permanently. Map it to `/app/cache` so it will be automatically deleted when the app is uninstalled.
|
||||
1. Click **Add** next to **Storage Volume**.
|
||||
2. For **Host path**, select `/app/cache`, then enter `/db`.
|
||||
3. For **Mount path**, enter `/var/www/html/db`.
|
||||
4. Click **Submit**.
|
||||
2. Add the logo volume. This is user-uploaded data that should be persistent and reusable, even if the app is reinstalled. Map it to `/app/data`.
|
||||
1. Click **Add** next to **Storage Volume**.
|
||||
2. For **Host path**, select `/app/data`, then enter `/logos`.
|
||||
3. For **Mount path**, enter `/var/www/html/images/uploads/logos`
|
||||
4. Click **Submit**.
|
||||
|
||||
a. Click **Add** next to **Storage Volume**.
|
||||
|
||||
b. For **Host path**, select `/app/cache`, then enter `/db`.
|
||||
|
||||
c. For **Mount path**, enter `/var/www/html/db`.
|
||||
|
||||
d. Click **Submit**.
|
||||
2. Add the logo volume. This is user-uploaded data that should be persistent and reusable, even if the app is reinstalled. Map it to `/app/data`.
|
||||
|
||||
a. Click **Add** next to **Storage Volume**.
|
||||
|
||||
b. For **Host path**, select `/app/data`, then enter `/logos`.
|
||||
|
||||
c. For **Mount path**, enter `/var/www/html/images/uploads/logos`.
|
||||
|
||||
d. Click **Submit**.
|
||||

|
||||
|
||||
You can check Files later to verify the mounted paths.
|
||||
@@ -118,37 +125,36 @@ If your app needs Postgres or Redis, enable it under **Instance Specifications**
|
||||

|
||||
|
||||
When enabled, Studio provides dynamic variables. You must use these variables in the **Environment Variables** section for your app to connect to the database.
|
||||
- **Postgres variables:**
|
||||
- **Postgres variables**
|
||||
|
||||
| Variables | Description |
|
||||
|--------------|-----------------------|
|
||||
| $(PG_USER) | PostgreSQL username |
|
||||
| $(PG_DBNAME) | Database name |
|
||||
| $(PG_PASS) | Postgres Password |
|
||||
| $(PG_HOST) | Postgres service host |
|
||||
| $(PG_PORT) | Postgres service port |
|
||||
| Variables | Description |
|
||||
|----------------|-----------------------|
|
||||
| `$(PG_USER)` | PostgreSQL username |
|
||||
| `$(PG_DBNAME)` | Database name |
|
||||
| `$(PG_PASS)` | Postgres Password |
|
||||
| `$(PG_HOST)` | Postgres service host |
|
||||
| `$(PG_PORT)` | Postgres service port |
|
||||
|
||||
- **Redis variables:**
|
||||
- **Redis variables**
|
||||
|
||||
| Variables | Description |
|
||||
|---------------|--------------------|
|
||||
| $(REDIS_HOST) | Redis service host |
|
||||
| $(REDIS_PORT) | Redis service port |
|
||||
| $(REDIS_USER) | Redis username |
|
||||
| $(REDIS_PASS) | Redis password |
|
||||
| Variables | Description |
|
||||
|-----------------|--------------------|
|
||||
| `$(REDIS_HOST)` | Redis service host |
|
||||
| `$(REDIS_PORT)` | Redis service port |
|
||||
| `$(REDIS_USER)` | Redis username |
|
||||
| `$(REDIS_PASS)` | Redis password |
|
||||
|
||||
### Generate the app project
|
||||
1. Once all your configurations are set, click **Create**. This generates the app's project files.
|
||||
2. After creation, Studio generates the package files for your app, and then automatically deploys the app. You can check the status in the bottom bar.
|
||||
3. When the app is successfully deployed, click **Preview** in the top-right corner to launch it.
|
||||

|
||||
|
||||

|
||||
|
||||
## Review the package files and test the app
|
||||
Apps deployed from Studio include a `-dev` suffix in the title to distinguish them from Market installations.
|
||||

|
||||
|
||||
You can click on files like `OlaresManifest.yaml` to review and make changes. For example, to change the app's display name and logo.
|
||||
You can click on files like `OlaresManifest.yaml` to review and make changes. For example, to change the app's display name and logo:
|
||||
|
||||
1. Click **<span class="material-symbols-outlined">box_edit</span>Edit** in the top-right to open the editor.
|
||||
2. Click `OlaresManifest.yaml` to view the content.
|
||||
@@ -164,7 +170,6 @@ You can click on files like `OlaresManifest.yaml` to review and make changes. Fo
|
||||
:::
|
||||

|
||||
|
||||
|
||||
## Uninstall or delete the app
|
||||
If you no longer need the app, you can remove it.
|
||||
1. Click <span class="material-symbols-outlined">more_vert</span> in the top-right corner.
|
||||
@@ -175,12 +180,11 @@ If you no longer need the app, you can remove it.
|
||||
## Troubleshoot a deployment
|
||||
|
||||
### Cannot install the app
|
||||
If installation fails, review the error at the bottom of the page and click **View** to expand details.
|
||||

|
||||
If installation fails, review the error at the bottom of the page and click **View** to check details.
|
||||
|
||||
### Run into issues when the app is running
|
||||
Once running, you can manage the app from its deployment details page in Studio. The interface of this page is similar to Control Hub. If details don't appear, refresh the page.
|
||||
You can:
|
||||
- Use the **Stop** and **Restart** controls to retry. This action can often resolve runtime issues like a frozen process.
|
||||
- Check events or logs to investigate runtime errors. See [Export container logs for troubleshooting](../controlhub/manage-container.md#export-container-logs-for-troubleshooting) for details.
|
||||
- Use the **Stop** or **Restart** controls to retry. This action can often resolve runtime issues like a frozen process.
|
||||
- Check events or logs to investigate runtime errors. See [Export container logs for troubleshooting](../../../manual/olares/controlhub/manage-container.md#export-container-logs-for-troubleshooting) for details.
|
||||

|
||||
@@ -5,10 +5,7 @@ description: Learn how to use Studio to set up a dev container, access it via VS
|
||||
# Develop in a dev container
|
||||
Olares Studio allows you to spin up a pre-configured dev container to write and debug code (such as Node.js scripts or CUDA programs) without managing local infrastructure. This provides an isolated environment identical to the production runtime.
|
||||
|
||||
The following guide shows the setup workflow using a Node.js project as an example.
|
||||
:::info
|
||||
This workflow is optimized for iterative coding and testing. If you intend to publish the application to the Olares Market, you must create your own image and follow the [developer documentation](../../../developer/develop/submit/index.md) for final configuration.
|
||||
:::
|
||||
The following guide shows the development and setup workflow using a Node.js project as an example.
|
||||
|
||||
## Prerequisite
|
||||
- Olares version 1.12.2 or later.
|
||||
@@ -56,7 +53,7 @@ If you prefer your local settings and extensions, you can tunnel into the contai
|
||||
code tunnel
|
||||
```
|
||||
5. Follow the terminal prompts to authenticate using a Microsoft or GitHub account via the provided URL.
|
||||
6. Assign a name to the tunnel when prompted (e.g., `myapp-demo`). This will output a vscode.dev URL tied to this remote workspace.
|
||||
6. Assign a name to the tunnel when prompted (e.g., `myapp-demo`). This will output a `vscode.dev` URL tied to this remote workspace.
|
||||

|
||||
|
||||
7. Open VS Code on your local machine, click the **><** icon in the bottom-left, and select **Tunnel**.
|
||||
@@ -183,6 +180,9 @@ You can follow the same steps to modify `OlaresManifest.yaml` and `deployment.ya
|
||||
- name: "80"
|
||||
port: 80
|
||||
targetPort: 80
|
||||
- name: myweb-dev-8080
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
# Add the following
|
||||
- name: myweb-dev-8081 # Must match entrance name
|
||||
port: 8081
|
||||
@@ -195,10 +195,10 @@ You can follow the same steps to modify `OlaresManifest.yaml` and `deployment.ya
|
||||
|
||||
4. Click **Apply** to redeploy the container.
|
||||
|
||||
You can verify the active ports in **Services** > **Ports**.
|
||||
Once deployed, go to **Services** > **Ports**. You can see your new port listed here.
|
||||

|
||||
|
||||
### Verify the new port
|
||||
### Test the connection
|
||||
1. Update `index.js` to listen on the new port:
|
||||
```js
|
||||
const express = require('express');
|
||||
@@ -1,15 +1,26 @@
|
||||
# Tutorial
|
||||
---
|
||||
description: Get started with Studio to deploy Docker-based apps, develop new apps, package and upload locally, and manage assets on your Olares device.
|
||||
---
|
||||
# Deploy and develop apps in Olares
|
||||
|
||||
Welcome to the Olares developer guides. These detailed tutorials offer a step-by-step guide on building an Olares Application from scratch.
|
||||
Studio provides a real Olares environment for building, porting, and testing apps when cloud features and the sandbox system are hard to simulate locally. With Studio you can:
|
||||
- Create a new Olares app in an online development container.
|
||||
- Port an existing app, adjust its configurations, and test the installation flow.
|
||||
- Package your app into a chart and download it when your app is ready.
|
||||
|
||||
To get started, you can learn some basic concepts of Olares, such as:
|
||||
- [Olares architectural components](../../concepts/architecture.md)
|
||||
- [Olares Application Chart](../../develop/package/chart.md)
|
||||
- [Olares Extension on Helm](../package/extension.md)
|
||||
## Access Studio
|
||||
You must manually install Studio:
|
||||
1. Open **Market**, and search for "Studio".
|
||||

|
||||
|
||||
These fundamentals will help you grasp our development process more effectively.
|
||||
2. Click **Get**, then **Install**, and wait for installation to complete.
|
||||
|
||||
You can also [learn about DevBox](studio.md), a built-in app that Olares provides for developers to build Olares applications.
|
||||
After installation, launch Studio from Market or from Launchpad.
|
||||
|
||||
If you're brand new to Olares development and want to jump straight into coding, start with the [**Create your first Olares app**](./note/index.md). This tutorial will step you through the process of building a small note application.
|
||||
## Understand the Studio UI
|
||||
The sidebar and **Home** page organize your main tasks in Studio:
|
||||
- **Home**: A welcome page with shortcuts to common actions and documentation.
|
||||
- **Applications**: A list of apps you have created and deployed with Studio.
|
||||
- **Start**: You can start deploying or developing apps, or uploading an app from a local chart file.
|
||||
|
||||

|
||||
@@ -8,7 +8,7 @@ Apps created in Studio are ideal for development and testing. For stable, long-t
|
||||
|
||||
## Download the App package from Studio
|
||||
|
||||
After confirming that your app works as expected, you can download its complete installation package.
|
||||
After confirming that your app works as expected, you can download its complete installation package:
|
||||
|
||||
1. Open your app project in **Studio**.
|
||||
2. Click <span class="material-symbols-outlined">more_vert</span> in the top-right corner.
|
||||
@@ -24,6 +24,4 @@ After confirming that your app works as expected, you can download its complete
|
||||
|
||||
Once finished, you can click **Open** to launch it.
|
||||
|
||||
All custom-installed apps will appear under the **My Olares** > **Upload** tab.
|
||||
|
||||
|
||||
All custom-installed apps will appear under the **My Olares** > **Upload** tab.
|
||||
@@ -1,13 +0,0 @@
|
||||
# Learn about Studio
|
||||
|
||||
At Olares, we provide a development tool called Studio. It helps developers create applications for **Olares**.
|
||||
|
||||
- Why is Studio necessary for developers?
|
||||
|
||||
Olares has many cloud-based features that are difficult to simulate in a standalone development environment. Furthermore, the unique sandbox system of **Olares** requires a real system environment for end-to-end testing. To simplify app simulation for developers and minimize system integration efforts during development, we provide the **Studio**. **Studio** is a quick, automatic toolset for creating app sandboxes.
|
||||
|
||||
- What features does Studio have?
|
||||
|
||||
- In Studio, you can build an app and generate a corresponding Olares Application Configuration* This deployment files can be modified, allowing you to port an existing app and deploy it to the Olares. During the modification process, you can continually attempt installation and resolve any issues that arise. Once the app passes your tests, you can download your Application Chart and submit it to the [Olares Market Repository](https://github.com/beclab/apps).
|
||||
|
||||
- In addition to porting existing apps, you can also create a native Olares application in Studio. Studio provides an online development container where coders can work in a real environment, utilize other system interfaces, database clusters, and more.
|
||||
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.
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
description: Get started with Studio to deploy Docker-based apps, develop new apps, package and upload locally, and manage assets on your Olares device.
|
||||
---
|
||||
# Deploy and develop apps in Olares
|
||||
|
||||
Studio provides a real Olares environment for building, porting, and testing apps when cloud features and the sandbox system are hard to simulate locally. With Studio you can:
|
||||
- Create a new Olares app in an online development container.
|
||||
- Port an existing app, adjust its configurations, and test the installation flow.
|
||||
-Package your app into a chart and download it when your app is ready.
|
||||
|
||||
## Access Studio
|
||||
Studio is available in Olares Market and must be installed manually.
|
||||
1. Open **Market**, and search for "Studio".
|
||||

|
||||
|
||||
2. Click **Get**, then **Install**, and wait for installation to complete.
|
||||
|
||||
After installation, launch Studio from Market or from Launchpad.
|
||||
|
||||
## Understand the Studio UI
|
||||
The sidebar and **Home** page organize your main tasks in Studio:
|
||||
- **Home**: A welcome page with shortcuts to common actions and documentation.
|
||||
- **Applications**: A list of apps you have created and deployed with Studio.
|
||||
- **Start**: You can start deploying or developing apps, or uploading an app from a local chart file.
|
||||
|
||||

|
||||
|
||||
|
||||
---
|
||||
<div>
|
||||
<h4><a href="./deploy">Deploy an app from Docker image</a></h4>
|
||||
Deploy an app from an existing Docker image, configure it, and test it in Studio.
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<h4><a href="./develop">Develop in a dev container</a></h4>
|
||||
Build and debug a new app using the Studio development environment.
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4><a href="./package-upload">Package and upload the app to Market</a></h4>
|
||||
Download an installable package and upload it to Market for local use.
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4><a href="./assets">Add app assets</a></h4>
|
||||
Use Olares image hosting to add and manage creative assets for your app.
|
||||
</div>
|
||||
|
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 |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 165 KiB |
|
After Width: | Height: | Size: 942 KiB |