Files
sub2api-cn-relay-manager/internal/provision/rollback_service.go
phamnazage-jpg 71cbaf5fa6 test(project): achieve ≥70% package coverage across all internal packages
- store/sqlite: 75.4% (repos + db coverage)
- host/sub2api: 80.8% (httptest mock server, pure function tests)
- app: 74.2% (handler error paths, NewActionSet closures)
- pack: 72.4%
- provision: 75.2%
- access: 77.3%
- config: 94.7% (lookup mock tests)

All tests pass: build, vet, race, coverage gates.
2026-05-15 19:26:25 +08:00

91 lines
2.6 KiB
Go

package provision
import (
"context"
"errors"
"fmt"
"sub2api-cn-relay-manager/internal/host/sub2api"
"sub2api-cn-relay-manager/internal/pack"
)
type rollbackHost interface {
ListManagedResources(ctx context.Context, req sub2api.ListManagedResourcesRequest) (sub2api.ManagedResourceSnapshot, error)
DeleteAccount(ctx context.Context, accountID string) error
DeletePlan(ctx context.Context, planID string) error
DeleteChannel(ctx context.Context, channelID string) error
DeleteGroup(ctx context.Context, groupID string) error
}
type RollbackRequest struct {
Provider pack.ProviderManifest
}
type RollbackReport struct {
AccountsDeleted int
PlansDeleted int
ChannelsDeleted int
GroupsDeleted int
}
type RollbackService struct {
host rollbackHost
}
func NewRollbackService(host rollbackHost) *RollbackService {
return &RollbackService{host: host}
}
func (s *RollbackService) Rollback(ctx context.Context, req RollbackRequest) (RollbackReport, error) {
if s.host == nil {
return RollbackReport{}, fmt.Errorf("rollback host is required")
}
names := SuggestResourceNames(req.Provider)
snapshot, err := s.host.ListManagedResources(ctx, sub2api.ListManagedResourcesRequest{
GroupName: names.Group,
ChannelName: names.Channel,
PlanName: names.Plan,
AccountNamePrefix: SuggestAccountNamePrefix(req.Provider),
})
if err != nil {
return RollbackReport{}, fmt.Errorf("list managed resources: %w", err)
}
var report RollbackReport
var errs []error
for index := len(snapshot.Accounts) - 1; index >= 0; index-- {
if err := s.host.DeleteAccount(ctx, snapshot.Accounts[index].ID); err != nil {
errs = append(errs, fmt.Errorf("delete account %s: %w", snapshot.Accounts[index].ID, err))
continue
}
report.AccountsDeleted++
}
for index := len(snapshot.Plans) - 1; index >= 0; index-- {
if err := s.host.DeletePlan(ctx, snapshot.Plans[index].ID); err != nil {
errs = append(errs, fmt.Errorf("delete plan %s: %w", snapshot.Plans[index].ID, err))
continue
}
report.PlansDeleted++
}
for index := len(snapshot.Channels) - 1; index >= 0; index-- {
if err := s.host.DeleteChannel(ctx, snapshot.Channels[index].ID); err != nil {
errs = append(errs, fmt.Errorf("delete channel %s: %w", snapshot.Channels[index].ID, err))
continue
}
report.ChannelsDeleted++
}
for index := len(snapshot.Groups) - 1; index >= 0; index-- {
if err := s.host.DeleteGroup(ctx, snapshot.Groups[index].ID); err != nil {
errs = append(errs, fmt.Errorf("delete group %s: %w", snapshot.Groups[index].ID, err))
continue
}
report.GroupsDeleted++
}
if len(errs) > 0 {
return report, errors.Join(errs...)
}
return report, nil
}