diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 731baaa3..064a5d2a 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -37,7 +37,12 @@ sudo mkdir /etc/overlaybd-snapshotter sudo cat <<-EOF | sudo tee /etc/overlaybd-snapshotter/config.json { "root": "/var/lib/overlaybd/", - "address": "/run/overlaybd-snapshotter/overlaybd.sock" + "address": "/run/overlaybd-snapshotter/overlaybd.sock", + "experimental": { + "enabled": false, + "preAuth": true, + "overlaybdApiServer": "http://127.0.0.1:9862" + } } EOF ``` diff --git a/docs/DOCKER.md b/docs/DOCKER.md index a8cd1981..579ae774 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -31,7 +31,12 @@ Edit `/etc/overlaybd-snapshotter/config.json`: "verbose": "info", "logReportCaller": false, "autoRemoveDev": true, - "mirrorRegistry": [] + "mirrorRegistry": [], + "experimental": { + "enabled": false, + "preAuth": true, + "overlaybdApiServer": "http://127.0.0.1:9862" + } } ``` diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md index ac979818..6ec6925c 100644 --- a/docs/QUICKSTART.md +++ b/docs/QUICKSTART.md @@ -78,7 +78,12 @@ The config file is `/etc/overlaybd-snapshotter/config.json`. Please create the f "host": "registry-1.docker.io", "insecure": false } - ] + ], + "experimental": { + "enabled": false, + "preAuth": true, + "overlaybdApiServer": "http://127.0.0.1:9862" + } } ``` | Field | Description | @@ -93,6 +98,9 @@ The config file is `/etc/overlaybd-snapshotter/config.json`. Please create the f | `exporterConfig.enable` | whether or not create a server to show Prometheus metrics | | `exporterConfig.uriPrefix` | URI prefix for export metrics, default `/metrics` | | `exporterConfig.port` | port for http server to show metrics, default `9863` | +| `experimental.enabled` | enable experimental features | +| `experimental.preAuth` | enable overlaybd API server pre-auth notification | +| `experimental.overlaybdApiServer` | overlaybd API server address, default `http://127.0.0.1:9862` | | `mirrorRegistry` | an arrary of mirror registries | | `mirrorRegistry.host` | host address, eg. `registry-1.docker.io`` | | `mirrorRegistry.insecure` | `true` or `false` | diff --git a/pkg/snapshot/overlay.go b/pkg/snapshot/overlay.go index 0f6e97e1..38d0b25b 100644 --- a/pkg/snapshot/overlay.go +++ b/pkg/snapshot/overlay.go @@ -22,6 +22,9 @@ import ( "encoding/binary" "errors" "fmt" + "io" + "net/http" + "net/url" "os" "os/exec" "path" @@ -86,6 +89,12 @@ type Registry struct { Insecure bool `json:"insecure"` } +type ExperimentalConfig struct { + Enabled bool `json:"enabled"` + PreAuth bool `json:"preAuth"` + OverlaybdApiServer string `json:"overlaybdApiServer"` +} + type BootConfig struct { AsyncRemove bool `json:"asyncRemove"` Address string `json:"address"` @@ -102,6 +111,7 @@ type BootConfig struct { Tenant int `json:"tenant"` // do not set this if only a single snapshotter service in the host TurboFsType []string `json:"turboFsType"` RuntimeType string `json:"runtimeType"` // "containerd" (default) or "docker" + Experimental ExperimentalConfig `json:"experimental"` } func DefaultBootConfig() *BootConfig { @@ -126,6 +136,11 @@ func DefaultBootConfig() *BootConfig { "ext4", }, RuntimeType: "containerd", + Experimental: ExperimentalConfig{ + Enabled: false, + PreAuth: true, + OverlaybdApiServer: "http://127.0.0.1:9862", + }, } } @@ -206,6 +221,7 @@ type snapshotter struct { turboFsType []string asyncRemove bool runtimeType string + experimental ExperimentalConfig quotaDriver *diskquota.PrjQuotaDriver quotaSize string @@ -270,6 +286,7 @@ func NewSnapshotter(bootConfig *BootConfig, opts ...Opt) (snapshots.Snapshotter, turboFsType: bootConfig.TurboFsType, tenant: bootConfig.Tenant, quotaSize: bootConfig.RootfsQuota, + experimental: bootConfig.Experimental, quotaDriver: &diskquota.PrjQuotaDriver{ QuotaIDs: make(map[uint32]struct{}), }, @@ -423,7 +440,78 @@ func (o *snapshotter) isPrepareRootfs(info snapshots.Info) bool { return true } +func (o *snapshotter) preAuthEnabled() bool { + return o.experimental.Enabled && o.experimental.PreAuth +} + +func (o *snapshotter) notifyOverlaybdAPIServer(configPath string) { + if !o.preAuthEnabled() || configPath == "" { + return + } + + go func() { + address := strings.TrimSpace(o.experimental.OverlaybdApiServer) + if address == "" { + log.L.Warn("overlaybd api server address is empty") + return + } + + authBase, err := url.Parse(address) + if err != nil { + log.L.WithError(err).Warnf("failed to parse overlaybd api server address %q", address) + return + } + + authURL := authBase.ResolveReference(&url.URL{Path: "/auth"}) + query := authURL.Query() + query.Set("config", configPath) + authURL.RawQuery = query.Encode() + + log.L.Infof("overlaybd api request: %s", authURL.String()) + + reqCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + req, err := http.NewRequestWithContext(reqCtx, http.MethodGet, authURL.String(), nil) + if err != nil { + log.L.WithError(err).Warnf("failed to create overlaybd api request for %s", authURL.String()) + return + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + log.L.WithError(err).Warnf("overlaybd api request failed: %s", authURL.String()) + return + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + log.L.WithError(err).Warnf("failed to read overlaybd api response from %s", authURL.String()) + return + } + + log.L.Infof("overlaybd api response: url=%s status=%s body=%s", authURL.String(), resp.Status, strings.TrimSpace(string(body))) + }() +} + func (o *snapshotter) createMountPoint(ctx context.Context, kind snapshots.Kind, key string, parent string, opts ...snapshots.Opt) (_ []mount.Mount, retErr error) { + if parent != "" && o.preAuthEnabled() { + readCtx, readTxn, err := o.ms.TransactionContext(ctx, false) + if err != nil { + return nil, err + } + + parentID, _, _, err := storage.GetInfo(readCtx, parent) + if rerr := readTxn.Rollback(); rerr != nil { + log.G(ctx).WithError(rerr).Warn("failed to rollback read transaction") + } + if err != nil { + return nil, fmt.Errorf("failed to get info of parent snapshot %s: %w", parent, err) + } + + o.notifyOverlaybdAPIServer(o.overlaybdConfPath(parentID)) + } ctx, t, err := o.ms.TransactionContext(ctx, true) if err != nil { @@ -631,6 +719,7 @@ func (o *snapshotter) createMountPoint(ctx context.Context, kind snapshots.Kind, } log.G(ctx).Debugf("attachAndMountBlockDevice (obdID: %s, writeType: %s, fsType %s, targetPath: %s)", obdID, writeType, fsType, overlaybdTargetPath(obdHbaNum(o.tenant), obdID)) + o.notifyOverlaybdAPIServer(o.overlaybdConfPath(obdID)) if err = o.attachAndMountBlockDevice(ctx, obdID, writeType, fsType, parent == ""); err != nil { log.G(ctx).Errorf("%v", err) return nil, fmt.Errorf("failed to attach and mount for snapshot %v: %w", obdID, err) diff --git a/pkg/snapshot/overlay_test.go b/pkg/snapshot/overlay_test.go index 1f38b626..5ca69c71 100644 --- a/pkg/snapshot/overlay_test.go +++ b/pkg/snapshot/overlay_test.go @@ -38,6 +38,62 @@ func newSnapshotterWithOpts(opts ...Opt) testsuite.SnapshotterFunc { } } +func TestDefaultBootConfigExperimental(t *testing.T) { + cfg := DefaultBootConfig() + + if cfg.Experimental.Enabled { + t.Fatal("expected experimental to be disabled by default") + } + if !cfg.Experimental.PreAuth { + t.Fatal("expected preAuth to be enabled by default") + } + if cfg.Experimental.OverlaybdApiServer != "http://127.0.0.1:9862" { + t.Fatalf("unexpected overlaybd api server address %q", cfg.Experimental.OverlaybdApiServer) + } +} + +func TestPreAuthEnabled(t *testing.T) { + tests := []struct { + name string + experimental ExperimentalConfig + want bool + }{ + { + name: "experimental disabled", + experimental: ExperimentalConfig{ + Enabled: false, + PreAuth: true, + }, + want: false, + }, + { + name: "preAuth disabled", + experimental: ExperimentalConfig{ + Enabled: true, + PreAuth: false, + }, + want: false, + }, + { + name: "experimental and preAuth enabled", + experimental: ExperimentalConfig{ + Enabled: true, + PreAuth: true, + }, + want: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sn := &snapshotter{experimental: tt.experimental} + if got := sn.preAuthEnabled(); got != tt.want { + t.Fatalf("preAuthEnabled() = %v, want %v", got, tt.want) + } + }) + } +} + func TestBasicSnapshotterOnOverlayFS(t *testing.T) { testutil.RequiresRoot(t) testsuite.SnapshotterSuite(t, "overlaybd-on-overlayFS", newSnapshotterWithOpts()) diff --git a/script/config.json b/script/config.json index e3ff5d50..133037fe 100644 --- a/script/config.json +++ b/script/config.json @@ -6,5 +6,10 @@ "runtimeType": "containerd", "logReportCaller": false, "autoRemoveDev": false, - "mirrorRegistry": [] + "mirrorRegistry": [], + "experimental": { + "enabled": false, + "preAuth": true, + "overlaybdApiServer": "http://127.0.0.1:9862" + } }