fix(files): 模组包上传也逐段核对 SHA-256、长文件名能写、卡住的导出按时停掉、世界被占用时文件页说明原因并等它结束
This commit is contained in:
59 files changed
+2538
-338
No files matched your search
@@ -43,6 +43,17 @@ const (
|
||||
// process listing on the node would show it.
|
||||
const TokenEnv = "FELIS_EXPORT_TOKEN"
|
||||
|
||||
// The Job's PUT goes chunked, so that it can end with a trailer: DigestTrailer
|
||||
// carries the SHA-256 of every byte it sent (sha-256=:<base64>:, RFC 9530),
|
||||
// which felis-api checks before the last of them reaches the browser.
|
||||
// LengthHeader declares the length up front when the Job knows it (one file),
|
||||
// which chunked encoding cannot carry, so the browser still gets a
|
||||
// Content-Length.
|
||||
const (
|
||||
DigestTrailer = "Content-Digest"
|
||||
LengthHeader = "X-Felis-Export-Length"
|
||||
)
|
||||
|
||||
// JobParams are the rendered inputs to an export Job. ExportJob is a pure
|
||||
// function of them, so the Job shape is unit-tested without a cluster.
|
||||
type JobParams struct {
|
||||
|
||||
@@ -8,8 +8,11 @@ import (
|
||||
"time"
|
||||
|
||||
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/runtime"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
k8stesting "k8s.io/client-go/testing"
|
||||
)
|
||||
|
||||
const secretToken = "5ecret5ecret5ecret5ecret5ecret5ecret5ecret5ecret5ecret5ecret5ecr"
|
||||
@@ -236,3 +239,34 @@ func TestStartCreatesTheJob(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A Job felis-api gave up on goes with its Pods, and one already gone is no
|
||||
// error: the sweep that stops it may run after the Job's own TTL took it.
|
||||
func TestStopDeletesTheJobAndItsPods(t *testing.T) {
|
||||
cs := fake.NewSimpleClientset()
|
||||
var policy metav1.DeletionPropagation
|
||||
cs.PrependReactor("delete", "jobs", func(a k8stesting.Action) (bool, runtime.Object, error) {
|
||||
if o := a.(k8stesting.DeleteActionImpl).GetDeleteOptions().PropagationPolicy; o != nil {
|
||||
policy = *o
|
||||
}
|
||||
return false, nil, nil
|
||||
})
|
||||
e := New(cs, Config{Image: "felis:1", BackupPVC: "felis-backups"})
|
||||
name, err := e.Start(context.Background(), Request{Server: "survival", Mode: ModeWorld, ID: "0011223344556677",
|
||||
TargetURL: "http://api:8081/x", Token: secretToken})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := e.Stop(context.Background(), name); err != nil {
|
||||
t.Fatalf("Stop: %v", err)
|
||||
}
|
||||
if _, err := cs.BatchV1().Jobs("minecraft").Get(context.Background(), name, metav1.GetOptions{}); !apierrors.IsNotFound(err) {
|
||||
t.Fatalf("the Job is still there: %v", err)
|
||||
}
|
||||
if policy != metav1.DeletePropagationBackground {
|
||||
t.Errorf("propagation = %q, want Background so its Pods go too", policy)
|
||||
}
|
||||
if err := e.Stop(context.Background(), name); err != nil {
|
||||
t.Errorf("stopping a Job already gone: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"time"
|
||||
|
||||
"felis.lolicon.best/internal/naming"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
@@ -130,3 +131,15 @@ func (e *Exporter) Start(ctx context.Context, r Request) (string, error) {
|
||||
}
|
||||
return job.Name, nil
|
||||
}
|
||||
|
||||
// Stop deletes an export Job felis-api has given up on, so a Pod that never got
|
||||
// going stops holding the world. Its Pods go with it; one already gone is not
|
||||
// an error.
|
||||
func (e *Exporter) Stop(ctx context.Context, job string) error {
|
||||
bg := metav1.DeletePropagationBackground
|
||||
err := e.cs.BatchV1().Jobs(e.cfg.Namespace).Delete(ctx, job, metav1.DeleteOptions{PropagationPolicy: &bg})
|
||||
if err != nil && !apierrors.IsNotFound(err) {
|
||||
return fmt.Errorf("worldexport: delete export job: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user