mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-20 13:30:46 +02:00
admin_script: make run interval configurable
This commit is contained in:
@@ -21,6 +21,8 @@ import (
|
||||
const (
|
||||
adminScriptJobType = "admin_script"
|
||||
maxAdminScriptOutputBytes = 16 * 1024
|
||||
defaultAdminScriptRunMins = 17
|
||||
adminScriptDetectTickSecs = 60
|
||||
)
|
||||
|
||||
const defaultAdminScript = `volume.balance -apply
|
||||
@@ -77,6 +79,15 @@ func (h *AdminScriptHandler) Descriptor() *plugin_pb.JobTypeDescriptor {
|
||||
Widget: plugin_pb.ConfigWidget_CONFIG_WIDGET_TEXTAREA,
|
||||
Required: true,
|
||||
},
|
||||
{
|
||||
Name: "run_interval_minutes",
|
||||
Label: "Run Interval (minutes)",
|
||||
Description: "Minimum interval between successful admin script runs.",
|
||||
FieldType: plugin_pb.ConfigFieldType_CONFIG_FIELD_TYPE_INT64,
|
||||
Widget: plugin_pb.ConfigWidget_CONFIG_WIDGET_NUMBER,
|
||||
Required: true,
|
||||
MinValue: &plugin_pb.ConfigValue{Kind: &plugin_pb.ConfigValue_Int64Value{Int64Value: 1}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -84,11 +95,14 @@ func (h *AdminScriptHandler) Descriptor() *plugin_pb.JobTypeDescriptor {
|
||||
"script": {
|
||||
Kind: &plugin_pb.ConfigValue_StringValue{StringValue: defaultAdminScript},
|
||||
},
|
||||
"run_interval_minutes": {
|
||||
Kind: &plugin_pb.ConfigValue_Int64Value{Int64Value: defaultAdminScriptRunMins},
|
||||
},
|
||||
},
|
||||
},
|
||||
AdminRuntimeDefaults: &plugin_pb.AdminRuntimeDefaults{
|
||||
Enabled: false,
|
||||
DetectionIntervalSeconds: 24 * 60 * 60,
|
||||
DetectionIntervalSeconds: adminScriptDetectTickSecs,
|
||||
DetectionTimeoutSeconds: 300,
|
||||
MaxJobsPerDetection: 1,
|
||||
GlobalExecutionConcurrency: 1,
|
||||
@@ -113,6 +127,31 @@ func (h *AdminScriptHandler) Detect(ctx context.Context, request *plugin_pb.RunD
|
||||
|
||||
script := normalizeAdminScript(readStringConfig(request.GetAdminConfigValues(), "script", ""))
|
||||
scriptName := strings.TrimSpace(readStringConfig(request.GetAdminConfigValues(), "script_name", ""))
|
||||
runIntervalMinutes := readAdminScriptRunIntervalMinutes(request.GetAdminConfigValues())
|
||||
if shouldSkipDetectionByInterval(request.GetLastSuccessfulRun(), runIntervalMinutes*60) {
|
||||
_ = sender.SendActivity(buildDetectorActivity(
|
||||
"skipped_by_interval",
|
||||
fmt.Sprintf("ADMIN SCRIPT: Detection skipped due to run interval (%dm)", runIntervalMinutes),
|
||||
map[string]*plugin_pb.ConfigValue{
|
||||
"run_interval_minutes": {
|
||||
Kind: &plugin_pb.ConfigValue_Int64Value{Int64Value: int64(runIntervalMinutes)},
|
||||
},
|
||||
},
|
||||
))
|
||||
if err := sender.SendProposals(&plugin_pb.DetectionProposals{
|
||||
JobType: adminScriptJobType,
|
||||
Proposals: []*plugin_pb.JobProposal{},
|
||||
HasMore: false,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return sender.SendComplete(&plugin_pb.DetectionComplete{
|
||||
JobType: adminScriptJobType,
|
||||
Success: true,
|
||||
TotalProposals: 0,
|
||||
})
|
||||
}
|
||||
|
||||
commands := parseAdminScriptCommands(script)
|
||||
execCount := countExecutableCommands(commands)
|
||||
if execCount == 0 {
|
||||
@@ -346,6 +385,14 @@ func (h *AdminScriptHandler) Execute(ctx context.Context, request *plugin_pb.Exe
|
||||
})
|
||||
}
|
||||
|
||||
func readAdminScriptRunIntervalMinutes(values map[string]*plugin_pb.ConfigValue) int {
|
||||
runIntervalMinutes := int(readInt64Config(values, "run_interval_minutes", defaultAdminScriptRunMins))
|
||||
if runIntervalMinutes <= 0 {
|
||||
return defaultAdminScriptRunMins
|
||||
}
|
||||
return runIntervalMinutes
|
||||
}
|
||||
|
||||
type adminScriptCommand struct {
|
||||
Name string
|
||||
Args []string
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package pluginworker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/plugin_pb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
func TestAdminScriptDescriptorDefaults(t *testing.T) {
|
||||
descriptor := NewAdminScriptHandler(nil).Descriptor()
|
||||
if descriptor == nil {
|
||||
t.Fatalf("expected descriptor")
|
||||
}
|
||||
if descriptor.AdminRuntimeDefaults == nil {
|
||||
t.Fatalf("expected admin runtime defaults")
|
||||
}
|
||||
if descriptor.AdminRuntimeDefaults.DetectionIntervalSeconds != adminScriptDetectTickSecs {
|
||||
t.Fatalf("unexpected detection interval seconds: got=%d want=%d",
|
||||
descriptor.AdminRuntimeDefaults.DetectionIntervalSeconds, adminScriptDetectTickSecs)
|
||||
}
|
||||
if descriptor.AdminConfigForm == nil {
|
||||
t.Fatalf("expected admin config form")
|
||||
}
|
||||
runInterval := readInt64Config(descriptor.AdminConfigForm.DefaultValues, "run_interval_minutes", 0)
|
||||
if runInterval != defaultAdminScriptRunMins {
|
||||
t.Fatalf("unexpected run_interval_minutes default: got=%d want=%d", runInterval, defaultAdminScriptRunMins)
|
||||
}
|
||||
script := readStringConfig(descriptor.AdminConfigForm.DefaultValues, "script", "")
|
||||
if strings.TrimSpace(script) == "" {
|
||||
t.Fatalf("expected non-empty default script")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminScriptDetectSkipsByRunInterval(t *testing.T) {
|
||||
handler := NewAdminScriptHandler(nil)
|
||||
sender := &recordingDetectionSender{}
|
||||
err := handler.Detect(context.Background(), &plugin_pb.RunDetectionRequest{
|
||||
JobType: adminScriptJobType,
|
||||
LastSuccessfulRun: timestamppb.New(time.Now().Add(-2 * time.Minute)),
|
||||
AdminConfigValues: map[string]*plugin_pb.ConfigValue{
|
||||
"script": {
|
||||
Kind: &plugin_pb.ConfigValue_StringValue{StringValue: defaultAdminScript},
|
||||
},
|
||||
"run_interval_minutes": {
|
||||
Kind: &plugin_pb.ConfigValue_Int64Value{Int64Value: 17},
|
||||
},
|
||||
},
|
||||
}, sender)
|
||||
if err != nil {
|
||||
t.Fatalf("detect returned err = %v", err)
|
||||
}
|
||||
if sender.proposals == nil {
|
||||
t.Fatalf("expected proposals message")
|
||||
}
|
||||
if len(sender.proposals.Proposals) != 0 {
|
||||
t.Fatalf("expected zero proposals, got %d", len(sender.proposals.Proposals))
|
||||
}
|
||||
if sender.complete == nil || !sender.complete.Success {
|
||||
t.Fatalf("expected successful completion message")
|
||||
}
|
||||
if len(sender.events) == 0 {
|
||||
t.Fatalf("expected detector activity events")
|
||||
}
|
||||
if !strings.Contains(sender.events[0].Message, "run interval") {
|
||||
t.Fatalf("unexpected skip message: %q", sender.events[0].Message)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminScriptDetectCreatesProposalWhenIntervalElapsed(t *testing.T) {
|
||||
handler := NewAdminScriptHandler(nil)
|
||||
sender := &recordingDetectionSender{}
|
||||
err := handler.Detect(context.Background(), &plugin_pb.RunDetectionRequest{
|
||||
JobType: adminScriptJobType,
|
||||
LastSuccessfulRun: timestamppb.New(time.Now().Add(-20 * time.Minute)),
|
||||
AdminConfigValues: map[string]*plugin_pb.ConfigValue{
|
||||
"script": {
|
||||
Kind: &plugin_pb.ConfigValue_StringValue{StringValue: defaultAdminScript},
|
||||
},
|
||||
"run_interval_minutes": {
|
||||
Kind: &plugin_pb.ConfigValue_Int64Value{Int64Value: 17},
|
||||
},
|
||||
},
|
||||
}, sender)
|
||||
if err != nil {
|
||||
t.Fatalf("detect returned err = %v", err)
|
||||
}
|
||||
if sender.proposals == nil {
|
||||
t.Fatalf("expected proposals message")
|
||||
}
|
||||
if len(sender.proposals.Proposals) != 1 {
|
||||
t.Fatalf("expected one proposal, got %d", len(sender.proposals.Proposals))
|
||||
}
|
||||
if sender.complete == nil || !sender.complete.Success || sender.complete.TotalProposals != 1 {
|
||||
t.Fatalf("unexpected completion message: %+v", sender.complete)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user