pkg/ioutils: move atomic file-writers to a separate (pkg/atomicwriter) package

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2024-12-28 17:06:23 +01:00
parent 641e2fca5b
commit 7864454792
15 changed files with 93 additions and 48 deletions

View File

@@ -31,8 +31,8 @@ import (
"github.com/docker/docker/layer"
libcontainerdtypes "github.com/docker/docker/libcontainerd/types"
"github.com/docker/docker/oci"
"github.com/docker/docker/pkg/atomicwriter"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/restartmanager"
"github.com/docker/docker/volume"
volumemounts "github.com/docker/docker/volume/mounts"
@@ -193,7 +193,7 @@ func (container *Container) toDisk() (*Container, error) {
}
// Save container settings
f, err := ioutils.NewAtomicFileWriter(pth, 0o600)
f, err := atomicwriter.New(pth, 0o600)
if err != nil {
return nil, err
}
@@ -273,7 +273,7 @@ func (container *Container) WriteHostConfig() (*containertypes.HostConfig, error
return nil, err
}
f, err := ioutils.NewAtomicFileWriter(pth, 0o600)
f, err := atomicwriter.New(pth, 0o600)
if err != nil {
return nil, err
}

View File

@@ -6,7 +6,7 @@ import (
"path/filepath"
"strings"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/atomicwriter"
)
// convertKVStringsToMap converts ["key=value"] to {"key":"value"}
@@ -44,7 +44,7 @@ func savePersistentState(root string, config nodeStartConfig) error {
if err != nil {
return err
}
return ioutils.AtomicWriteFile(filepath.Join(root, stateFile), dt, 0o600)
return atomicwriter.WriteFile(filepath.Join(root, stateFile), dt, 0o600)
}
func clearPersistentState(root string) error {

View File

@@ -23,9 +23,9 @@ import (
"github.com/docker/docker/internal/containerfs"
"github.com/docker/docker/internal/directory"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/atomicwriter"
"github.com/docker/docker/pkg/chrootarchive"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/parsers"
"github.com/docker/docker/quota"
"github.com/docker/go-units"
@@ -393,7 +393,7 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr
}
// Write link id to link file
if err := ioutils.AtomicWriteFile(path.Join(dir, "link"), []byte(lid), 0o644); err != nil {
if err := atomicwriter.WriteFile(path.Join(dir, "link"), []byte(lid), 0o644); err != nil {
return err
}
@@ -406,7 +406,7 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr
return err
}
if err := ioutils.AtomicWriteFile(path.Join(d.dir(parent), "committed"), []byte{}, 0o600); err != nil {
if err := atomicwriter.WriteFile(path.Join(d.dir(parent), "committed"), []byte{}, 0o600); err != nil {
return err
}
@@ -415,7 +415,7 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts) (retErr
return err
}
if lower != "" {
if err := ioutils.AtomicWriteFile(path.Join(dir, lowerFile), []byte(lower), 0o644); err != nil {
if err := atomicwriter.WriteFile(path.Join(dir, lowerFile), []byte(lower), 0o644); err != nil {
return err
}
}

View File

@@ -4,7 +4,7 @@ import (
"os"
"path/filepath"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/atomicwriter"
"github.com/google/uuid"
"github.com/pkg/errors"
)
@@ -24,7 +24,7 @@ func LoadOrCreateID(root string) (string, error) {
idb, err := os.ReadFile(idPath)
if os.IsNotExist(err) {
id = uuid.New().String()
if err := ioutils.AtomicWriteFile(idPath, []byte(id), os.FileMode(0o600)); err != nil {
if err := atomicwriter.WriteFile(idPath, []byte(id), os.FileMode(0o600)); err != nil {
return "", errors.Wrap(err, "error saving ID file")
}
} else if err != nil {

View File

@@ -21,7 +21,7 @@ import (
"github.com/docker/docker/daemon/config"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/libcontainerd/shimopts"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/atomicwriter"
"github.com/opencontainers/runtime-spec/specs-go/features"
"github.com/pkg/errors"
)
@@ -191,7 +191,7 @@ func wrapRuntime(dir, name, binary string, args []string) (string, error) {
// containers.
suffix := base32Disemvoweled.EncodeToString(sum.Sum(nil))
scriptPath := filepath.Join(dir, name+"."+suffix)
if err := ioutils.AtomicWriteFile(scriptPath, wrapper.Bytes(), 0o700); err != nil {
if err := atomicwriter.WriteFile(scriptPath, wrapper.Bytes(), 0o700); err != nil {
return "", err
}
return scriptPath, nil

View File

@@ -5,7 +5,7 @@ import (
"path/filepath"
"sync"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/atomicwriter"
)
// Store implements a K/V store for mapping distribution-related IDs
@@ -60,7 +60,7 @@ func (store *FSMetadataStore) Set(namespace, key string, value []byte) error {
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
return err
}
return ioutils.AtomicWriteFile(path, value, 0o644)
return atomicwriter.WriteFile(path, value, 0o644)
}
// Delete removes data indexed by namespace and key. The data file named after

View File

@@ -8,7 +8,7 @@ import (
"sync"
"github.com/containerd/log"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/atomicwriter"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
)
@@ -118,7 +118,7 @@ func (s *fs) Set(data []byte) (digest.Digest, error) {
}
dgst := digest.FromBytes(data)
if err := ioutils.AtomicWriteFile(s.contentFile(dgst), data, 0o600); err != nil {
if err := atomicwriter.WriteFile(s.contentFile(dgst), data, 0o600); err != nil {
return "", errors.Wrap(err, "failed to write digest data")
}
@@ -148,7 +148,7 @@ func (s *fs) SetMetadata(dgst digest.Digest, key string, data []byte) error {
if err := os.MkdirAll(baseDir, 0o700); err != nil {
return err
}
return ioutils.AtomicWriteFile(filepath.Join(s.metadataDir(dgst), key), data, 0o600)
return atomicwriter.WriteFile(filepath.Join(s.metadataDir(dgst), key), data, 0o600)
}
// GetMetadata returns metadata for a given digest.

View File

@@ -12,6 +12,7 @@ import (
"github.com/containerd/log"
"github.com/docker/distribution"
"github.com/docker/docker/pkg/atomicwriter"
"github.com/docker/docker/pkg/ioutils"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
@@ -29,7 +30,7 @@ type fileMetadataStore struct {
type fileMetadataTransaction struct {
store *fileMetadataStore
ws *ioutils.AtomicWriteSet
ws *atomicwriter.WriteSet
}
// newFSMetadataStore returns an instance of a metadata store
@@ -66,7 +67,7 @@ func (fms *fileMetadataStore) StartTransaction() (*fileMetadataTransaction, erro
if err := os.MkdirAll(tmpDir, 0o755); err != nil {
return nil, err
}
ws, err := ioutils.NewAtomicWriteSet(tmpDir)
ws, err := atomicwriter.NewWriteSet(tmpDir)
if err != nil {
return nil, err
}

View File

@@ -31,7 +31,7 @@ import (
"text/template"
"github.com/containerd/log"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/atomicwriter"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
)
@@ -370,7 +370,7 @@ func (rc *ResolvConf) WriteFile(path, hashPath string, perm os.FileMode) error {
// Write the hash file.
if hashPath != "" {
hashFile, err := ioutils.NewAtomicFileWriter(hashPath, perm)
hashFile, err := atomicwriter.New(hashPath, perm)
if err != nil {
return errSystem{err}
}

View File

@@ -1,4 +1,4 @@
package ioutils // import "github.com/docker/docker/pkg/ioutils"
package atomicwriter
import (
"io"
@@ -6,11 +6,11 @@ import (
"path/filepath"
)
// NewAtomicFileWriter returns WriteCloser so that writing to it writes to a
// New returns a WriteCloser so that writing to it writes to a
// temporary file and closing it atomically changes the temporary file to
// destination path. Writing and closing concurrently is not allowed.
// NOTE: umask is not considered for the file's permissions.
func NewAtomicFileWriter(filename string, perm os.FileMode) (io.WriteCloser, error) {
func New(filename string, perm os.FileMode) (io.WriteCloser, error) {
f, err := os.CreateTemp(filepath.Dir(filename), ".tmp-"+filepath.Base(filename))
if err != nil {
return nil, err
@@ -27,10 +27,10 @@ func NewAtomicFileWriter(filename string, perm os.FileMode) (io.WriteCloser, err
}, nil
}
// AtomicWriteFile atomically writes data to a file named by filename and with the specified permission bits.
// WriteFile atomically writes data to a file named by filename and with the specified permission bits.
// NOTE: umask is not considered for the file's permissions.
func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error {
f, err := NewAtomicFileWriter(filename, perm)
func WriteFile(filename string, data []byte, perm os.FileMode) error {
f, err := New(filename, perm)
if err != nil {
return err
}
@@ -82,32 +82,32 @@ func (w *atomicFileWriter) Close() (retErr error) {
return nil
}
// AtomicWriteSet is used to atomically write a set
// WriteSet is used to atomically write a set
// of files and ensure they are visible at the same time.
// Must be committed to a new directory.
type AtomicWriteSet struct {
type WriteSet struct {
root string
}
// NewAtomicWriteSet creates a new atomic write set to
// NewWriteSet creates a new atomic write set to
// atomically create a set of files. The given directory
// is used as the base directory for storing files before
// commit. If no temporary directory is given the system
// default is used.
func NewAtomicWriteSet(tmpDir string) (*AtomicWriteSet, error) {
func NewWriteSet(tmpDir string) (*WriteSet, error) {
td, err := os.MkdirTemp(tmpDir, "write-set-")
if err != nil {
return nil, err
}
return &AtomicWriteSet{
return &WriteSet{
root: td,
}, nil
}
// WriteFile writes a file to the set, guaranteeing the file
// has been synced.
func (ws *AtomicWriteSet) WriteFile(filename string, data []byte, perm os.FileMode) error {
func (ws *WriteSet) WriteFile(filename string, data []byte, perm os.FileMode) error {
f, err := ws.FileWriter(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
if err != nil {
return err
@@ -136,7 +136,7 @@ func (w syncFileCloser) Close() error {
// FileWriter opens a file writer inside the set. The file
// should be synced and closed before calling commit.
func (ws *AtomicWriteSet) FileWriter(name string, flag int, perm os.FileMode) (io.WriteCloser, error) {
func (ws *WriteSet) FileWriter(name string, flag int, perm os.FileMode) (io.WriteCloser, error) {
f, err := os.OpenFile(filepath.Join(ws.root, name), flag, perm)
if err != nil {
return nil, err
@@ -146,18 +146,18 @@ func (ws *AtomicWriteSet) FileWriter(name string, flag int, perm os.FileMode) (i
// Cancel cancels the set and removes all temporary data
// created in the set.
func (ws *AtomicWriteSet) Cancel() error {
func (ws *WriteSet) Cancel() error {
return os.RemoveAll(ws.root)
}
// Commit moves all created files to the target directory. The
// target directory must not exist and the parent of the target
// directory must exist.
func (ws *AtomicWriteSet) Commit(target string) error {
func (ws *WriteSet) Commit(target string) error {
return os.Rename(ws.root, target)
}
// String returns the location the set is writing to.
func (ws *AtomicWriteSet) String() string {
func (ws *WriteSet) String() string {
return ws.root
}

View File

@@ -1,4 +1,4 @@
package ioutils // import "github.com/docker/docker/pkg/ioutils"
package atomicwriter
import (
"bytes"
@@ -21,7 +21,7 @@ func TestAtomicWriteToFile(t *testing.T) {
tmpDir := t.TempDir()
expected := []byte("barbaz")
if err := AtomicWriteFile(filepath.Join(tmpDir, "foo"), expected, testMode); err != nil {
if err := WriteFile(filepath.Join(tmpDir, "foo"), expected, testMode); err != nil {
t.Fatalf("Error writing to file: %v", err)
}
@@ -51,7 +51,7 @@ func TestAtomicWriteSetCommit(t *testing.T) {
}
targetDir := filepath.Join(tmpDir, "target")
ws, err := NewAtomicWriteSet(filepath.Join(tmpDir, "tmp"))
ws, err := NewWriteSet(filepath.Join(tmpDir, "tmp"))
if err != nil {
t.Fatalf("Error creating atomic write set: %s", err)
}
@@ -94,7 +94,7 @@ func TestAtomicWriteSetCancel(t *testing.T) {
t.Fatalf("Error creating tmp directory: %s", err)
}
ws, err := NewAtomicWriteSet(filepath.Join(tmpDir, "tmp"))
ws, err := NewWriteSet(filepath.Join(tmpDir, "tmp"))
if err != nil {
t.Fatalf("Error creating atomic write set: %s", err)
}

View File

@@ -0,0 +1,44 @@
package ioutils
import (
"io"
"os"
"github.com/docker/docker/pkg/atomicwriter"
)
// NewAtomicFileWriter returns WriteCloser so that writing to it writes to a
// temporary file and closing it atomically changes the temporary file to
// destination path. Writing and closing concurrently is not allowed.
// NOTE: umask is not considered for the file's permissions.
//
// Deprecated: use [atomicwriter.New] instead.
func NewAtomicFileWriter(filename string, perm os.FileMode) (io.WriteCloser, error) {
return atomicwriter.New(filename, perm)
}
// AtomicWriteFile atomically writes data to a file named by filename and with the specified permission bits.
// NOTE: umask is not considered for the file's permissions.
//
// Deprecated: use [atomicwriter.WriteFile] instead.
func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error {
return atomicwriter.WriteFile(filename, data, perm)
}
// AtomicWriteSet is used to atomically write a set
// of files and ensure they are visible at the same time.
// Must be committed to a new directory.
//
// Deprecated: use [atomicwriter.WriteSet] instead.
type AtomicWriteSet = atomicwriter.WriteSet
// NewAtomicWriteSet creates a new atomic write set to
// atomically create a set of files. The given directory
// is used as the base directory for storing files before
// commit. If no temporary directory is given the system
// default is used.
//
// Deprecated: use [atomicwriter.NewWriteSet] instead.
func NewAtomicWriteSet(tmpDir string) (*atomicwriter.WriteSet, error) {
return atomicwriter.NewWriteSet(tmpDir)
}

View File

@@ -19,8 +19,8 @@ import (
"github.com/docker/docker/api/types/events"
"github.com/docker/docker/internal/containerfs"
"github.com/docker/docker/internal/lazyregexp"
"github.com/docker/docker/pkg/atomicwriter"
"github.com/docker/docker/pkg/authorization"
"github.com/docker/docker/pkg/ioutils"
v2 "github.com/docker/docker/plugin/v2"
"github.com/docker/docker/registry"
"github.com/moby/pubsub"
@@ -281,7 +281,7 @@ func (pm *Manager) save(p *v2.Plugin) error {
if err != nil {
return errors.Wrap(err, "failed to marshal plugin json")
}
if err := ioutils.AtomicWriteFile(filepath.Join(pm.config.Root, p.GetID(), configFileName), pluginJSON, 0o600); err != nil {
if err := atomicwriter.WriteFile(filepath.Join(pm.config.Root, p.GetID(), configFileName), pluginJSON, 0o600); err != nil {
return errors.Wrap(err, "failed to write atomically plugin json")
}
return nil

View File

@@ -9,7 +9,7 @@ import (
"sync"
"github.com/distribution/reference"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/atomicwriter"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
)
@@ -315,7 +315,7 @@ func (store *refStore) save() error {
if err != nil {
return err
}
return ioutils.AtomicWriteFile(store.jsonPath, jsonData, 0o600)
return atomicwriter.WriteFile(store.jsonPath, jsonData, 0o600)
}
func (store *refStore) reload() error {

View File

@@ -16,8 +16,8 @@ import (
"github.com/containerd/log"
"github.com/docker/docker/daemon/names"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/pkg/atomicwriter"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/quota"
"github.com/docker/docker/volume"
"github.com/pkg/errors"
@@ -389,7 +389,7 @@ func (v *localVolume) saveOpts() error {
if err != nil {
return err
}
err = ioutils.AtomicWriteFile(filepath.Join(v.rootPath, "opts.json"), b, 0o600)
err = atomicwriter.WriteFile(filepath.Join(v.rootPath, "opts.json"), b, 0o600)
if err != nil {
return errdefs.System(errors.Wrap(err, "error while persisting volume options"))
}