Merge pull request #114 from runcom/containers-restore
server: containers restore
This commit is contained in:
commit
96cc03ccd9
9 changed files with 200 additions and 33 deletions
|
@ -173,6 +173,12 @@ var containerStatusCommand = cli.Command{
|
||||||
var listContainersCommand = cli.Command{
|
var listContainersCommand = cli.Command{
|
||||||
Name: "list",
|
Name: "list",
|
||||||
Usage: "list containers",
|
Usage: "list containers",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "quiet",
|
||||||
|
Usage: "list only container IDs",
|
||||||
|
},
|
||||||
|
},
|
||||||
Action: func(context *cli.Context) error {
|
Action: func(context *cli.Context) error {
|
||||||
// Set up a connection to the server.
|
// Set up a connection to the server.
|
||||||
conn, err := getClientConnection(context)
|
conn, err := getClientConnection(context)
|
||||||
|
@ -182,7 +188,7 @@ var listContainersCommand = cli.Command{
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
client := pb.NewRuntimeServiceClient(conn)
|
client := pb.NewRuntimeServiceClient(conn)
|
||||||
|
|
||||||
err = ListContainers(client)
|
err = ListContainers(client, context.Bool("quiet"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("listing containers failed: %v", err)
|
return fmt.Errorf("listing containers failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -298,12 +304,16 @@ func ContainerStatus(client pb.RuntimeServiceClient, ID string) error {
|
||||||
|
|
||||||
// ListContainers sends a ListContainerRequest to the server, and parses
|
// ListContainers sends a ListContainerRequest to the server, and parses
|
||||||
// the returned ListContainerResponse.
|
// the returned ListContainerResponse.
|
||||||
func ListContainers(client pb.RuntimeServiceClient) error {
|
func ListContainers(client pb.RuntimeServiceClient, quiet bool) error {
|
||||||
r, err := client.ListContainers(context.Background(), &pb.ListContainersRequest{})
|
r, err := client.ListContainers(context.Background(), &pb.ListContainersRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, c := range r.GetContainers() {
|
for _, c := range r.GetContainers() {
|
||||||
|
if quiet {
|
||||||
|
fmt.Println(*c.Id)
|
||||||
|
continue
|
||||||
|
}
|
||||||
fmt.Printf("ID: %s\n", *c.Id)
|
fmt.Printf("ID: %s\n", *c.Id)
|
||||||
fmt.Printf("Pod: %s\n", *c.PodSandboxId)
|
fmt.Printf("Pod: %s\n", *c.PodSandboxId)
|
||||||
if c.State != nil {
|
if c.State != nil {
|
||||||
|
|
|
@ -137,6 +137,12 @@ var podSandboxStatusCommand = cli.Command{
|
||||||
var listPodSandboxCommand = cli.Command{
|
var listPodSandboxCommand = cli.Command{
|
||||||
Name: "list",
|
Name: "list",
|
||||||
Usage: "list pod sandboxes",
|
Usage: "list pod sandboxes",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "quiet",
|
||||||
|
Usage: "list only pod IDs",
|
||||||
|
},
|
||||||
|
},
|
||||||
Action: func(context *cli.Context) error {
|
Action: func(context *cli.Context) error {
|
||||||
// Set up a connection to the server.
|
// Set up a connection to the server.
|
||||||
conn, err := getClientConnection(context)
|
conn, err := getClientConnection(context)
|
||||||
|
@ -146,7 +152,7 @@ var listPodSandboxCommand = cli.Command{
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
client := pb.NewRuntimeServiceClient(conn)
|
client := pb.NewRuntimeServiceClient(conn)
|
||||||
|
|
||||||
err = ListPodSandboxes(client)
|
err = ListPodSandboxes(client, context.Bool("quiet"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("listing pod sandboxes failed: %v", err)
|
return fmt.Errorf("listing pod sandboxes failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -258,12 +264,16 @@ func PodSandboxStatus(client pb.RuntimeServiceClient, ID string) error {
|
||||||
|
|
||||||
// ListPodSandboxes sends a ListPodSandboxRequest to the server, and parses
|
// ListPodSandboxes sends a ListPodSandboxRequest to the server, and parses
|
||||||
// the returned ListPodSandboxResponse.
|
// the returned ListPodSandboxResponse.
|
||||||
func ListPodSandboxes(client pb.RuntimeServiceClient) error {
|
func ListPodSandboxes(client pb.RuntimeServiceClient, quiet bool) error {
|
||||||
r, err := client.ListPodSandbox(context.Background(), &pb.ListPodSandboxRequest{})
|
r, err := client.ListPodSandbox(context.Background(), &pb.ListPodSandboxRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, pod := range r.Items {
|
for _, pod := range r.Items {
|
||||||
|
if quiet {
|
||||||
|
fmt.Println(*pod.Id)
|
||||||
|
continue
|
||||||
|
}
|
||||||
fmt.Printf("ID: %s\n", *pod.Id)
|
fmt.Printf("ID: %s\n", *pod.Id)
|
||||||
if pod.Metadata != nil {
|
if pod.Metadata != nil {
|
||||||
if pod.Metadata.Name != nil {
|
if pod.Metadata.Name != nil {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
@ -204,7 +205,6 @@ func (s *Server) createSandboxContainer(containerID string, containerName string
|
||||||
specgen.AddAnnotation(k, v)
|
specgen.AddAnnotation(k, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if containerConfig.GetPrivileged() {
|
if containerConfig.GetPrivileged() {
|
||||||
specgen.SetupPrivileged(true)
|
specgen.SetupPrivileged(true)
|
||||||
}
|
}
|
||||||
|
@ -305,7 +305,18 @@ func (s *Server) createSandboxContainer(containerID string, containerName string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := specgen.SaveToFile(filepath.Join(containerDir, "config.json")); err != nil {
|
specgen.AddAnnotation("ocid/name", containerName)
|
||||||
|
specgen.AddAnnotation("ocid/sandbox_id", sb.id)
|
||||||
|
specgen.AddAnnotation("ocid/log_path", logPath)
|
||||||
|
specgen.AddAnnotation("ocid/tty", fmt.Sprintf("%v", containerConfig.GetTty()))
|
||||||
|
labelsJSON, err := json.Marshal(labels)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
specgen.AddAnnotation("ocid/labels", string(labelsJSON))
|
||||||
|
|
||||||
|
if err = specgen.SaveToFile(filepath.Join(containerDir, "config.json")); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,7 +332,7 @@ func (s *Server) createSandboxContainer(containerID string, containerName string
|
||||||
|
|
||||||
// TODO: copy the rootfs into the bundle.
|
// TODO: copy the rootfs into the bundle.
|
||||||
// Currently, utils.CreateFakeRootfs is used to populate the rootfs.
|
// Currently, utils.CreateFakeRootfs is used to populate the rootfs.
|
||||||
if err := utils.CreateFakeRootfs(containerDir, image); err != nil {
|
if err = utils.CreateFakeRootfs(containerDir, image); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,9 +422,10 @@ func (s *Server) ListContainers(ctx context.Context, req *pb.ListContainersReque
|
||||||
cState := s.runtime.ContainerStatus(ctr)
|
cState := s.runtime.ContainerStatus(ctr)
|
||||||
created := cState.Created.Unix()
|
created := cState.Created.Unix()
|
||||||
rState := pb.ContainerState_UNKNOWN
|
rState := pb.ContainerState_UNKNOWN
|
||||||
|
cID := ctr.ID()
|
||||||
|
|
||||||
c := &pb.Container{
|
c := &pb.Container{
|
||||||
Id: &cState.ID,
|
Id: &cID,
|
||||||
PodSandboxId: &podSandboxID,
|
PodSandboxId: &podSandboxID,
|
||||||
CreatedAt: int64Ptr(created),
|
CreatedAt: int64Ptr(created),
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,6 +173,7 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
g.SetProcessSelinuxLabel(processLabel)
|
||||||
}
|
}
|
||||||
|
|
||||||
containerID, containerName, err := s.generateContainerIDandName(name, "infra", 0)
|
containerID, containerName, err := s.generateContainerIDandName(name, "infra", 0)
|
||||||
|
@ -355,6 +356,10 @@ func (s *Server) RemovePodSandbox(ctx context.Context, req *pb.RemovePodSandboxR
|
||||||
s.removeContainer(c)
|
s.removeContainer(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := label.UnreserveLabel(sb.processLabel); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Remove the files related to the sandbox
|
// Remove the files related to the sandbox
|
||||||
podSandboxDir := filepath.Join(s.sandboxDir, sb.id)
|
podSandboxDir := filepath.Join(s.sandboxDir, sb.id)
|
||||||
if err := os.RemoveAll(podSandboxDir); err != nil {
|
if err := os.RemoveAll(podSandboxDir); err != nil {
|
||||||
|
|
|
@ -38,6 +38,49 @@ type Server struct {
|
||||||
ctrIDIndex *truncindex.TruncIndex
|
ctrIDIndex *truncindex.TruncIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) loadContainer(id string) error {
|
||||||
|
config, err := ioutil.ReadFile(filepath.Join(s.runtime.ContainerDir(), id, "config.json"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var m rspec.Spec
|
||||||
|
if err = json.Unmarshal(config, &m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
labels := make(map[string]string)
|
||||||
|
if err = json.Unmarshal([]byte(m.Annotations["ocid/labels"]), &labels); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
name := m.Annotations["ocid/name"]
|
||||||
|
name, err = s.reserveContainerName(id, name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sb := s.getSandbox(m.Annotations["ocid/sandbox_id"])
|
||||||
|
if sb == nil {
|
||||||
|
logrus.Warnf("could not get sandbox with id %s, skipping", m.Annotations["ocid/sandbox_id"])
|
||||||
|
}
|
||||||
|
|
||||||
|
var tty bool
|
||||||
|
if v := m.Annotations["ocid/tty"]; v == "true" {
|
||||||
|
tty = true
|
||||||
|
}
|
||||||
|
containerPath := filepath.Join(s.runtime.ContainerDir(), id)
|
||||||
|
|
||||||
|
ctr, err := oci.NewContainer(id, name, containerPath, m.Annotations["ocid/log_path"], labels, sb.id, tty)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.addContainer(ctr)
|
||||||
|
if err = s.runtime.UpdateStatus(ctr); err != nil {
|
||||||
|
logrus.Warnf("error updating status for container %s: %v", ctr.ID(), err)
|
||||||
|
}
|
||||||
|
if err = s.ctrIDIndex.Add(id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) loadSandbox(id string) error {
|
func (s *Server) loadSandbox(id string) error {
|
||||||
config, err := ioutil.ReadFile(filepath.Join(s.sandboxDir, id, "config.json"))
|
config, err := ioutil.ReadFile(filepath.Join(s.sandboxDir, id, "config.json"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -72,13 +115,24 @@ func (s *Server) loadSandbox(id string) error {
|
||||||
})
|
})
|
||||||
sandboxPath := filepath.Join(s.sandboxDir, id)
|
sandboxPath := filepath.Join(s.sandboxDir, id)
|
||||||
|
|
||||||
scontainer, err := oci.NewContainer(m.Annotations["ocid/container_id"], m.Annotations["ocid/container_name"], sandboxPath, sandboxPath, labels, id, false)
|
if err := label.ReserveLabel(processLabel); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cname, err := s.reserveContainerName(m.Annotations["ocid/container_id"], m.Annotations["ocid/container_name"])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
scontainer, err := oci.NewContainer(m.Annotations["ocid/container_id"], cname, sandboxPath, sandboxPath, labels, id, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.addContainer(scontainer)
|
s.addContainer(scontainer)
|
||||||
if err = s.runtime.UpdateStatus(scontainer); err != nil {
|
if err = s.runtime.UpdateStatus(scontainer); err != nil {
|
||||||
logrus.Warnf("error updating status for container %s: %v", scontainer, err)
|
logrus.Warnf("error updating status for container %s: %v", scontainer.ID(), err)
|
||||||
|
}
|
||||||
|
if err = s.ctrIDIndex.Add(scontainer.ID()); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if err = s.podIDIndex.Add(id); err != nil {
|
if err = s.podIDIndex.Add(id); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -86,17 +140,32 @@ func (s *Server) loadSandbox(id string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) restore() error {
|
func (s *Server) restore() {
|
||||||
dir, err := ioutil.ReadDir(s.sandboxDir)
|
sandboxDir, err := ioutil.ReadDir(s.sandboxDir)
|
||||||
if err != nil {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
return err
|
logrus.Warnf("could not read sandbox directory %s: %v", sandboxDir, err)
|
||||||
}
|
}
|
||||||
for _, v := range dir {
|
for _, v := range sandboxDir {
|
||||||
if err := s.loadSandbox(v.Name()); err != nil {
|
if !v.IsDir() {
|
||||||
return err
|
continue
|
||||||
|
}
|
||||||
|
if err = s.loadSandbox(v.Name()); err != nil {
|
||||||
|
logrus.Warnf("could not restore sandbox %s: %v", v.Name(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
containerDir, err := ioutil.ReadDir(s.runtime.ContainerDir())
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
logrus.Warnf("could not read container directory %s: %v", s.runtime.ContainerDir(), err)
|
||||||
|
}
|
||||||
|
for _, v := range containerDir {
|
||||||
|
if !v.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := s.loadContainer(v.Name()); err != nil {
|
||||||
|
logrus.Warnf("could not restore container %s: %v", v.Name(), err)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) reservePodName(id, name string) (string, error) {
|
func (s *Server) reservePodName(id, name string) (string, error) {
|
||||||
|
@ -182,9 +251,8 @@ func New(runtimePath, root, sandboxDir, containerDir, conmonPath, pausePath stri
|
||||||
s.ctrIDIndex = truncindex.NewTruncIndex([]string{})
|
s.ctrIDIndex = truncindex.NewTruncIndex([]string{})
|
||||||
s.ctrNameIndex = registrar.NewRegistrar()
|
s.ctrNameIndex = registrar.NewRegistrar()
|
||||||
|
|
||||||
if err := s.restore(); err != nil {
|
s.restore()
|
||||||
logrus.Warnf("couldn't restore: %v", err)
|
|
||||||
}
|
|
||||||
logrus.Debugf("sandboxes: %v", s.state.sandboxes)
|
logrus.Debugf("sandboxes: %v", s.state.sandboxes)
|
||||||
logrus.Debugf("containers: %v", s.state.containers)
|
logrus.Debugf("containers: %v", s.state.containers)
|
||||||
return s, nil
|
return s, nil
|
||||||
|
|
|
@ -33,8 +33,9 @@ function teardown() {
|
||||||
run ocic pod remove --id "$pod_id"
|
run ocic pod remove --id "$pod_id"
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
stop_ocid
|
cleanup_ctrs
|
||||||
cleanup_pods
|
cleanup_pods
|
||||||
|
stop_ocid
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "ctr lifecycle" {
|
@test "ctr lifecycle" {
|
||||||
|
@ -103,6 +104,7 @@ function teardown() {
|
||||||
run ocic ctr list
|
run ocic ctr list
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
stop_ocid
|
cleanup_ctrs
|
||||||
cleanup_pods
|
cleanup_pods
|
||||||
|
stop_ocid
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,15 +76,29 @@ function start_ocid() {
|
||||||
wait_until_reachable
|
wait_until_reachable
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanup_pods() {
|
function cleanup_ctrs() {
|
||||||
run ocic pod list
|
run ocic ctr list --quiet
|
||||||
if [ "$status" -eq 0 ]; then
|
if [ "$status" -eq 0 ]; then
|
||||||
printf '%s\n' "$output" | while IFS= read -r line
|
if [ "$output" != "" ]; then
|
||||||
do
|
printf '%s\n' "$output" | while IFS= read -r line
|
||||||
pod=$(echo "$line" | sed -e 's/ID: //g')
|
do
|
||||||
ocic pod stop --id "$pod"
|
ocic ctr stop --id "$line"
|
||||||
ocic pod remove --id "$pod"
|
ocic ctr remove --id "$line"
|
||||||
done
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup_pods() {
|
||||||
|
run ocic pod list --quiet
|
||||||
|
if [ "$status" -eq 0 ]; then
|
||||||
|
if [ "$output" != "" ]; then
|
||||||
|
printf '%s\n' "$output" | while IFS= read -r line
|
||||||
|
do
|
||||||
|
ocic pod stop --id "$line"
|
||||||
|
ocic pod remove --id "$line"
|
||||||
|
done
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,8 +35,9 @@ function teardown() {
|
||||||
run ocic pod remove --id "$id"
|
run ocic pod remove --id "$id"
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
stop_ocid
|
cleanup_ctrs
|
||||||
cleanup_pods
|
cleanup_pods
|
||||||
|
stop_ocid
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "pod remove" {
|
@test "pod remove" {
|
||||||
|
@ -60,6 +61,7 @@ function teardown() {
|
||||||
run ocic pod remove --id "$pod_id"
|
run ocic pod remove --id "$pod_id"
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
stop_ocid
|
cleanup_ctrs
|
||||||
cleanup_pods
|
cleanup_pods
|
||||||
|
stop_ocid
|
||||||
}
|
}
|
||||||
|
|
44
test/restore.bats
Normal file
44
test/restore.bats
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
function teardown() {
|
||||||
|
cleanup_test
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ocid restore" {
|
||||||
|
# this test requires docker, thus it can't yet be run in a container
|
||||||
|
if [ "$TRAVIS" = "true" ]; then # instead of $TRAVIS, add a function is_containerized to skip here
|
||||||
|
skip "cannot yet run this test in a container, use sudo make localintegration"
|
||||||
|
fi
|
||||||
|
|
||||||
|
start_ocid
|
||||||
|
run ocic pod create --config "$TESTDATA"/sandbox_config.json
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
pod_id="$output"
|
||||||
|
|
||||||
|
run ocic ctr create --config "$TESTDATA"/container_config.json --pod "$pod_id"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
ctr_id="$output"
|
||||||
|
|
||||||
|
stop_ocid
|
||||||
|
|
||||||
|
start_ocid
|
||||||
|
run ocic pod list
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ "${output}" != "" ]]
|
||||||
|
[[ "${output}" =~ "${pod_id}" ]]
|
||||||
|
|
||||||
|
run ocic ctr list
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ "${output}" != "" ]]
|
||||||
|
[[ "${output}" =~ "${pod_id}" ]]
|
||||||
|
|
||||||
|
cleanup_ctrs
|
||||||
|
cleanup_pods
|
||||||
|
stop_ocid
|
||||||
|
}
|
Loading…
Reference in a new issue