Skip to content

Commit d51de37

Browse files
authored
Merge pull request #613 from Kern--/remote-sn-rootfs
Add remote snapshotter rootfs (stargz)
2 parents d99a632 + 208efd3 commit d51de37

File tree

25 files changed

+2638
-18
lines changed

25 files changed

+2638
-18
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@
44
[submodule "firecracker"]
55
path = _submodules/firecracker
66
url = https://github.com/firecracker-microvm/firecracker.git
7+
[submodule "stargz-snapshotter"]
8+
path = _submodules/stargz-snapshotter
9+
url = https://github.com/containerd/stargz-snapshotter

Makefile

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
# express or implied. See the License for the specific language governing
1212
# permissions and limitations under the License.
1313

14-
SUBDIRS:=agent runtime examples firecracker-control/cmd/containerd snapshotter
14+
SUBDIRS:=agent runtime examples firecracker-control/cmd/containerd snapshotter docker-credential-mmds
1515
TEST_SUBDIRS:=$(addprefix test-,$(SUBDIRS))
1616
INTEG_TEST_SUBDIRS:=$(addprefix integ-test-,$(SUBDIRS))
1717

@@ -66,6 +66,10 @@ RUNC_DIR=$(SUBMODULES)/runc
6666
RUNC_BIN=$(RUNC_DIR)/runc
6767
RUNC_BUILDER_NAME?=runc-builder
6868

69+
STARGZ_DIR=$(SUBMODULES)/stargz-snapshotter
70+
STARGZ_BIN=$(STARGZ_DIR)/out/containerd-stargz-grpc
71+
STARGZ_BUILDER_NAME?=stargz-builder
72+
6973
PROTO_BUILDER_NAME?=proto-builder
7074

7175
# Set this to pass additional commandline flags to the go compiler, e.g. "make test EXTRAGOARGS=-v"
@@ -112,6 +116,7 @@ distclean: clean
112116
$(MAKE) -C tools/image-builder distclean
113117
$(call rmi-if-exists,localhost/$(RUNC_BUILDER_NAME):$(DOCKER_IMAGE_TAG))
114118
$(call rmi-if-exists,localhost/$(FIRECRACKER_BUILDER_NAME):$(DOCKER_IMAGE_TAG))
119+
$(call rmi-if-exists,localhost/$(STARGZ_BUILDER_NAME):$(DOCKER_IMAGE_TAG))
115120
docker volume rm -f $(CARGO_CACHE_VOLUME_NAME)
116121
docker volume rm -f $(GO_CACHE_VOLUME_NAME)
117122
$(call rmi-if-exists,$(FIRECRACKER_CONTAINERD_TEST_IMAGE):$(DOCKER_IMAGE_TAG))
@@ -134,15 +139,25 @@ deps:
134139
install:
135140
for d in $(SUBDIRS); do $(MAKE) -C $$d install; done
136141

137-
image: $(RUNC_BIN) agent-in-docker
142+
files_ephemeral: $(RUNC_BIN) agent-in-docker
138143
mkdir -p tools/image-builder/files_ephemeral/usr/local/bin
139144
mkdir -p tools/image-builder/files_ephemeral/var/firecracker-containerd-test/scripts
140145
for f in tools/docker/scripts/*; do test -f $$f && install -m 755 $$f tools/image-builder/files_ephemeral/var/firecracker-containerd-test/scripts; done
141146
cp $(RUNC_BIN) tools/image-builder/files_ephemeral/usr/local/bin
142147
cp agent/agent tools/image-builder/files_ephemeral/usr/local/bin
143148
touch tools/image-builder/files_ephemeral
149+
150+
files_ephemeral_stargz: $(STARGZ_BIN) docker-credential-mmds-in-docker
151+
mkdir -p tools/image-builder/files_ephemeral_stargz/usr/local/bin
152+
cp docker-credential-mmds/docker-credential-mmds tools/image-builder/files_ephemeral_stargz/usr/local/bin
153+
cp $(STARGZ_BIN) tools/image-builder/files_ephemeral_stargz/usr/local/bin
154+
155+
image: files_ephemeral
144156
$(MAKE) -C tools/image-builder all-in-docker
145157

158+
image-stargz: files_ephemeral files_ephemeral_stargz
159+
$(MAKE) -C tools/image-builder stargz-in-docker
160+
146161
test: $(TEST_SUBDIRS)
147162
go test ./... $(EXTRAGOARGS)
148163

@@ -223,6 +238,10 @@ ROOTFS_NO_AGENT_INSTALLPATH=$(FIRECRACKER_CONTAINERD_RUNTIME_DIR)/rootfs-no-agen
223238
$(ROOTFS_NO_AGENT_INSTALLPATH): tools/image-builder/rootfs-no-agent.img $(FIRECRACKER_CONTAINERD_RUNTIME_DIR)
224239
install -D -o root -g root -m400 $< $@
225240

241+
ROOTFS_STARGZ_INSTALLPATH=$(FIRECRACKER_CONTAINERD_RUNTIME_DIR)/rootfs-stargz.img
242+
$(ROOTFS_STARGZ_INSTALLPATH): tools/image-builder/rootfs-stargz.img $(FIRECRACKER_CONTAINERD_RUNTIME_DIR)
243+
install -D -o root -g root -m400 $< $@
244+
226245
.PHONY: default-vmlinux
227246
default-vmlinux: $(DEFAULT_VMLINUX_NAME)
228247

@@ -238,6 +257,9 @@ install-default-runc-jailer-config: $(DEFAULT_RUNC_JAILER_CONFIG_INSTALLPATH)
238257
.PHONY: install-test-rootfs
239258
install-test-rootfs: $(ROOTFS_SLOW_BOOT_INSTALLPATH) $(ROOTFS_SLOW_REBOOT_INSTALLPATH) $(ROOTFS_NO_AGENT_INSTALLPATH)
240259

260+
.PHONY: install-stargz-rootfs
261+
install-stargz-rootfs: $(ROOTFS_STARGZ_INSTALLPATH)
262+
241263
##########################
242264
# CNI Network
243265
##########################
@@ -368,3 +390,31 @@ $(RUNC_BIN): $(RUNC_DIR)/VERSION tools/runc-builder-stamp
368390
.PHONY: install-runc
369391
install-runc: $(RUNC_BIN)
370392
install -D -o root -g root -m755 -t $(INSTALLROOT)/bin $(RUNC_BIN)
393+
394+
##########################
395+
# Stargz submodule
396+
##########################
397+
.PHONY: stargz-snapshotter
398+
stargz-snapshotter: $(STARGZ_BIN)
399+
400+
$(STARGZ_DIR)/go.mod:
401+
git submodule update --init --recursive $(STARGZ_DIR)
402+
403+
tools/stargz-builder-stamp: tools/docker/Dockerfile.stargz-builder
404+
docker build \
405+
-t localhost/$(STARGZ_BUILDER_NAME):$(DOCKER_IMAGE_TAG) \
406+
-f tools/docker/Dockerfile.stargz-builder \
407+
tools/
408+
touch $@
409+
410+
$(STARGZ_BIN): $(STARGZ_DIR)/go.mod tools/stargz-builder-stamp
411+
docker run --rm -it \
412+
--user $(UID):$(GID) \
413+
--volume $(GO_CACHE_VOLUME_NAME):/go \
414+
--volume $(CURDIR):/src \
415+
-e HOME=/tmp \
416+
-e GOPATH=/go \
417+
-e GOPROXY=$(shell go env GOPROXY) \
418+
--workdir /src/$(STARGZ_DIR) \
419+
localhost/$(STARGZ_BUILDER_NAME):$(DOCKER_IMAGE_TAG) \
420+
make

_submodules/stargz-snapshotter

Submodule stargz-snapshotter added at 6394619

docker-credential-mmds/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
docker-credential-mmds

docker-credential-mmds/Makefile

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"). You may
4+
# not use this file except in compliance with the License. A copy of the
5+
# License is located at
6+
#
7+
# http://aws.amazon.com/apache2.0/
8+
#
9+
# or in the "license" file accompanying this file. This file is distributed
10+
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
# express or implied. See the License for the specific language governing
12+
# permissions and limitations under the License.
13+
14+
SOURCES := $(shell find mmds . -name '*.go')
15+
GOMOD := $(shell go env GOMOD)
16+
GOSUM := $(GOMOD:.mod=.sum)
17+
18+
REVISION=$(shell git rev-parse HEAD)
19+
20+
all: credential-helper
21+
22+
credential-helper: docker-credential-mmds
23+
24+
docker-credential-mmds: $(SOURCES) $(GOMOD) $(GOSUM)
25+
go build -o docker-credential-mmds $(EXTRAGOARGS) -ldflags "-X main.revision=$(REVISION)"
26+
27+
test:
28+
go test ./... $(EXTRAGOARGS)
29+
30+
integ-test:
31+
32+
# installation is handled by the rootfs building targets in the main Makefile
33+
install:
34+
35+
clean:
36+
rm docker-credential-mmds
37+
38+
.PHONY: all credential-helper clean test integ-test install
39+

docker-credential-mmds/go.mod

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module github.com/firecracker-microvm/firecracker-containerd/docker-credential-mmds
2+
3+
go 1.17
4+
5+
require (
6+
github.com/docker/docker-credential-helpers v0.6.4
7+
github.com/stretchr/testify v1.5.1
8+
)
9+
10+
require (
11+
github.com/davecgh/go-spew v1.1.0 // indirect
12+
github.com/pmezard/go-difflib v1.0.0 // indirect
13+
gopkg.in/yaml.v2 v2.2.2 // indirect
14+
)

docker-credential-mmds/go.sum

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg=
2+
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
3+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o=
5+
github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c=
6+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
7+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
8+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
9+
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
10+
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
11+
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
12+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
13+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
14+
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
15+
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

docker-credential-mmds/main.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"). You may
4+
// not use this file except in compliance with the License. A copy of the
5+
// License is located at
6+
//
7+
// http://aws.amazon.com/apache2.0/
8+
//
9+
// or in the "license" file accompanying this file. This file is distributed
10+
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
// express or implied. See the License for the specific language governing
12+
// permissions and limitations under the License.
13+
package main
14+
15+
import (
16+
"fmt"
17+
"os"
18+
19+
"github.com/docker/docker-credential-helpers/credentials"
20+
"github.com/firecracker-microvm/firecracker-containerd/docker-credential-mmds/mmds"
21+
)
22+
23+
func main() {
24+
helper, err := mmds.NewHelper()
25+
if err != nil {
26+
fmt.Fprintf(os.Stderr, "docker-credential-mmds: %s\n", err)
27+
os.Exit(1)
28+
}
29+
credentials.Serve(helper)
30+
}

docker-credential-mmds/mmds/client.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"). You may
4+
// not use this file except in compliance with the License. A copy of the
5+
// License is located at
6+
//
7+
// http://aws.amazon.com/apache2.0/
8+
//
9+
// or in the "license" file accompanying this file. This file is distributed
10+
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
// express or implied. See the License for the specific language governing
12+
// permissions and limitations under the License.
13+
package mmds
14+
15+
import (
16+
"encoding/json"
17+
"errors"
18+
"fmt"
19+
"io/ioutil"
20+
"net/http"
21+
"net/url"
22+
"strings"
23+
)
24+
25+
const (
26+
mmdsAddress = "169.254.169.254"
27+
getTokenPath = "/latest/api/token/"
28+
tokenTtlHeader = "X-metadata-token-ttl-seconds"
29+
tokenHeader = "X-metadata-token"
30+
tokenTtl = "21600"
31+
)
32+
33+
var errUnauthorized = errors.New("Unauthorized")
34+
35+
type version int
36+
37+
const (
38+
v1 version = 1
39+
v2
40+
)
41+
42+
// Client is a guest-side MMDS client
43+
type Client struct {
44+
httpClient *http.Client
45+
version version
46+
token string
47+
}
48+
49+
// NewClient creates a new MMDS client
50+
// The MMDS client will attempt to get an MMDS v2 token,
51+
// however if MMDS v2 is not enabled in the guest, it will
52+
// fall back to MMDS v1 without session tokens.
53+
func NewClient(httpClient *http.Client) (*Client, error) {
54+
client := Client{
55+
httpClient: httpClient,
56+
version: v2,
57+
token: "",
58+
}
59+
err := client.refreshToken()
60+
if err != nil {
61+
return nil, err
62+
}
63+
return &client, nil
64+
}
65+
66+
func (c *Client) refreshToken() error {
67+
resp, err := c.httpClient.Do(&http.Request{
68+
Method: http.MethodPut,
69+
URL: &url.URL{
70+
Scheme: "http",
71+
Host: mmdsAddress,
72+
Path: getTokenPath,
73+
},
74+
Header: http.Header{
75+
tokenTtlHeader: []string{tokenTtl},
76+
},
77+
})
78+
if err != nil {
79+
return err
80+
}
81+
defer resp.Body.Close()
82+
if resp.StatusCode == http.StatusMethodNotAllowed {
83+
c.version = v1
84+
return nil
85+
}
86+
token, err := ioutil.ReadAll(resp.Body)
87+
if err != nil {
88+
return err
89+
}
90+
c.token = string(token)
91+
return nil
92+
}
93+
94+
// GetMetadata retrieves JSON metadata from MMDS and converts it to a map
95+
func (c *Client) GetMetadata(path string) (map[string]interface{}, error) {
96+
b, err := c.getMetadata(path)
97+
if err != nil {
98+
return nil, err
99+
}
100+
var result map[string]interface{}
101+
err = json.Unmarshal(b, &result)
102+
return result, err
103+
}
104+
105+
func (c *Client) getMetadata(path string) ([]byte, error) {
106+
headers := http.Header{
107+
"Accept": []string{"application/json"},
108+
}
109+
if c.version == v2 {
110+
headers[tokenHeader] = []string{c.token}
111+
}
112+
if !strings.HasPrefix(path, "/") {
113+
path = "/" + path
114+
}
115+
resp, err := c.httpClient.Do(&http.Request{
116+
Method: http.MethodGet,
117+
URL: &url.URL{
118+
Scheme: "http",
119+
Host: mmdsAddress,
120+
Path: path,
121+
},
122+
Header: headers,
123+
})
124+
if err != nil {
125+
return []byte{}, err
126+
}
127+
defer resp.Body.Close()
128+
return readBody(resp)
129+
}
130+
131+
func readBody(resp *http.Response) ([]byte, error) {
132+
switch status := resp.StatusCode; {
133+
case status < 300:
134+
return ioutil.ReadAll(resp.Body)
135+
case status == http.StatusUnauthorized:
136+
return []byte{}, errUnauthorized
137+
default:
138+
body, err := ioutil.ReadAll(resp.Body)
139+
return []byte{}, fmt.Errorf("unexpected http response: %d - (err %v) %s", status, err, string(body))
140+
}
141+
}

0 commit comments

Comments
 (0)