Compare commits

...

7 Commits

Author SHA1 Message Date
hysyeah
2997bb4ce4 fix: entrance policy update by upgrade (#2295) 2025-12-23 19:34:04 +08:00
hys
0d8a3f7aba fix: skip app update if deployment not changed 2025-12-23 18:00:59 +08:00
hys
5b1d1d3196 fifx: entrance policy update by upgrade 2025-12-23 16:39:12 +08:00
hys
f10626957a update appservice image to 0.4.68 2025-12-22 19:45:49 +08:00
hysyeah
6ce2e8d5fb feat: add deviceName to helm values (#2289) 2025-12-22 19:45:49 +08:00
eball
bab074cd37 daemon: bump jws sdk version (#2287) 2025-12-22 17:58:55 +08:00
dkeven
afb7d49455 fix(cli): unify node GPU info update logic (#2288) 2025-12-22 17:58:35 +08:00
13 changed files with 593 additions and 84 deletions

View File

@@ -187,7 +187,7 @@ func (m *InstallPluginModule) Init() {
Prepare: &prepare.PrepareCollection{
new(common.OnlyFirstMaster),
},
Action: new(UpdateNodeLabels),
Action: new(UpdateNodeGPUInfo),
Parallel: false,
Retry: 1,
}
@@ -223,23 +223,6 @@ func (m *InstallPluginModule) Init() {
}
}
type GetCudaVersionModule struct {
common.KubeModule
}
func (g *GetCudaVersionModule) Init() {
g.Name = "GetCudaVersion"
getCudaVersion := &task.LocalTask{
Name: "GetCudaVersion",
Action: new(GetCudaVersion),
}
g.Tasks = []task.Interface{
getCudaVersion,
}
}
type NodeLabelingModule struct {
common.KubeModule
}
@@ -253,7 +236,7 @@ func (l *NodeLabelingModule) Init() {
new(CudaInstalled),
new(CurrentNodeInK8s),
},
Action: new(UpdateNodeLabels),
Action: new(UpdateNodeGPUInfo),
Retry: 1,
}

View File

@@ -10,7 +10,10 @@ import (
"strings"
"time"
v1alpha1 "bytetrade.io/web3os/app-service/api/sys.bytetrade.io/v1alpha1"
apputils "bytetrade.io/web3os/app-service/pkg/utils"
ctrl "sigs.k8s.io/controller-runtime"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
"github.com/beclab/Olares/cli/pkg/clientset"
"github.com/beclab/Olares/cli/pkg/common"
@@ -26,7 +29,11 @@ import (
"github.com/pelletier/go-toml"
"github.com/pkg/errors"
apixclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/util/retry"
)
@@ -323,59 +330,11 @@ func (t *CheckGpuStatus) Execute(runtime connector.Runtime) error {
return fmt.Errorf("GPU Container State is Pending")
}
type GetCudaVersion struct {
type UpdateNodeGPUInfo struct {
common.KubeAction
}
func (g *GetCudaVersion) Execute(runtime connector.Runtime) error {
var nvidiaSmiFile string
var systemInfo = runtime.GetSystemInfo()
switch {
case systemInfo.IsWsl():
nvidiaSmiFile = "/usr/lib/wsl/lib/nvidia-smi"
default:
nvidiaSmiFile = "/usr/bin/nvidia-smi"
}
if !util.IsExist(nvidiaSmiFile) {
logger.Info("nvidia-smi not exists")
return nil
}
var cudaVersion string
res, err := runtime.GetRunner().Cmd(fmt.Sprintf("%s --version", nvidiaSmiFile), false, true)
if err != nil {
logger.Errorf("get cuda version error %v", err)
return nil
}
lines := strings.Split(res, "\n")
if len(lines) == 0 {
return nil
}
for _, line := range lines {
if strings.Contains(line, "CUDA Version") {
parts := strings.Split(line, ":")
if len(parts) != 2 {
break
}
cudaVersion = strings.TrimSpace(parts[1])
}
}
if cudaVersion != "" {
common.SetSystemEnv("OLARES_SYSTEM_CUDA_VERSION", cudaVersion)
}
return nil
}
type UpdateNodeLabels struct {
common.KubeAction
}
func (u *UpdateNodeLabels) Execute(runtime connector.Runtime) error {
func (u *UpdateNodeGPUInfo) Execute(runtime connector.Runtime) error {
client, err := clientset.NewKubeClient()
if err != nil {
return errors.Wrap(errors.WithStack(err), "kubeclient create error")
@@ -482,6 +441,85 @@ func UpdateNodeGpuLabel(ctx context.Context, client kubernetes.Interface, driver
}
}
if cuda != nil && *cuda != "" {
if err := updateCudaVersionSystemEnv(ctx, *cuda); err != nil {
logger.Errorf("failed to update SystemEnv for CUDA version: %v", err)
return err
}
}
return nil
}
func updateCudaVersionSystemEnv(ctx context.Context, cudaVersion string) error {
envName := "OLARES_SYSTEM_CUDA_VERSION"
common.SetSystemEnv(envName, cudaVersion)
config, err := ctrl.GetConfig()
if err != nil {
return fmt.Errorf("failed to get rest config: %w", err)
}
apix, err := apixclientset.NewForConfig(config)
if err != nil {
return fmt.Errorf("failed to create crd client: %w", err)
}
_, err = apix.ApiextensionsV1().CustomResourceDefinitions().Get(ctx, "systemenvs.sys.bytetrade.io", metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
logger.Debugf("SystemEnv CRD not found, skipping CUDA version update")
return nil
}
return fmt.Errorf("failed to get SystemEnv CRD: %w", err)
}
scheme := kruntime.NewScheme()
if err := v1alpha1.AddToScheme(scheme); err != nil {
return fmt.Errorf("failed to add systemenv scheme: %w", err)
}
c, err := ctrlclient.New(config, ctrlclient.Options{Scheme: scheme})
if err != nil {
return fmt.Errorf("failed to create client: %w", err)
}
resourceName, err := apputils.EnvNameToResourceName(envName)
if err != nil {
return fmt.Errorf("invalid system env name: %s", envName)
}
var existingSystemEnv v1alpha1.SystemEnv
err = c.Get(ctx, types.NamespacedName{Name: resourceName}, &existingSystemEnv)
if err == nil {
if existingSystemEnv.Default != cudaVersion {
existingSystemEnv.Default = cudaVersion
if err := c.Update(ctx, &existingSystemEnv); err != nil {
return fmt.Errorf("failed to update SystemEnv %s: %w", resourceName, err)
}
logger.Infof("Updated SystemEnv %s default to %s", resourceName, cudaVersion)
}
return nil
}
if !apierrors.IsNotFound(err) {
return fmt.Errorf("failed to get SystemEnv %s: %w", resourceName, err)
}
systemEnv := &v1alpha1.SystemEnv{
ObjectMeta: metav1.ObjectMeta{
Name: resourceName,
},
EnvVarSpec: v1alpha1.EnvVarSpec{
EnvName: envName,
Default: cudaVersion,
},
}
if err := c.Create(ctx, systemEnv); err != nil && !apierrors.IsAlreadyExists(err) {
return fmt.Errorf("failed to create SystemEnv %s: %w", resourceName, err)
}
logger.Infof("Created SystemEnv: %s with default %s", envName, cudaVersion)
return nil
}

View File

@@ -58,7 +58,6 @@ func (l *linuxInstallPhaseBuilder) installGpuPlugin() phase {
return []module.Module{
&gpu.RestartK3sServiceModule{Skip: !(l.runtime.Arg.Kubetype == common.K3s)},
&gpu.InstallPluginModule{Skip: skipGpuPlugin},
&gpu.GetCudaVersionModule{},
}
}

View File

@@ -22,7 +22,7 @@ require (
bytetrade.io/web3os/bfl v0.0.0-00010101000000-000000000000
github.com/Masterminds/semver/v3 v3.4.0
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
github.com/beclab/Olares/cli v0.0.0-20251016092744-6241cceceb89
github.com/beclab/Olares/cli v0.0.0-20251219153848-63d422037cf9
github.com/containerd/containerd v1.7.28
github.com/distribution/distribution/v3 v3.0.0
github.com/dustin/go-humanize v1.0.1
@@ -39,6 +39,7 @@ require (
github.com/libp2p/go-netroute v0.2.2
github.com/mackerelio/go-osstat v0.2.5
github.com/mdlayher/raw v0.1.0
github.com/miekg/dns v1.1.55
github.com/muka/network_manager v0.0.0-20200903202308-ae5ede816e07
github.com/nxadm/tail v1.4.11
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
@@ -56,6 +57,7 @@ require (
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b
golang.org/x/sys v0.35.0
k8s.io/api v0.34.1
k8s.io/apiextensions-apiserver v0.34.0
k8s.io/apimachinery v0.34.1
k8s.io/client-go v12.0.0+incompatible
k8s.io/cri-api v0.34.1
@@ -129,7 +131,6 @@ require (
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mdlayher/packet v0.0.0-20220221164757-67998ac0ff93 // indirect
github.com/mdlayher/socket v0.2.1 // indirect
github.com/miekg/dns v1.1.55 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/spdystream v0.5.0 // indirect
@@ -200,7 +201,6 @@ require (
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
howett.net/plist v1.0.0 // indirect
k8s.io/apiextensions-apiserver v0.34.0 // indirect
k8s.io/apiserver v0.34.0 // indirect
k8s.io/component-base v0.34.1 // indirect
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect

View File

@@ -24,8 +24,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/beclab/Olares/cli v0.0.0-20251016092744-6241cceceb89 h1:5s9hXV8K3faToQtE9DbiM7O6jt5kIiEsLAaKn6F0UfA=
github.com/beclab/Olares/cli v0.0.0-20251016092744-6241cceceb89/go.mod h1:iEvZxM6PnFxFRppneTzV3hgr2tIxDnsI3dhp4pi7pFg=
github.com/beclab/Olares/cli v0.0.0-20251219153848-63d422037cf9 h1:YNHfPra2FqsKJ5mAxSWNVIK6VyWygRyZiNwfPqiFxlg=
github.com/beclab/Olares/cli v0.0.0-20251219153848-63d422037cf9/go.mod h1:cYPcuju2yRSp9BQjIN/CC495dDOOvVoL42r/gvFlutk=
github.com/beclab/app-service v0.4.37 h1:gt60wQxgPWMc3oN94TNSdiQAvzqTyCv/OUP93jNSQTY=
github.com/beclab/app-service v0.4.37/go.mod h1:0vEg3rv/DbR7dYznvTlXNXyYNn+TXNMaxz03GQYRWUQ=
github.com/beclab/bfl v0.3.36 h1:PgeSPGc+XoONiwFsKq9xX8rqcL4kVM1G/ut0lYYj/js=

View File

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

View File

@@ -376,6 +376,14 @@ func (r *ApplicationReconciler) createApplication(ctx context.Context, req ctrl.
func (r *ApplicationReconciler) updateApplication(ctx context.Context, req ctrl.Request,
deployment client.Object, app *appv1alpha1.Application, name string) error {
// Skip update if triggered by app modification (not deployment change)
if app.Annotations != nil {
if lastVersion := app.Annotations[deploymentResourceVersionAnnotation]; lastVersion == deployment.GetResourceVersion() {
klog.Infof("skip updateApplication: deployment %s not changed, triggered by app modification", deployment.GetName())
return nil
}
}
appCopy := app.DeepCopy()
appNames := getAppName(deployment)
isMultiApp := len(appNames) > 1
@@ -406,7 +414,7 @@ func (r *ApplicationReconciler) updateApplication(ctx context.Context, req ctrl.
} else {
appid = appv1alpha1.AppName(name).GetAppID()
}
_, sharedEntrances := r.getAppSettings(ctx, name, appid, owner, deployment, isMultiApp, entrancesMap[name])
settings, sharedEntrances := r.getAppSettings(ctx, name, appid, owner, deployment, isMultiApp, entrancesMap[name])
appCopy.Spec.Name = name
appCopy.Spec.Namespace = deployment.GetNamespace()
@@ -415,7 +423,21 @@ func (r *ApplicationReconciler) updateApplication(ctx context.Context, req ctrl.
appCopy.Spec.Icon = icon
appCopy.Spec.SharedEntrances = sharedEntrances
appCopy.Spec.Ports = servicePortsMap[name]
appCopy.Spec.Entrances = entrancesMap[name]
// Merge entrances: preserve authLevel from existing, update other fields
appCopy.Spec.Entrances = mergeEntrances(app.Spec.Entrances, entrancesMap[name])
if appCopy.Spec.Settings == nil {
appCopy.Spec.Settings = make(map[string]string)
}
if settings["defaultThirdLevelDomainConfig"] != "" {
appCopy.Spec.Settings["defaultThirdLevelDomainConfig"] = settings["defaultThirdLevelDomainConfig"]
}
if incomingPolicy := settings[applicationSettingsPolicyKey]; incomingPolicy != "" {
existingPolicy := appCopy.Spec.Settings[applicationSettingsPolicyKey]
appCopy.Spec.Settings[applicationSettingsPolicyKey] = mergePolicySettings(existingPolicy, incomingPolicy)
}
if tailScale != nil {
appCopy.Spec.TailScale = *tailScale
@@ -436,6 +458,13 @@ func (r *ApplicationReconciler) updateApplication(ctx context.Context, req ctrl.
}
}
// Record deployment resourceVersion to detect app-only modifications
if appCopy.Annotations == nil {
appCopy.Annotations = make(map[string]string)
}
klog.Infof("deploymentname: %s, version: %v", deployment.GetName(), deployment.GetResourceVersion())
appCopy.Annotations[deploymentResourceVersionAnnotation] = deployment.GetResourceVersion()
err = r.Patch(ctx, appCopy, client.MergeFrom(app))
if err != nil {
klog.Infof("update spec failed %v", err)

View File

@@ -1,10 +1,17 @@
package controllers
import (
"encoding/json"
appv1alpha1 "bytetrade.io/web3os/app-service/api/app.bytetrade.io/v1alpha1"
)
const (
applicationSettingsPolicyKey = "policy"
namespaceFinalizer = "finalizers.bytetrade.io/namespaces"
userFinalizer = "finalizers.bytetrade.io/users"
creator = "bytetrade.io/creator"
applicationSettingsPolicyKey = "policy"
namespaceFinalizer = "finalizers.bytetrade.io/namespaces"
userFinalizer = "finalizers.bytetrade.io/users"
creator = "bytetrade.io/creator"
deploymentResourceVersionAnnotation = "bytetrade.io/deployment-resource-version"
)
type applicationSettingsSubPolicy struct {
@@ -20,3 +27,59 @@ type applicationSettingsPolicy struct {
OneTime bool `json:"one_time"`
Duration int32 `json:"valid_duration"`
}
// mergeEntrances merges new entrances with existing ones.
// Preserves authLevel from existing entrances, other fields are updated from new entrances.
func mergeEntrances(existing, incoming []appv1alpha1.Entrance) []appv1alpha1.Entrance {
if len(existing) == 0 {
return incoming
}
existingByName := make(map[string]*appv1alpha1.Entrance, len(existing))
for i := range existing {
existingByName[existing[i].Name] = &existing[i]
}
merged := make([]appv1alpha1.Entrance, 0, len(incoming))
for _, entry := range incoming {
if old, exists := existingByName[entry.Name]; exists {
entry.AuthLevel = old.AuthLevel
}
merged = append(merged, entry)
}
return merged
}
func mergePolicySettings(existingPolicy, incomingPolicy string) string {
if incomingPolicy == "" {
return existingPolicy
}
if existingPolicy == "" {
return incomingPolicy
}
var existing, incoming map[string]applicationSettingsPolicy
if err := json.Unmarshal([]byte(existingPolicy), &existing); err != nil {
return incomingPolicy
}
if err := json.Unmarshal([]byte(incomingPolicy), &incoming); err != nil {
return existingPolicy
}
merged := make(map[string]applicationSettingsPolicy, len(incoming))
for name, incomingEntry := range incoming {
if existingEntry, exists := existing[name]; exists {
incomingEntry.DefaultPolicy = existingEntry.DefaultPolicy
incomingEntry.SubPolicies = existingEntry.SubPolicies
}
merged[name] = incomingEntry
}
result, err := json.Marshal(merged)
if err != nil {
return existingPolicy
}
return string(result)
}

View File

@@ -0,0 +1,303 @@
package controllers
import (
"encoding/json"
appv1alpha1 "bytetrade.io/web3os/app-service/api/app.bytetrade.io/v1alpha1"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("mergeEntrances", func() {
It("should return incoming when existing is empty", func() {
incoming := []appv1alpha1.Entrance{
{Name: "web", Host: "web-svc", Port: 8080, AuthLevel: "public"},
}
result := mergeEntrances(nil, incoming)
Expect(result).To(Equal(incoming))
})
It("should return incoming when existing is empty slice", func() {
existing := []appv1alpha1.Entrance{}
incoming := []appv1alpha1.Entrance{
{Name: "web", Host: "web-svc", Port: 8080, AuthLevel: "public"},
}
result := mergeEntrances(existing, incoming)
Expect(result).To(Equal(incoming))
})
It("should preserve authLevel from existing entrances", func() {
existing := []appv1alpha1.Entrance{
{Name: "web", Host: "old-svc", Port: 80, AuthLevel: "private"},
}
incoming := []appv1alpha1.Entrance{
{Name: "web", Host: "new-svc", Port: 8080, AuthLevel: "public"},
}
result := mergeEntrances(existing, incoming)
Expect(result).To(HaveLen(1))
Expect(result[0].Name).To(Equal("web"))
Expect(result[0].Host).To(Equal("new-svc"))
Expect(result[0].Port).To(Equal(int32(8080)))
Expect(result[0].AuthLevel).To(Equal("private")) // preserved from existing
})
It("should update other fields from incoming", func() {
existing := []appv1alpha1.Entrance{
{Name: "web", Host: "old-svc", Port: 80, AuthLevel: "private", Title: "Old Title", Icon: "old-icon"},
}
incoming := []appv1alpha1.Entrance{
{Name: "web", Host: "new-svc", Port: 8080, AuthLevel: "public", Title: "New Title", Icon: "new-icon"},
}
result := mergeEntrances(existing, incoming)
Expect(result).To(HaveLen(1))
Expect(result[0].Host).To(Equal("new-svc"))
Expect(result[0].Port).To(Equal(int32(8080)))
Expect(result[0].Title).To(Equal("New Title"))
Expect(result[0].Icon).To(Equal("new-icon"))
Expect(result[0].AuthLevel).To(Equal("private")) // preserved
})
It("should handle new entrance not in existing", func() {
existing := []appv1alpha1.Entrance{
{Name: "web", Host: "web-svc", Port: 80, AuthLevel: "private"},
}
incoming := []appv1alpha1.Entrance{
{Name: "web", Host: "web-svc", Port: 80, AuthLevel: "public"},
{Name: "api", Host: "api-svc", Port: 3000, AuthLevel: "public"},
}
result := mergeEntrances(existing, incoming)
Expect(result).To(HaveLen(2))
// web entrance preserves authLevel
Expect(result[0].Name).To(Equal("web"))
Expect(result[0].AuthLevel).To(Equal("private"))
// api entrance uses incoming authLevel (no existing)
Expect(result[1].Name).To(Equal("api"))
Expect(result[1].AuthLevel).To(Equal("public"))
})
It("should handle removed entrance from existing", func() {
existing := []appv1alpha1.Entrance{
{Name: "web", Host: "web-svc", Port: 80, AuthLevel: "private"},
{Name: "api", Host: "api-svc", Port: 3000, AuthLevel: "private"},
}
incoming := []appv1alpha1.Entrance{
{Name: "web", Host: "web-svc", Port: 80, AuthLevel: "public"},
}
result := mergeEntrances(existing, incoming)
Expect(result).To(HaveLen(1))
Expect(result[0].Name).To(Equal("web"))
Expect(result[0].AuthLevel).To(Equal("private"))
})
It("should handle multiple entrances correctly", func() {
existing := []appv1alpha1.Entrance{
{Name: "web", Host: "web-svc", Port: 80, AuthLevel: "private"},
{Name: "admin", Host: "admin-svc", Port: 9000, AuthLevel: "internal"},
}
incoming := []appv1alpha1.Entrance{
{Name: "web", Host: "web-new", Port: 8080, AuthLevel: "public"},
{Name: "admin", Host: "admin-new", Port: 9090, AuthLevel: "public"},
{Name: "api", Host: "api-svc", Port: 3000, AuthLevel: "public"},
}
result := mergeEntrances(existing, incoming)
Expect(result).To(HaveLen(3))
// web: authLevel preserved
Expect(result[0].Name).To(Equal("web"))
Expect(result[0].Host).To(Equal("web-new"))
Expect(result[0].AuthLevel).To(Equal("private"))
// admin: authLevel preserved
Expect(result[1].Name).To(Equal("admin"))
Expect(result[1].Host).To(Equal("admin-new"))
Expect(result[1].AuthLevel).To(Equal("internal"))
// api: new entrance, uses incoming authLevel
Expect(result[2].Name).To(Equal("api"))
Expect(result[2].AuthLevel).To(Equal("public"))
})
})
var _ = Describe("mergePolicySettings", func() {
It("should return existing when incoming is empty", func() {
existing := `{"calibreweb-svc":{"default_policy":"system","sub_policies":null,"one_time":false,"valid_duration":0}}`
result := mergePolicySettings(existing, "")
Expect(result).To(Equal(existing))
})
It("should return incoming when existing is empty", func() {
incoming := `{"calibreweb-svc":{"default_policy":"system","one_time":false,"sub_policies":[{"one_time":true,"policy":"one_factor","uri":"/api/send","valid_duration":0}],"valid_duration":0}}`
result := mergePolicySettings("", incoming)
Expect(result).To(Equal(incoming))
})
It("should preserve default_policy from existing", func() {
existing := `{"calibreweb-svc":{"default_policy":"private","sub_policies":null,"one_time":false,"valid_duration":0}}`
incoming := `{"calibreweb-svc":{"default_policy":"system","sub_policies":null,"one_time":false,"valid_duration":0}}`
result := mergePolicySettings(existing, incoming)
var resultPolicy map[string]applicationSettingsPolicy
err := json.Unmarshal([]byte(result), &resultPolicy)
Expect(err).NotTo(HaveOccurred())
Expect(resultPolicy["calibreweb-svc"].DefaultPolicy).To(Equal("private"))
})
It("should preserve sub_policies from existing", func() {
existing := `{"calibreweb-svc":{"default_policy":"system","sub_policies":[{"uri":"/api/send","policy":"one_factor","one_time":true,"valid_duration":0}],"one_time":false,"valid_duration":0}}`
incoming := `{"calibreweb-svc":{"default_policy":"system","sub_policies":[{"uri":"/api/new","policy":"two_factor","one_time":false,"valid_duration":3600}],"one_time":false,"valid_duration":0}}`
result := mergePolicySettings(existing, incoming)
var resultPolicy map[string]applicationSettingsPolicy
err := json.Unmarshal([]byte(result), &resultPolicy)
Expect(err).NotTo(HaveOccurred())
// sub_policies preserved from existing
Expect(resultPolicy["calibreweb-svc"].SubPolicies).To(HaveLen(1))
Expect(resultPolicy["calibreweb-svc"].SubPolicies[0].URI).To(Equal("/api/send"))
Expect(resultPolicy["calibreweb-svc"].SubPolicies[0].Policy).To(Equal("one_factor"))
Expect(resultPolicy["calibreweb-svc"].SubPolicies[0].OneTime).To(BeTrue())
})
It("should preserve both default_policy and sub_policies from existing", func() {
existing := `{"calibreweb-svc":{"default_policy":"public","sub_policies":[{"uri":"/api/send","policy":"one_factor","one_time":true,"valid_duration":0}],"one_time":false,"valid_duration":0}}`
incoming := `{"calibreweb-svc":{"default_policy":"system","sub_policies":[{"uri":"/api/new","policy":"two_factor","one_time":false,"valid_duration":3600}],"one_time":true,"valid_duration":1800}}`
result := mergePolicySettings(existing, incoming)
var resultPolicy map[string]applicationSettingsPolicy
err := json.Unmarshal([]byte(result), &resultPolicy)
Expect(err).NotTo(HaveOccurred())
// default_policy preserved from existing
Expect(resultPolicy["calibreweb-svc"].DefaultPolicy).To(Equal("public"))
// sub_policies preserved from existing
Expect(resultPolicy["calibreweb-svc"].SubPolicies).To(HaveLen(1))
Expect(resultPolicy["calibreweb-svc"].SubPolicies[0].URI).To(Equal("/api/send"))
// other fields use incoming
Expect(resultPolicy["calibreweb-svc"].OneTime).To(BeTrue())
Expect(resultPolicy["calibreweb-svc"].Duration).To(Equal(int32(1800)))
})
It("should preserve null sub_policies from existing", func() {
existing := `{"calibreweb-svc":{"default_policy":"system","sub_policies":null,"one_time":false,"valid_duration":0}}`
incoming := `{"calibreweb-svc":{"default_policy":"system","sub_policies":[{"uri":"/api/send","policy":"one_factor","one_time":true,"valid_duration":0}],"one_time":false,"valid_duration":0}}`
result := mergePolicySettings(existing, incoming)
var resultPolicy map[string]applicationSettingsPolicy
err := json.Unmarshal([]byte(result), &resultPolicy)
Expect(err).NotTo(HaveOccurred())
// sub_policies preserved as null from existing
Expect(resultPolicy["calibreweb-svc"].SubPolicies).To(BeNil())
})
It("should add new entrance policy with incoming values", func() {
existing := `{"calibreweb-svc":{"default_policy":"private","sub_policies":[{"uri":"/old","policy":"one_factor","one_time":false,"valid_duration":0}],"one_time":false,"valid_duration":0}}`
incoming := `{"calibreweb-svc":{"default_policy":"system","sub_policies":null,"one_time":false,"valid_duration":0},"api-svc":{"default_policy":"public","sub_policies":[{"uri":"/api","policy":"two_factor","one_time":true,"valid_duration":3600}],"one_time":false,"valid_duration":0}}`
result := mergePolicySettings(existing, incoming)
var resultPolicy map[string]applicationSettingsPolicy
err := json.Unmarshal([]byte(result), &resultPolicy)
Expect(err).NotTo(HaveOccurred())
// existing entry: preserved default_policy and sub_policies
Expect(resultPolicy["calibreweb-svc"].DefaultPolicy).To(Equal("private"))
Expect(resultPolicy["calibreweb-svc"].SubPolicies).To(HaveLen(1))
Expect(resultPolicy["calibreweb-svc"].SubPolicies[0].URI).To(Equal("/old"))
// new entry: uses incoming values
Expect(resultPolicy).To(HaveKey("api-svc"))
Expect(resultPolicy["api-svc"].DefaultPolicy).To(Equal("public"))
Expect(resultPolicy["api-svc"].SubPolicies).To(HaveLen(1))
Expect(resultPolicy["api-svc"].SubPolicies[0].URI).To(Equal("/api"))
})
It("should delete entrance policy not in incoming", func() {
existing := `{"calibreweb-svc":{"default_policy":"system","sub_policies":null,"one_time":false,"valid_duration":0},"api-svc":{"default_policy":"public","sub_policies":null,"one_time":false,"valid_duration":0}}`
incoming := `{"calibreweb-svc":{"default_policy":"system","sub_policies":null,"one_time":false,"valid_duration":0}}`
result := mergePolicySettings(existing, incoming)
var resultPolicy map[string]applicationSettingsPolicy
err := json.Unmarshal([]byte(result), &resultPolicy)
Expect(err).NotTo(HaveOccurred())
Expect(resultPolicy).To(HaveKey("calibreweb-svc"))
Expect(resultPolicy).NotTo(HaveKey("api-svc"))
})
It("should handle add, preserve, and delete together", func() {
existing := `{
"web-svc":{"default_policy":"private","sub_policies":[{"uri":"/web","policy":"one_factor","one_time":false,"valid_duration":0}],"one_time":false,"valid_duration":0},
"admin-svc":{"default_policy":"internal","sub_policies":[{"uri":"/admin","policy":"two_factor","one_time":true,"valid_duration":3600}],"one_time":false,"valid_duration":0},
"legacy-svc":{"default_policy":"public","sub_policies":null,"one_time":false,"valid_duration":0}
}`
incoming := `{
"web-svc":{"default_policy":"system","sub_policies":null,"one_time":true,"valid_duration":1800},
"admin-svc":{"default_policy":"system","sub_policies":[{"uri":"/new","policy":"one_factor","one_time":false,"valid_duration":0}],"one_time":false,"valid_duration":0},
"api-svc":{"default_policy":"system","sub_policies":[{"uri":"/api","policy":"one_factor","one_time":false,"valid_duration":0}],"one_time":false,"valid_duration":0}
}`
result := mergePolicySettings(existing, incoming)
var resultPolicy map[string]applicationSettingsPolicy
err := json.Unmarshal([]byte(result), &resultPolicy)
Expect(err).NotTo(HaveOccurred())
// web-svc: default_policy and sub_policies preserved, other fields from incoming
Expect(resultPolicy["web-svc"].DefaultPolicy).To(Equal("private"))
Expect(resultPolicy["web-svc"].SubPolicies).To(HaveLen(1))
Expect(resultPolicy["web-svc"].SubPolicies[0].URI).To(Equal("/web"))
Expect(resultPolicy["web-svc"].OneTime).To(BeTrue())
Expect(resultPolicy["web-svc"].Duration).To(Equal(int32(1800)))
// admin-svc: default_policy and sub_policies preserved
Expect(resultPolicy["admin-svc"].DefaultPolicy).To(Equal("internal"))
Expect(resultPolicy["admin-svc"].SubPolicies).To(HaveLen(1))
Expect(resultPolicy["admin-svc"].SubPolicies[0].URI).To(Equal("/admin"))
Expect(resultPolicy["admin-svc"].SubPolicies[0].Policy).To(Equal("two_factor"))
// api-svc: new entry, uses incoming values
Expect(resultPolicy).To(HaveKey("api-svc"))
Expect(resultPolicy["api-svc"].DefaultPolicy).To(Equal("system"))
Expect(resultPolicy["api-svc"].SubPolicies).To(HaveLen(1))
Expect(resultPolicy["api-svc"].SubPolicies[0].URI).To(Equal("/api"))
// legacy-svc: deleted (not in incoming)
Expect(resultPolicy).NotTo(HaveKey("legacy-svc"))
})
It("should return incoming when existing JSON is invalid", func() {
existing := `invalid json`
incoming := `{"calibreweb-svc":{"default_policy":"system","sub_policies":null,"one_time":false,"valid_duration":0}}`
result := mergePolicySettings(existing, incoming)
Expect(result).To(Equal(incoming))
})
It("should return existing when incoming JSON is invalid", func() {
existing := `{"calibreweb-svc":{"default_policy":"system","sub_policies":null,"one_time":false,"valid_duration":0}}`
incoming := `invalid json`
result := mergePolicySettings(existing, incoming)
Expect(result).To(Equal(existing))
})
})

View File

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

View File

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

View File

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

View File

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