Skip to content

[WIP]: internal/filesystem consolidation, part 2 #4308

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ linters:
- revive
# Gocritic
- gocritic
- forbidigo

# 3. We used to use these, but have now removed them

Expand All @@ -41,6 +42,12 @@ linters:
# - nakedret

settings:
forbidigo:
forbid:
# FIXME: there are still calls to os.WriteFile in tests under `cmd`
- pattern: ^os\.WriteFile.*$
pkg: github.com/containerd/nerdctl/v2/pkg
msg: os.WriteFile is neither atomic nor durable - use nerdctl filesystem.WriteFile instead
staticcheck:
checks:
# Below is the default set
Expand Down
6 changes: 6 additions & 0 deletions cmd/nerdctl/helpers/flagutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/spf13/cobra"

"github.com/containerd/nerdctl/v2/pkg"
"github.com/containerd/nerdctl/v2/pkg/api/types"
)

Expand Down Expand Up @@ -112,6 +113,11 @@ func ProcessRootCmdFlags(cmd *cobra.Command) (types.GlobalCommandOptions, error)
return types.GlobalCommandOptions{}, err
}

err = pkg.InitFS(dataRoot)
if err != nil {
return types.GlobalCommandOptions{}, err
}

return types.GlobalCommandOptions{
Debug: debug,
DebugFull: debugFull,
Expand Down
2 changes: 1 addition & 1 deletion docs/dev/auditing_dockerfile.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ On a warm cache, it is still over 150MB and 30+ seconds.
In and of itself, this is hard to reduce, as we need these...

Actions:
- [ ] we could cache the module download location to reduce round-trips on modules that are shared accross
- [ ] we could cache the module download location to reduce round-trips on modules that are shared across
different projects
- [ ] we are likely installing nerdctl modules six times - (once per architecture during the build phase, then once per
ubuntu version and architecture during the tests runs (this is not even accounted for in the audit above)) - it should
Expand Down
2 changes: 1 addition & 1 deletion docs/dev/store.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ containers can be named the same), etc.
However, storing data on the filesystem in a reliable way comes with challenges:
- incomplete writes may happen (because of a system restart, or an application crash), leaving important structured files
in a broken state
- concurrent writes, or reading while writing would obviously be a problem as well, be it accross goroutines, or between
- concurrent writes, or reading while writing would obviously be a problem as well, be it across goroutines, or between
concurrent executions of the nerdctl binary, or embedded in a third-party application that does concurrently access resources

The `pkg/store` package does provide a "storage" abstraction that takes care of these issues, generally providing
Expand Down
2 changes: 1 addition & 1 deletion docs/dir.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ Data volume

Can be overridden with `nerdctl --cni-netconfpath=<NETCONFPATH>` flag and environment variable `$NETCONFPATH`.

At the top-level of <NETCONFPATH>, network (files) are shared accross all namespaces.
At the top-level of <NETCONFPATH>, network (files) are shared across all namespaces.
Sub-folders inside <NETCONFPATH> are only available to the namespace bearing the same name,
and its networks definitions are private.

Expand Down
22 changes: 12 additions & 10 deletions pkg/buildkitutil/buildkitutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import (
"testing"

"gotest.tools/v3/assert"

"github.com/containerd/nerdctl/v2/pkg/internal/filesystem"
)

func TestBuildKitFile(t *testing.T) {
Expand All @@ -55,7 +57,7 @@ func TestBuildKitFile(t *testing.T) {
{
name: "only Dockerfile is present",
prepare: func(t *testing.T) error {
return os.WriteFile(filepath.Join(tmp, DefaultDockerfileName), []byte{}, 0644)
return filesystem.WriteFile(filepath.Join(tmp, DefaultDockerfileName), []byte{}, 0644)
},
args: args{".", ""},
wantAbsDir: tmp,
Expand All @@ -65,7 +67,7 @@ func TestBuildKitFile(t *testing.T) {
{
name: "only Containerfile is present",
prepare: func(t *testing.T) error {
return os.WriteFile(filepath.Join(tmp, "Containerfile"), []byte{}, 0644)
return filesystem.WriteFile(filepath.Join(tmp, "Containerfile"), []byte{}, 0644)
},
args: args{".", ""},
wantAbsDir: tmp,
Expand All @@ -75,11 +77,11 @@ func TestBuildKitFile(t *testing.T) {
{
name: "both Dockerfile and Containerfile are present",
prepare: func(t *testing.T) error {
var err = os.WriteFile(filepath.Join(tmp, "Dockerfile"), []byte{}, 0644)
var err = filesystem.WriteFile(filepath.Join(tmp, "Dockerfile"), []byte{}, 0644)
if err != nil {
return err
}
return os.WriteFile(filepath.Join(tmp, "Containerfile"), []byte{}, 0644)
return filesystem.WriteFile(filepath.Join(tmp, "Containerfile"), []byte{}, 0644)
},
args: args{".", ""},
wantAbsDir: tmp,
Expand All @@ -89,11 +91,11 @@ func TestBuildKitFile(t *testing.T) {
{
name: "Dockerfile and Containerfile have different contents",
prepare: func(t *testing.T) error {
var err = os.WriteFile(filepath.Join(tmp, "Dockerfile"), []byte{'d'}, 0644)
var err = filesystem.WriteFile(filepath.Join(tmp, "Dockerfile"), []byte{'d'}, 0644)
if err != nil {
return err
}
return os.WriteFile(filepath.Join(tmp, "Containerfile"), []byte{'c'}, 0644)
return filesystem.WriteFile(filepath.Join(tmp, "Containerfile"), []byte{'c'}, 0644)
},
args: args{".", ""},
wantAbsDir: tmp,
Expand All @@ -103,7 +105,7 @@ func TestBuildKitFile(t *testing.T) {
{
name: "Custom file is specfied",
prepare: func(t *testing.T) error {
return os.WriteFile(filepath.Join(tmp, "CustomFile"), []byte{}, 0644)
return filesystem.WriteFile(filepath.Join(tmp, "CustomFile"), []byte{}, 0644)
},
args: args{".", "CustomFile"},
wantAbsDir: tmp,
Expand All @@ -113,7 +115,7 @@ func TestBuildKitFile(t *testing.T) {
{
name: "Absolute path is specified along with custom file",
prepare: func(t *testing.T) error {
return os.WriteFile(filepath.Join(tmp, "CustomFile"), []byte{}, 0644)
return filesystem.WriteFile(filepath.Join(tmp, "CustomFile"), []byte{}, 0644)
},
args: args{tmp, "CustomFile"},
wantAbsDir: tmp,
Expand All @@ -123,7 +125,7 @@ func TestBuildKitFile(t *testing.T) {
{
name: "Absolute path is specified along with Docker file",
prepare: func(t *testing.T) error {
return os.WriteFile(filepath.Join(tmp, "Dockerfile"), []byte{}, 0644)
return filesystem.WriteFile(filepath.Join(tmp, "Dockerfile"), []byte{}, 0644)
},
args: args{tmp, "."},
wantAbsDir: tmp,
Expand All @@ -133,7 +135,7 @@ func TestBuildKitFile(t *testing.T) {
{
name: "Absolute path is specified with Container file in the path",
prepare: func(t *testing.T) error {
return os.WriteFile(filepath.Join(tmp, ContainerfileName), []byte{}, 0644)
return filesystem.WriteFile(filepath.Join(tmp, ContainerfileName), []byte{}, 0644)
},
args: args{tmp, "."},
wantAbsDir: tmp,
Expand Down
3 changes: 2 additions & 1 deletion pkg/cmd/builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
"github.com/containerd/nerdctl/v2/pkg/buildkitutil"
"github.com/containerd/nerdctl/v2/pkg/clientutil"
"github.com/containerd/nerdctl/v2/pkg/containerutil"
"github.com/containerd/nerdctl/v2/pkg/internal/filesystem"
"github.com/containerd/nerdctl/v2/pkg/platformutil"
"github.com/containerd/nerdctl/v2/pkg/referenceutil"
"github.com/containerd/nerdctl/v2/pkg/strutil"
Expand Down Expand Up @@ -110,7 +111,7 @@ func Build(ctx context.Context, client *containerd.Client, options types.Builder
if err != nil {
return err
}
if err := os.WriteFile(options.IidFile, []byte(id), 0644); err != nil {
if err := filesystem.WriteFile(options.IidFile, []byte(id), 0644); err != nil {
return err
}
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/cmd/compose/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/containerd/nerdctl/v2/pkg/composer"
"github.com/containerd/nerdctl/v2/pkg/composer/serviceparser"
"github.com/containerd/nerdctl/v2/pkg/imgutil"
"github.com/containerd/nerdctl/v2/pkg/internal/filesystem"
"github.com/containerd/nerdctl/v2/pkg/ipfs"
"github.com/containerd/nerdctl/v2/pkg/netutil"
"github.com/containerd/nerdctl/v2/pkg/referenceutil"
Expand Down Expand Up @@ -136,7 +137,7 @@ func New(client *containerd.Client, globalOptions types.GlobalCommandOptions, op
return err
}
defer os.RemoveAll(dir)
if err := os.WriteFile(filepath.Join(dir, "api"), []byte(ipfsAddress), 0600); err != nil {
if err := filesystem.WriteFile(filepath.Join(dir, "api"), []byte(ipfsAddress), 0600); err != nil {
return err
}
ipfsPath = dir
Expand Down
3 changes: 2 additions & 1 deletion pkg/cmd/container/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import (
"github.com/containerd/nerdctl/v2/pkg/imgutil"
"github.com/containerd/nerdctl/v2/pkg/imgutil/load"
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
"github.com/containerd/nerdctl/v2/pkg/internal/filesystem"
"github.com/containerd/nerdctl/v2/pkg/ipcutil"
"github.com/containerd/nerdctl/v2/pkg/labels"
"github.com/containerd/nerdctl/v2/pkg/logging"
Expand Down Expand Up @@ -951,7 +952,7 @@ func generateLogConfig(dataStore string, id string, logDriver string, logOpt []s
}

logConfigFilePath := logging.LogConfigFilePath(dataStore, ns, id)
if err = os.WriteFile(logConfigFilePath, logConfigB, 0600); err != nil {
if err = filesystem.WriteFile(logConfigFilePath, logConfigB, 0600); err != nil {
return logConfig, err
}

Expand Down
3 changes: 2 additions & 1 deletion pkg/cmd/image/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (

"github.com/containerd/nerdctl/v2/pkg/api/types"
"github.com/containerd/nerdctl/v2/pkg/imgutil"
"github.com/containerd/nerdctl/v2/pkg/internal/filesystem"
"github.com/containerd/nerdctl/v2/pkg/ipfs"
"github.com/containerd/nerdctl/v2/pkg/referenceutil"
"github.com/containerd/nerdctl/v2/pkg/signutil"
Expand Down Expand Up @@ -62,7 +63,7 @@ func EnsureImage(ctx context.Context, client *containerd.Client, rawRef string,
return nil, err
}
defer os.RemoveAll(dir)
if err := os.WriteFile(filepath.Join(dir, "api"), []byte(options.IPFSAddress), 0600); err != nil {
if err := filesystem.WriteFile(filepath.Join(dir, "api"), []byte(options.IPFSAddress), 0600); err != nil {
return nil, err
}
ipfsPath = dir
Expand Down
3 changes: 2 additions & 1 deletion pkg/cmd/image/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import (
nerdconverter "github.com/containerd/nerdctl/v2/pkg/imgutil/converter"
"github.com/containerd/nerdctl/v2/pkg/imgutil/dockerconfigresolver"
"github.com/containerd/nerdctl/v2/pkg/imgutil/push"
"github.com/containerd/nerdctl/v2/pkg/internal/filesystem"
"github.com/containerd/nerdctl/v2/pkg/ipfs"
"github.com/containerd/nerdctl/v2/pkg/platformutil"
"github.com/containerd/nerdctl/v2/pkg/referenceutil"
Expand Down Expand Up @@ -85,7 +86,7 @@ func Push(ctx context.Context, client *containerd.Client, rawRef string, options
return err
}
defer os.RemoveAll(dir)
if err := os.WriteFile(filepath.Join(dir, "api"), []byte(options.IpfsAddress), 0600); err != nil {
if err := filesystem.WriteFile(filepath.Join(dir, "api"), []byte(options.IpfsAddress), 0600); err != nil {
return err
}
ipfsPath = dir
Expand Down
3 changes: 2 additions & 1 deletion pkg/cmd/ipfs/registry_serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/containerd/log"

"github.com/containerd/nerdctl/v2/pkg/api/types"
"github.com/containerd/nerdctl/v2/pkg/internal/filesystem"
"github.com/containerd/nerdctl/v2/pkg/ipfs"
)

Expand All @@ -35,7 +36,7 @@ func RegistryServe(options types.IPFSRegistryServeOptions) error {
return err
}
defer os.RemoveAll(dir)
if err := os.WriteFile(filepath.Join(dir, "api"), []byte(options.IPFSAddress), 0600); err != nil {
if err := filesystem.WriteFile(filepath.Join(dir, "api"), []byte(options.IPFSAddress), 0600); err != nil {
return err
}
ipfsPath = dir
Expand Down
4 changes: 2 additions & 2 deletions pkg/composer/serviceparser/serviceparser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package serviceparser

import (
"fmt"
"os"
"path/filepath"
"runtime"
"strconv"
Expand All @@ -27,6 +26,7 @@ import (
"github.com/compose-spec/compose-go/v2/types"
"gotest.tools/v3/assert"

"github.com/containerd/nerdctl/v2/pkg/internal/filesystem"
"github.com/containerd/nerdctl/v2/pkg/strutil"
"github.com/containerd/nerdctl/v2/pkg/testutil"
)
Expand Down Expand Up @@ -521,7 +521,7 @@ configs:
assert.NilError(t, err)

for _, f := range []string{"secret1", "secret2", "secret3", "config1", "config2"} {
err = os.WriteFile(filepath.Join(project.WorkingDir, f), []byte("content-"+f), 0444)
err = filesystem.WriteFile(filepath.Join(project.WorkingDir, f), []byte("content-"+f), 0444)
assert.NilError(t, err)
}

Expand Down
3 changes: 2 additions & 1 deletion pkg/containerutil/container_network_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"github.com/containerd/nerdctl/v2/pkg/clientutil"
"github.com/containerd/nerdctl/v2/pkg/dnsutil/hostsstore"
"github.com/containerd/nerdctl/v2/pkg/idutil/containerwalker"
"github.com/containerd/nerdctl/v2/pkg/internal/filesystem"
"github.com/containerd/nerdctl/v2/pkg/labels"
"github.com/containerd/nerdctl/v2/pkg/mountutil"
"github.com/containerd/nerdctl/v2/pkg/netutil"
Expand Down Expand Up @@ -829,7 +830,7 @@ func writeEtcHostnameForContainer(globalOptions types.GlobalCommandOptions, host
}

hostnamePath := filepath.Join(stateDir, "hostname")
if err := os.WriteFile(hostnamePath, []byte(hostname+"\n"), 0644); err != nil {
if err := filesystem.WriteFile(hostnamePath, []byte(hostname+"\n"), 0644); err != nil {
return nil, err
}

Expand Down
18 changes: 13 additions & 5 deletions pkg/dnsutil/hostsstore/hostsstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import (
"github.com/containerd/errdefs"
"github.com/containerd/log"

"github.com/containerd/nerdctl/v2/pkg/internal/filesystem"
"github.com/containerd/nerdctl/v2/pkg/store"
)

Expand Down Expand Up @@ -116,11 +117,11 @@ func (x *hostsStore) Acquire(meta Meta) (err error) {
// Because of the way we call network manager ContainerNetworkingOpts then SetupNetworking in sequence
// we need to make sure we do not overwrite an already allocated hosts file.
if _, err = os.Stat(loc); os.IsNotExist(err) {
if err = os.WriteFile(loc, []byte{}, 0o644); err != nil {
if err = filesystem.WriteFile(loc, []byte{}, 0o644); err != nil {
return errors.Join(store.ErrSystemFailure, err)
}

// os.WriteFile relies on syscall.Open. Unless there are ACLs, the effective mode of the file will be matched
// WriteFile relies on syscall.Open. Unless there are ACLs, the effective mode of the file will be matched
// against the current process umask.
// See https://www.man7.org/linux/man-pages/man2/open.2.html for details.
// Since we must make sure that these files are world readable, explicitly chmod them here.
Expand Down Expand Up @@ -185,12 +186,12 @@ func (x *hostsStore) AllocHostsFile(id string, content []byte) (location string,
return err
}

err = os.WriteFile(loc, content, 0o644)
err = filesystem.WriteFile(loc, content, 0o644)
if err != nil {
err = errors.Join(store.ErrSystemFailure, err)
}

// os.WriteFile relies on syscall.Open. Unless there are ACLs, the effective mode of the file will be matched
// WriteFile relies on syscall.Open. Unless there are ACLs, the effective mode of the file will be matched
// against the current process umask.
// See https://www.man7.org/linux/man-pages/man2/open.2.html for details.
// Since we must make sure that these files are world readable, explicitly chmod them here.
Expand Down Expand Up @@ -351,7 +352,14 @@ func (x *hostsStore) updateAllHosts() (err error) {
return err
}

err = os.WriteFile(loc, buf.Bytes(), 0o644)
// Because the file is mounted, we cannot do atomic writes here as that would change inode.
// The practical implications of this are that a partial / interrupted write would leave the hosts file with
// an invalid entry and/or missing entries. At worse, this would lead to a container losing localhost network
// capabilities.
// Proper consistency requires that we would have a rollback mechanism in case of recoverable failure,
// and a disaster management / cleanup mechanism, presumably at the top-level of the operation.
// nolint:forbidigo
err = filesystem.WriteFile(loc, buf.Bytes(), 0o644)
if err != nil {
log.L.WithError(err).Errorf("failed to write hosts file for %q", entry)
}
Expand Down
23 changes: 23 additions & 0 deletions pkg/fs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
Copyright The containerd Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package pkg

import "github.com/containerd/nerdctl/v2/pkg/internal/filesystem"

func InitFS(path string) error {
return filesystem.SetFSOpsDirectory(path)
}
Loading
Loading