mirror of
https://github.com/hanzo-fi/payments.git
synced 2026-10-30 20:31:20 +00:00
206 lines
4.5 KiB
Go
206 lines
4.5 KiB
Go
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/hanzo-fi/go-libs/v5/pkg/fx/workflowfx"
|
|
"github.com/hanzo-fi/go-libs/v5/pkg/observe/log"
|
|
"github.com/hanzo-fi/go-libs/v5/pkg/workflow/temporal"
|
|
"github.com/hanzo-fi/payments/internal/connectors/engine"
|
|
"github.com/spf13/cobra"
|
|
"go.opentelemetry.io/otel/metric"
|
|
"go.opentelemetry.io/otel/metric/noop"
|
|
"go.temporal.io/api/enums/v1"
|
|
"go.temporal.io/api/workflow/v1"
|
|
"go.temporal.io/api/workflowservice/v1"
|
|
"go.temporal.io/sdk/client"
|
|
"go.uber.org/fx"
|
|
)
|
|
|
|
func newPurge() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "purge",
|
|
Short: "Launch purge command to clean temporal",
|
|
SilenceUsage: true,
|
|
RunE: runPurge(),
|
|
}
|
|
|
|
temporal.AddFlags(cmd.Flags())
|
|
|
|
return cmd
|
|
}
|
|
|
|
func runPurge() func(cmd *cobra.Command, args []string) error {
|
|
return func(cmd *cobra.Command, args []string) error {
|
|
setLogger()
|
|
|
|
return purgeOptions(cmd)
|
|
}
|
|
}
|
|
|
|
func purgeOptions(cmd *cobra.Command) error {
|
|
temporalNamespace, _ := cmd.Flags().GetString(temporal.TemporalNamespaceFlag)
|
|
stack, _ := cmd.Flags().GetString(StackFlag)
|
|
logger := logging.NewDefaultLogger(cmd.OutOrStdout(), true, true, false)
|
|
|
|
var purge *Purge
|
|
options := []fx.Option{
|
|
fx.Supply(fx.Annotate(logger, fx.As(new(logging.Logger)))),
|
|
workflowfx.TemporalClientModuleFromFlags(
|
|
cmd,
|
|
engine.Tracer,
|
|
temporal.SearchAttributes{
|
|
SearchAttributes: engine.SearchAttributes,
|
|
},
|
|
),
|
|
fx.Provide(func() metric.MeterProvider {
|
|
return noop.NewMeterProvider()
|
|
}),
|
|
fx.Provide(NewPurge),
|
|
fx.Populate(&purge),
|
|
}
|
|
|
|
app := fx.New(options...)
|
|
if err := app.Start(cmd.Context()); err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
if err := app.Stop(context.Background()); err != nil {
|
|
logger.Errorf("failed to stop purge app: %s", err)
|
|
}
|
|
}()
|
|
|
|
return purge.clean(cmd.Context(), temporalNamespace, stack)
|
|
}
|
|
|
|
type Purge struct {
|
|
logger logging.Logger
|
|
temporalClient client.Client
|
|
}
|
|
|
|
func NewPurge(logger logging.Logger, temporalClient client.Client) *Purge {
|
|
return &Purge{
|
|
logger: logger,
|
|
temporalClient: temporalClient,
|
|
}
|
|
}
|
|
|
|
func (p *Purge) clean(
|
|
ctx context.Context,
|
|
temporalNamespace string,
|
|
stackName string,
|
|
) error {
|
|
if err := p.cleanTemporalSchedules(ctx, stackName); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := p.cleanTemporalWorkflows(ctx, temporalNamespace, stackName); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *Purge) cleanTemporalSchedules(
|
|
ctx context.Context,
|
|
stackName string,
|
|
) error {
|
|
// list schedules
|
|
listView, err := p.temporalClient.ScheduleClient().List(ctx, client.ScheduleListOptions{
|
|
PageSize: 500,
|
|
Query: fmt.Sprintf("Stack=\"%s\"", stackName),
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
wg := sync.WaitGroup{}
|
|
for listView.HasNext() {
|
|
s, err := listView.Next()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
wg.Add(1)
|
|
go func(s *client.ScheduleListEntry) {
|
|
defer wg.Done()
|
|
|
|
// get handle
|
|
handle := p.temporalClient.ScheduleClient().GetHandle(ctx, s.ID)
|
|
|
|
// delete schedule
|
|
if err := handle.Delete(ctx); err != nil {
|
|
p.logger.Errorf("failed to delete schedule %s: %v", s.ID, err)
|
|
return
|
|
}
|
|
}(s)
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *Purge) cleanTemporalWorkflows(
|
|
ctx context.Context,
|
|
temporalNamespace string,
|
|
stackName string,
|
|
) error {
|
|
var nextPageToken []byte
|
|
wg := sync.WaitGroup{}
|
|
for {
|
|
resp, err := p.temporalClient.WorkflowService().ListWorkflowExecutions(
|
|
ctx,
|
|
&workflowservice.ListWorkflowExecutionsRequest{
|
|
Namespace: temporalNamespace,
|
|
PageSize: 1000,
|
|
NextPageToken: nextPageToken,
|
|
Query: fmt.Sprintf("Stack=\"%s\"", stackName),
|
|
},
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
toClean := 0
|
|
for _, e := range resp.Executions {
|
|
if e.Status != enums.WORKFLOW_EXECUTION_STATUS_RUNNING {
|
|
continue
|
|
}
|
|
|
|
toClean++
|
|
wg.Add(1)
|
|
go func(e *workflow.WorkflowExecutionInfo) {
|
|
defer wg.Done()
|
|
|
|
// close workflow
|
|
_, err := p.temporalClient.WorkflowService().TerminateWorkflowExecution(
|
|
ctx,
|
|
&workflowservice.TerminateWorkflowExecutionRequest{
|
|
Namespace: temporalNamespace,
|
|
WorkflowExecution: e.Execution,
|
|
Reason: "stack delete",
|
|
},
|
|
)
|
|
if err != nil {
|
|
p.logger.Errorf("failed to terminate workflow %s: %v", e.Execution.GetWorkflowId(), err)
|
|
return
|
|
}
|
|
}(e)
|
|
}
|
|
|
|
if resp.NextPageToken == nil {
|
|
break
|
|
}
|
|
|
|
nextPageToken = resp.NextPageToken
|
|
p.logger.Infof("cleaned %d/1000 workflows, fetching next page", toClean)
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
return nil
|
|
}
|