Skip to content

Commit 2ae16de

Browse files
committed
Enable to distribute images on IPFS
Signed-off-by: Kohei Tokunaga <[email protected]>
1 parent f316402 commit 2ae16de

29 files changed

+1673
-59
lines changed

.github/workflows/test.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ jobs:
5252

5353
test-integration:
5454
runs-on: ubuntu-20.04
55-
timeout-minutes: 20
55+
timeout-minutes: 30
5656
strategy:
5757
matrix:
5858
containerd: [1.4.5, 1.5.7, 1.6.0-beta.2]
@@ -77,7 +77,7 @@ jobs:
7777

7878
test-integration-rootless:
7979
runs-on: ubuntu-20.04
80-
timeout-minutes: 20
80+
timeout-minutes: 30
8181
strategy:
8282
matrix:
8383
containerd: [1.4.5, 1.5.7, 1.6.0-beta.2]
@@ -100,7 +100,7 @@ jobs:
100100

101101
cross:
102102
runs-on: ubuntu-20.04
103-
timeout-minutes: 20
103+
timeout-minutes: 30
104104
steps:
105105
- uses: actions/setup-go@v2
106106
with:
@@ -129,7 +129,7 @@ jobs:
129129
test-integration-cgroup2:
130130
# nested virtualization is only available on macOS hosts
131131
runs-on: macos-10.15
132-
timeout-minutes: 40
132+
timeout-minutes: 50
133133
env:
134134
VAGRANT_VAGRANTFILE: hack/Vagrantfile.fedora
135135
steps:

Dockerfile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ ARG SLIRP4NETNS_VERSION=1.1.12
3434
# Extra deps: FUSE-OverlayFS
3535
ARG FUSE_OVERLAYFS_VERSION=1.7.1
3636
ARG CONTAINERD_FUSE_OVERLAYFS_VERSION=1.0.3
37+
# Extra deps: IPFS
38+
ARG IPFS_VERSION=0.10.0
3739

3840
# Test deps
3941
ARG GO_VERSION=1.17
@@ -161,6 +163,15 @@ RUN fname="containerd-fuse-overlayfs-${CONTAINERD_FUSE_OVERLAYFS_VERSION}-${TARG
161163
tar xzf "${fname}" -C /out/bin && \
162164
rm -f "${fname}" && \
163165
echo "- containerd-fuse-overlayfs: v${CONTAINERD_FUSE_OVERLAYFS_VERSION}" >> /out/share/doc/nerdctl-full/README.md
166+
ARG IPFS_VERSION
167+
RUN fname="go-ipfs_v${IPFS_VERSION}_${TARGETOS:-linux}-${TARGETARCH:-amd64}.tar.gz" && \
168+
curl -o "${fname}" -fSL "https://github.com/ipfs/go-ipfs/releases/download/v${IPFS_VERSION}/${fname}" && \
169+
grep "${fname}" "/SHA256SUMS.d/go-ipfs-${IPFS_VERSION}" | sha512sum -c && \
170+
tmpout=$(mktemp -d) && \
171+
tar -C ${tmpout} -xzf "${fname}" go-ipfs/ipfs && \
172+
mv ${tmpout}/go-ipfs/ipfs /out/bin/ && \
173+
echo "- IPFS: v${IPFS_VERSION}" >> /out/share/doc/nerdctl-full/README.md
174+
164175
RUN echo "" >> /out/share/doc/nerdctl-full/README.md && \
165176
echo "## License" >> /out/share/doc/nerdctl-full/README.md && \
166177
echo "- bin/slirp4netns: [GNU GENERAL PUBLIC LICENSE, Version 2](https://github.com/rootless-containers/slirp4netns/blob/v${SLIRP4NETNS_VERSION}/COPYING)" >> /out/share/doc/nerdctl-full/README.md && \
@@ -213,6 +224,14 @@ COPY . /go/src/github.com/containerd/nerdctl
213224
WORKDIR /go/src/github.com/containerd/nerdctl
214225
VOLUME /tmp
215226
ENV CGO_ENABLED=0
227+
# enable offline ipfs for integration test
228+
COPY ./Dockerfile.d/test-integration-etc_containerd-stargz-grpc_config.toml /etc/containerd-stargz-grpc/config.toml
229+
COPY ./Dockerfile.d/test-integration-ipfs-offline.service /usr/local/lib/systemd/system/
230+
# install ipfs service. avoid using 5001(api)/8080(gateway) which are reserved by tests.
231+
RUN systemctl enable test-integration-ipfs-offline && \
232+
ipfs init && \
233+
ipfs config Addresses.API "/ip4/127.0.0.1/tcp/5888" && \
234+
ipfs config Addresses.Gateway "/ip4/127.0.0.1/tcp/5889"
216235
CMD ["go", "test", "-v", "./cmd/nerdctl/..."]
217236

218237
FROM test-integration AS test-integration-rootless
@@ -231,6 +250,8 @@ RUN ssh-keygen -q -t rsa -f /root/.ssh/id_rsa -N '' && \
231250
cp -a /root/.ssh/id_rsa.pub /home/rootless/.ssh/authorized_keys && \
232251
mkdir -p /home/rootless/.local/share && \
233252
chown -R rootless:rootless /home/rootless
253+
# ipfs daemon for rootless containerd will be enabled in /test-integration-rootless.sh
254+
RUN systemctl disable test-integration-ipfs-offline
234255
VOLUME /home/rootless/.local/share
235256
RUN go test -o /usr/local/bin/nerdctl.test -c ./cmd/nerdctl
236257
COPY ./Dockerfile.d/test-integration-rootless.sh /
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# From https://github.com/ipfs/go-ipfs/releases
2+
519d912be9367b8d5de75866aab1989954018ee1d414733ce0283735f806decb4fd11cffcb42f1beb638348a05cae95e3271c5121132e9e8e6b91ad0bfb1fda4 go-ipfs_v0.10.0_linux-amd64.tar.gz
3+
82a9b7a3e8701b982daa9ffbb9565ee725b50dc367a4358ab2a67e97a3b8b29b48d6c607e7edb54608181171dd9b4eded5b88bc4018b4dc348bac64de0edd26a go-ipfs_v0.10.0_linux-arm64.tar.gz
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Enable IPFS
2+
ipfs = true
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[Unit]
2+
Description=ipfs daemon for integration test (offline)
3+
4+
[Service]
5+
ExecStart=ipfs daemon --init --offline
6+
7+
[Install]
8+
WantedBy=docker-entrypoint.target

Dockerfile.d/test-integration-rootless.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,17 @@ if [[ "$(id -u)" = "0" ]]; then
2121
else
2222
containerd-rootless-setuptool.sh install
2323
containerd-rootless-setuptool.sh install-buildkit
24+
containerd-rootless-setuptool.sh install-stargz
25+
cat <<EOF >> /home/rootless/.config/containerd/config.toml
26+
[proxy_plugins]
27+
[proxy_plugins."stargz"]
28+
type = "snapshot"
29+
address = "/run/user/1000/containerd-stargz-grpc/containerd-stargz-grpc.sock"
30+
EOF
31+
systemctl --user restart containerd.service
32+
containerd-rootless-setuptool.sh -- install-ipfs --init --offline # offline ipfs daemon for testing
33+
echo "ipfs = true" >> /home/rootless/.config/containerd-stargz-grpc/config.toml
34+
systemctl --user restart stargz-snapshotter.service
35+
export IPFS_PATH="/home/rootless/.local/share/ipfs"
2436
exec "$@"
2537
fi

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,8 @@ Run a command in a new container.
281281

282282
Usage: `nerdctl run [OPTIONS] IMAGE [COMMAND] [ARG...]`
283283

284+
:nerd_face: `ipfs://` prefix can be used for `IMAGE` to pull it from IFPS. See [`/docs/ipfs.md`](./docs/ipfs.md) for details.
285+
284286
Basic flags:
285287
- :whale: :window: `-i, --interactive`: Keep STDIN open even if not attached"
286288
- :whale: :window: `-t, --tty`: Allocate a pseudo-TTY
@@ -667,6 +669,8 @@ Pull an image from a registry.
667669

668670
Usage: `nerdctl pull [OPTIONS] NAME[:TAG|@DIGEST]`
669671

672+
:nerd_face: `ipfs://` prefix can be used for `IMAGE` to pull it from IFPS. See [`/docs/ipfs.md`](./docs/ipfs.md) for details.
673+
670674
Flags:
671675
- :whale: `--platform=(amd64|arm64|...)`: Pull content for a specific platform
672676
- :nerd_face: Unlike Docker, this flag can be specified multiple times (`--platform=amd64 --platform=arm64`)
@@ -680,6 +684,8 @@ Push an image to a registry.
680684

681685
Usage: `nerdctl push [OPTIONS] NAME[:TAG]`
682686

687+
:nerd_face: `ipfs://` prefix can be used for `IMAGE` to push it to IFPS. See [`/docs/ipfs.md`](./docs/ipfs.md) for details.
688+
683689
Flags:
684690
- :nerd_face: `--platform=(amd64|arm64|...)`: Push content for a specific platform
685691
- :nerd_face: `--all-platforms`: Push content for all platforms
@@ -1110,3 +1116,4 @@ Others:
11101116
- [`./docs/freebsd.md`](./docs/freebsd.md): Running FreeBSD jails
11111117
- [`./docs/multi-platform.md`](./docs/multi-platform.md): Multi-platform mode
11121118
- [`./docs/experimental.md`](./docs/experimental.md): Experimental features
1119+
- [`./docs/ipfs.md`](./docs/ipfs.md): Distributing images on IPFS

cmd/nerdctl/commit.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ import (
2121
"errors"
2222
"fmt"
2323

24-
refdocker "github.com/containerd/containerd/reference/docker"
2524
"github.com/containerd/nerdctl/pkg/idutil/containerwalker"
2625
"github.com/containerd/nerdctl/pkg/imgutil/commit"
26+
"github.com/containerd/nerdctl/pkg/referenceutil"
2727

2828
"github.com/spf13/cobra"
2929
)
@@ -85,7 +85,7 @@ func commitAction(cmd *cobra.Command, args []string) error {
8585
func newCommitOpts(cmd *cobra.Command, args []string) (*commit.Opts, error) {
8686
rawRef := args[1]
8787

88-
named, err := refdocker.ParseDockerRef(rawRef)
88+
named, err := referenceutil.ParseDockerRef(rawRef)
8989
if err != nil {
9090
return nil, err
9191
}

cmd/nerdctl/compose.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ import (
2424
"github.com/containerd/containerd"
2525
"github.com/containerd/containerd/errdefs"
2626
"github.com/containerd/containerd/platforms"
27-
refdocker "github.com/containerd/containerd/reference/docker"
2827
"github.com/containerd/nerdctl/pkg/composer"
2928
"github.com/containerd/nerdctl/pkg/imgutil"
29+
"github.com/containerd/nerdctl/pkg/ipfs"
3030
"github.com/containerd/nerdctl/pkg/netutil"
31+
"github.com/containerd/nerdctl/pkg/referenceutil"
32+
httpapi "github.com/ipfs/go-ipfs-http-client"
3133
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
3234

3335
"github.com/spf13/cobra"
@@ -145,11 +147,11 @@ func getComposer(cmd *cobra.Command, client *containerd.Client) (*composer.Compo
145147
}
146148

147149
o.ImageExists = func(ctx context.Context, rawRef string) (bool, error) {
148-
named, err := refdocker.ParseDockerRef(rawRef)
150+
refNamed, err := referenceutil.ParseAny(rawRef)
149151
if err != nil {
150152
return false, err
151153
}
152-
ref := named.String()
154+
ref := refNamed.String()
153155
if _, err := client.ImageService().Get(ctx, ref); err != nil {
154156
if errors.Is(err, errdefs.ErrNotFound) {
155157
return false, nil
@@ -168,8 +170,18 @@ func getComposer(cmd *cobra.Command, client *containerd.Client) (*composer.Compo
168170
}
169171
ocispecPlatforms = []ocispec.Platform{parsed} // no append
170172
}
171-
_, imgErr := imgutil.EnsureImage(ctx, client, cmd.OutOrStdout(), snapshotter, imageName,
172-
pullMode, insecure, ocispecPlatforms, nil)
173+
var imgErr error
174+
if scheme, ref, err := referenceutil.ParseIPFSRefWithScheme(imageName); err == nil {
175+
ipfsClient, err := httpapi.NewLocalApi()
176+
if err != nil {
177+
return err
178+
}
179+
_, imgErr = ipfs.EnsureImage(ctx, client, ipfsClient, cmd.OutOrStdout(), snapshotter, scheme, ref,
180+
pullMode, ocispecPlatforms, nil)
181+
} else {
182+
_, imgErr = imgutil.EnsureImage(ctx, client, cmd.OutOrStdout(), snapshotter, imageName,
183+
pullMode, insecure, ocispecPlatforms, nil)
184+
}
173185
return imgErr
174186
}
175187

cmd/nerdctl/compose_up_test.go

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package main
1919
import (
2020
"fmt"
2121
"io"
22+
"os"
2223
"strings"
2324
"testing"
2425
"time"
@@ -30,8 +31,73 @@ import (
3031

3132
func TestComposeUp(t *testing.T) {
3233
base := testutil.NewBase(t)
34+
testComposeUp(t, base, fmt.Sprintf(`
35+
version: '3.1'
3336
34-
var dockerComposeYAML = fmt.Sprintf(`
37+
services:
38+
39+
wordpress:
40+
image: %s
41+
restart: always
42+
ports:
43+
- 8080:80
44+
environment:
45+
WORDPRESS_DB_HOST: db
46+
WORDPRESS_DB_USER: exampleuser
47+
WORDPRESS_DB_PASSWORD: examplepass
48+
WORDPRESS_DB_NAME: exampledb
49+
volumes:
50+
- wordpress:/var/www/html
51+
52+
db:
53+
image: %s
54+
restart: always
55+
environment:
56+
MYSQL_DATABASE: exampledb
57+
MYSQL_USER: exampleuser
58+
MYSQL_PASSWORD: examplepass
59+
MYSQL_RANDOM_ROOT_PASSWORD: '1'
60+
volumes:
61+
- db:/var/lib/mysql
62+
63+
volumes:
64+
wordpress:
65+
db:
66+
`, testutil.WordpressImage, testutil.MariaDBImage))
67+
}
68+
69+
func TestIPFSComposeUp(t *testing.T) {
70+
requiresIPFS(t)
71+
testutil.DockerIncompatible(t)
72+
tests := []struct {
73+
name string
74+
snapshotter string
75+
pushOptions []string
76+
requiresStargz bool
77+
}{
78+
{
79+
name: "overlayfs",
80+
snapshotter: "overlayfs",
81+
},
82+
{
83+
name: "stargz",
84+
snapshotter: "stargz",
85+
pushOptions: []string{"--estargz"},
86+
requiresStargz: true,
87+
},
88+
}
89+
for _, tt := range tests {
90+
t.Run(tt.name, func(t *testing.T) {
91+
base := testutil.NewBase(t)
92+
if tt.requiresStargz {
93+
requiresStargz(base)
94+
}
95+
ipfsImgs := make([]string, 2)
96+
for i, img := range []string{testutil.WordpressImage, testutil.MariaDBImage} {
97+
ipfsImgs[i] = pushImageToIPFS(t, base, img, tt.pushOptions...)
98+
}
99+
base.Env = append(os.Environ(), "CONTAINERD_SNAPSHOTTER="+tt.snapshotter)
100+
testComposeUp(t, base, fmt.Sprintf(`
35101
version: '3.1'
36102
37103
services:
@@ -47,6 +113,8 @@ services:
47113
WORDPRESS_DB_PASSWORD: examplepass
48114
WORDPRESS_DB_NAME: exampledb
49115
volumes:
116+
# workaround for https://github.com/containerd/stargz-snapshotter/issues/444
117+
- "/run"
50118
- wordpress:/var/www/html
51119
52120
db:
@@ -58,12 +126,19 @@ services:
58126
MYSQL_PASSWORD: examplepass
59127
MYSQL_RANDOM_ROOT_PASSWORD: '1'
60128
volumes:
129+
# workaround for https://github.com/containerd/stargz-snapshotter/issues/444
130+
- "/run"
61131
- db:/var/lib/mysql
62132
63133
volumes:
64134
wordpress:
65135
db:
66-
`, testutil.WordpressImage, testutil.MariaDBImage)
136+
`, ipfsImgs[0], ipfsImgs[1]))
137+
})
138+
}
139+
}
140+
141+
func testComposeUp(t *testing.T, base *testutil.Base, dockerComposeYAML string) {
67142
comp := testutil.NewComposeDir(t, dockerComposeYAML)
68143
defer comp.CleanUp()
69144

0 commit comments

Comments
 (0)