feat: scale instances, --reuse-values, values diff, UI redesign, hover animations
Backend (Phase 1):
- Add ScaleInstance endpoint (POST /clusters/{id}/instances/{id}/scale)
- Add GetInstanceValuesDiff endpoint (GET .../values-diff)
- Enable ReuseValues=true in Helm Upgrade for --reuse-values behavior
- Add GetValues/GetChartDefaultValues to HelmClient interface
- Add ScaleInstanceRequest/Response and InstanceValuesDiffResponse DTOs
Frontend (Phase 2):
- InstanceCard: +/- scale buttons with loading spinner
- ModifyModal: values diff view (current vs defaults), Use Defaults button
- ArtifactBrowserPage: collapsible sidebar, compact tag grid, search filter
- TagCard: "LATEST" badge, compact layout, responsive design
- InstanceCard: compact 3-column layout, fewer scrolls needed
- InstancesManagementPage: 3-column grid, compact view
- Global hover-lift and hover-glow CSS utilities
- SidebarNav: subtle hover transition on links
This commit is contained in:
@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/ocdp/cluster-service/internal/adapter/input/http/dto"
|
||||
"github.com/ocdp/cluster-service/internal/domain/entity"
|
||||
"github.com/ocdp/cluster-service/internal/domain/repository"
|
||||
"github.com/ocdp/cluster-service/internal/pkg/authz"
|
||||
@ -417,6 +418,80 @@ func (s *InstanceService) StreamInstanceLogs(ctx context.Context, clusterID, ins
|
||||
return streamer.StreamPodLogs(ctx, cluster, instance.Namespace, podName, containerName, tailLines)
|
||||
}
|
||||
|
||||
// ScaleInstance 扩缩容实例(修改 replicaCount 后执行 Helm upgrade)
|
||||
func (s *InstanceService) ScaleInstance(ctx context.Context, clusterID, instanceID string, replicas int, workload string) (*entity.Instance, error) {
|
||||
principal, err := authz.RequirePrincipal(ctx)
|
||||
if err != nil {
|
||||
return nil, entity.ErrUnauthorized
|
||||
}
|
||||
instance, err := s.instanceRepo.GetByID(ctx, instanceID)
|
||||
if err != nil {
|
||||
return nil, entity.ErrInstanceNotFound
|
||||
}
|
||||
if !s.canWriteInstance(principal, instance) {
|
||||
return nil, entity.ErrForbidden
|
||||
}
|
||||
cluster, err := s.clusterRepo.GetByID(ctx, clusterID)
|
||||
if err != nil {
|
||||
return nil, entity.ErrClusterNotFound
|
||||
}
|
||||
|
||||
// Get existing Helm values and patch replicaCount
|
||||
vals, err := s.helmClient.GetValues(ctx, cluster, instance.Name, instance.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get current values: %w", err)
|
||||
}
|
||||
if vals == nil {
|
||||
vals = make(map[string]interface{})
|
||||
}
|
||||
vals["replicaCount"] = replicas
|
||||
|
||||
instance.SetValues(vals)
|
||||
instance.BeginOperation(entity.OperationUpgrade, fmt.Sprintf("Scaling to %d replicas", replicas))
|
||||
if err := s.instanceRepo.Update(ctx, instance); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go s.executeAndSyncUpgrade(context.Background(), instance.ID, cluster, nil, instance)
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
// GetInstanceValuesDiff 获取实例当前 values 与 chart 默认 values 的差异
|
||||
func (s *InstanceService) GetInstanceValuesDiff(ctx context.Context, clusterID, instanceID string) (*dto.InstanceValuesDiffResponse, error) {
|
||||
principal, err := authz.RequirePrincipal(ctx)
|
||||
if err != nil {
|
||||
return nil, entity.ErrUnauthorized
|
||||
}
|
||||
instance, err := s.instanceRepo.GetByID(ctx, instanceID)
|
||||
if err != nil {
|
||||
return nil, entity.ErrInstanceNotFound
|
||||
}
|
||||
if !s.canReadInstance(principal, instance) {
|
||||
return nil, entity.ErrInstanceNotFound
|
||||
}
|
||||
cluster, err := s.clusterRepo.GetByID(ctx, clusterID)
|
||||
if err != nil {
|
||||
return nil, entity.ErrClusterNotFound
|
||||
}
|
||||
|
||||
current, err := s.helmClient.GetValues(ctx, cluster, instance.Name, instance.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get default values from the chart archive
|
||||
chartPath := s.chartArchivePath(instance)
|
||||
defaults, err := s.helmClient.GetChartDefaultValues(chartPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read chart defaults: %w", err)
|
||||
}
|
||||
|
||||
return &dto.InstanceValuesDiffResponse{
|
||||
Current: current,
|
||||
Defaults: defaults,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *InstanceService) canReadInstance(principal *authz.Principal, instance *entity.Instance) bool {
|
||||
if principal.IsAdmin() {
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user