Compare commits

...

8 Commits

Author SHA1 Message Date
hysyeah
afc30bf263 fix: for an app with env will create namespace at first (#2263) 2025-12-17 21:41:46 +08:00
hys
b990d7a021 fix: for an app with env will create namespace at first 2025-12-17 21:34:09 +08:00
hys
ee44bb773d fix: concurrency cause two app in downloading state 2025-12-17 21:26:42 +08:00
hysyeah
e6a77e5c5a fix: compatible with legacy mongodb uninstall (#2257) 2025-12-17 17:21:00 +08:00
hys
03d9b0016c fix: compatible with legacy mongodb uninstall 2025-12-17 12:36:30 +08:00
hys
7ca2cadb2d fix: update app-service,image-service image tag 2025-12-16 21:35:11 +08:00
hysyeah
106ebaf41f fix: helm failed release (#2251) 2025-12-16 21:28:31 +08:00
hys
39f55b6681 fix: helm failed release 2025-12-16 21:18:02 +08:00
15 changed files with 219 additions and 46 deletions

View File

@@ -170,7 +170,7 @@ spec:
priorityClassName: "system-cluster-critical"
containers:
- name: app-service
image: beclab/app-service:0.4.62
image: beclab/app-service:0.4.63
imagePullPolicy: IfNotPresent
securityContext:
runAsUser: 0
@@ -393,7 +393,7 @@ spec:
priorityClassName: "system-cluster-critical"
containers:
- name: image-service
image: beclab/image-service:0.4.48
image: beclab/image-service:0.4.63
imagePullPolicy: IfNotPresent
securityContext:
runAsUser: 0

View File

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

View File

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

View File

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

View File

@@ -174,12 +174,6 @@ func (p *DownloadingApp) exec(ctx context.Context) error {
return err
}
values["sysVersion"] = terminus.Spec.Version
refs, err := p.getRefsForImageManager(appConfig, values)
if err != nil {
klog.Errorf("get image refs from resources failed %v", err)
return err
}
nodeInfo, err := utils.GetNodeInfo(ctx)
if err != nil {
klog.Errorf("failed to get node info %v", err)
@@ -187,6 +181,12 @@ func (p *DownloadingApp) exec(ctx context.Context) error {
}
values["nodes"] = nodeInfo
refs, err := p.getRefsForImageManager(appConfig, values)
if err != nil {
klog.Errorf("get image refs from resources failed %v", err)
return err
}
err = p.imageClient.Create(ctx, p.manager, refs)
if err != nil {

View File

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

View File

@@ -7,7 +7,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
)
var _ OperationApp = &UpgradeFailedApp{}
var _ OperationApp = &InstallingCancelFailedApp{}
type InstallingCancelFailedApp struct {
UninstallFailedApp

View File

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

View File

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

View File

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

View File

@@ -7,10 +7,10 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
)
var _ OperationApp = &UpgradeFailedApp{}
var _ StatefulApp = &UpgradeFailedApp{}
type UpgradeFailedApp struct {
SuspendFailedApp
*DoNothingApp
}
func NewUpgradeFailedApp(c client.Client,
@@ -18,13 +18,10 @@ func NewUpgradeFailedApp(c client.Client,
return appFactory.New(c, manager, 0,
func(c client.Client, manager *appsv1.ApplicationManager, ttl time.Duration) StatefulApp {
return &UpgradeFailedApp{
SuspendFailedApp: SuspendFailedApp{
&baseOperationApp{
ttl: ttl,
baseStatefulApp: &baseStatefulApp{
manager: manager,
client: c,
},
DoNothingApp: &DoNothingApp{
baseStatefulApp: &baseStatefulApp{
manager: manager,
client: c,
},
},
}

View File

@@ -87,6 +87,7 @@ func UpgradeCharts(ctx context.Context, actionConfig *action.Configuration, sett
client.Namespace = namespace
client.Timeout = 300 * time.Second
client.Recreate = false
client.Atomic = true
if reuseValue {
client.ReuseValues = true
}

View File

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

View File

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

View File

@@ -3,12 +3,12 @@ package security
import (
"bytetrade.io/web3os/app-service/pkg/constants"
"bytetrade.io/web3os/app-service/pkg/utils"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/utils/pointer"
netv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/utils/pointer"
)
const (