diff --git a/server/container.go b/server/container.go new file mode 100644 index 00000000..0107c118 --- /dev/null +++ b/server/container.go @@ -0,0 +1,29 @@ +package server + +import ( + "fmt" + + "github.com/kubernetes-incubator/cri-o/oci" +) + +type containerRequest interface { + GetContainerId() string +} + +func (s *Server) getContainerFromRequest(req containerRequest) (*oci.Container, error) { + ctrID := req.GetContainerId() + if ctrID == "" { + return nil, fmt.Errorf("container ID should not be empty") + } + + containerID, err := s.ctrIDIndex.Get(ctrID) + if err != nil { + return nil, fmt.Errorf("container with ID starting with %s not found: %v", ctrID, err) + } + + c := s.state.containers.Get(containerID) + if c == nil { + return nil, fmt.Errorf("specified container not found: %s", containerID) + } + return c, nil +} diff --git a/server/sandbox.go b/server/sandbox.go index 6adb3e9b..27589126 100644 --- a/server/sandbox.go +++ b/server/sandbox.go @@ -1,18 +1,10 @@ package server import ( - "encoding/json" "fmt" - "os" - "path/filepath" - "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/stringid" "github.com/kubernetes-incubator/cri-o/oci" - "github.com/kubernetes-incubator/cri-o/utils" - "github.com/opencontainers/runc/libcontainer/label" - "github.com/opencontainers/runtime-tools/generate" - "golang.org/x/net/context" "k8s.io/kubernetes/pkg/fields" pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" ) @@ -82,520 +74,3 @@ func (s *Server) getPodSandboxFromRequest(req podSandboxRequest) (*sandbox, erro } return sb, nil } - -// RunPodSandbox creates and runs a pod-level sandbox. -func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest) (*pb.RunPodSandboxResponse, error) { - logrus.Debugf("RunPodSandboxRequest %+v", req) - var processLabel, mountLabel string - // process req.Name - name := req.GetConfig().GetMetadata().GetName() - if name == "" { - return nil, fmt.Errorf("PodSandboxConfig.Name should not be empty") - } - - namespace := req.GetConfig().GetMetadata().GetNamespace() - attempt := req.GetConfig().GetMetadata().GetAttempt() - - var err error - id, name, err := s.generatePodIDandName(name, namespace, attempt) - if err != nil { - return nil, err - } - - defer func() { - if err != nil { - s.releasePodName(name) - } - }() - - if err = s.podIDIndex.Add(id); err != nil { - return nil, err - } - - defer func() { - if err != nil { - if err = s.podIDIndex.Delete(id); err != nil { - logrus.Warnf("couldn't delete pod id %s from idIndex", id) - } - } - }() - - podSandboxDir := filepath.Join(s.config.SandboxDir, id) - if _, err = os.Stat(podSandboxDir); err == nil { - return nil, fmt.Errorf("pod sandbox (%s) already exists", podSandboxDir) - } - - defer func() { - if err != nil { - if err2 := os.RemoveAll(podSandboxDir); err2 != nil { - logrus.Warnf("couldn't cleanup podSandboxDir %s: %v", podSandboxDir, err2) - } - } - }() - - if err = os.MkdirAll(podSandboxDir, 0755); err != nil { - return nil, err - } - - // creates a spec Generator with the default spec. - g := generate.New() - - // TODO: Make the `graph/vfs` part of this configurable once the storage - // integration has been merged. - podInfraRootfs := filepath.Join(s.config.Root, "graph/vfs/pause") - // setup defaults for the pod sandbox - g.SetRootPath(filepath.Join(podInfraRootfs, "rootfs")) - g.SetRootReadonly(true) - g.SetProcessArgs([]string{"/pause"}) - - // set hostname - hostname := req.GetConfig().GetHostname() - if hostname != "" { - g.SetHostname(hostname) - } - - // set log directory - logDir := req.GetConfig().GetLogDirectory() - if logDir == "" { - logDir = filepath.Join(s.config.LogDir, id) - } - - // set DNS options - dnsServers := req.GetConfig().GetDnsConfig().GetServers() - dnsSearches := req.GetConfig().GetDnsConfig().GetSearches() - dnsOptions := req.GetConfig().GetDnsConfig().GetOptions() - resolvPath := fmt.Sprintf("%s/resolv.conf", podSandboxDir) - err = parseDNSOptions(dnsServers, dnsSearches, dnsOptions, resolvPath) - if err != nil { - err1 := removeFile(resolvPath) - if err1 != nil { - err = err1 - return nil, fmt.Errorf("%v; failed to remove %s: %v", err, resolvPath, err1) - } - return nil, err - } - - g.AddBindMount(resolvPath, "/etc/resolv.conf", "ro") - - // add metadata - metadata := req.GetConfig().GetMetadata() - metadataJSON, err := json.Marshal(metadata) - if err != nil { - return nil, err - } - - // add labels - labels := req.GetConfig().GetLabels() - labelsJSON, err := json.Marshal(labels) - if err != nil { - return nil, err - } - - // add annotations - annotations := req.GetConfig().GetAnnotations() - annotationsJSON, err := json.Marshal(annotations) - if err != nil { - return nil, err - } - - // Don't use SELinux separation with Host Pid or IPC Namespace, - if !req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().GetHostPid() && !req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().GetHostIpc() { - processLabel, mountLabel, err = getSELinuxLabels(nil) - if err != nil { - return nil, err - } - g.SetProcessSelinuxLabel(processLabel) - } - - containerID, containerName, err := s.generateContainerIDandName(name, "infra", 0) - if err != nil { - return nil, err - } - - defer func() { - if err != nil { - s.releaseContainerName(containerName) - } - }() - - if err = s.ctrIDIndex.Add(containerID); err != nil { - return nil, err - } - - defer func() { - if err != nil { - if err = s.ctrIDIndex.Delete(containerID); err != nil { - logrus.Warnf("couldn't delete ctr id %s from idIndex", containerID) - } - } - }() - - g.AddAnnotation("ocid/metadata", string(metadataJSON)) - g.AddAnnotation("ocid/labels", string(labelsJSON)) - g.AddAnnotation("ocid/annotations", string(annotationsJSON)) - g.AddAnnotation("ocid/log_path", logDir) - g.AddAnnotation("ocid/name", name) - g.AddAnnotation("ocid/container_name", containerName) - g.AddAnnotation("ocid/container_id", containerID) - - sb := &sandbox{ - id: id, - name: name, - logDir: logDir, - labels: labels, - annotations: annotations, - containers: oci.NewMemoryStore(), - processLabel: processLabel, - mountLabel: mountLabel, - metadata: metadata, - } - - s.addSandbox(sb) - - for k, v := range annotations { - g.AddAnnotation(k, v) - } - - // setup cgroup settings - cgroupParent := req.GetConfig().GetLinux().GetCgroupParent() - if cgroupParent != "" { - g.SetLinuxCgroupsPath(cgroupParent) - } - - // set up namespaces - if req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().GetHostNetwork() { - err = g.RemoveLinuxNamespace("network") - if err != nil { - return nil, err - } - } - - if req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().GetHostPid() { - err = g.RemoveLinuxNamespace("pid") - if err != nil { - return nil, err - } - } - - if req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().GetHostIpc() { - err = g.RemoveLinuxNamespace("ipc") - if err != nil { - return nil, err - } - } - - err = g.SaveToFile(filepath.Join(podSandboxDir, "config.json")) - if err != nil { - return nil, err - } - - if _, err = os.Stat(podInfraRootfs); err != nil { - if os.IsNotExist(err) { - // TODO: Replace by rootfs creation API when it is ready - if err = utils.CreateInfraRootfs(podInfraRootfs, s.config.Pause); err != nil { - return nil, err - } - } else { - return nil, err - } - } - - container, err := oci.NewContainer(containerID, containerName, podSandboxDir, podSandboxDir, labels, nil, id, false) - if err != nil { - return nil, err - } - - sb.infraContainer = container - - if err = s.runtime.CreateContainer(container); err != nil { - return nil, err - } - - if err = s.runtime.UpdateStatus(container); err != nil { - return nil, err - } - - // setup the network - podNamespace := "" - netnsPath, err := container.NetNsPath() - if err != nil { - return nil, err - } - if err = s.netPlugin.SetUpPod(netnsPath, podNamespace, id, containerName); err != nil { - return nil, fmt.Errorf("failed to create network for container %s in sandbox %s: %v", containerName, id, err) - } - - if err = s.runtime.StartContainer(container); err != nil { - return nil, err - } - - if err = s.runtime.UpdateStatus(container); err != nil { - return nil, err - } - - resp := &pb.RunPodSandboxResponse{PodSandboxId: &id} - logrus.Debugf("RunPodSandboxResponse: %+v", resp) - return resp, nil -} - -// StopPodSandbox stops the sandbox. If there are any running containers in the -// sandbox, they should be force terminated. -func (s *Server) StopPodSandbox(ctx context.Context, req *pb.StopPodSandboxRequest) (*pb.StopPodSandboxResponse, error) { - logrus.Debugf("StopPodSandboxRequest %+v", req) - sb, err := s.getPodSandboxFromRequest(req) - if err != nil { - return nil, err - } - - podNamespace := "" - podInfraContainer := sb.infraContainer - netnsPath, err := podInfraContainer.NetNsPath() - if err != nil { - return nil, err - } - - if err := s.netPlugin.TearDownPod(netnsPath, podNamespace, sb.id, podInfraContainer.Name()); err != nil { - return nil, fmt.Errorf("failed to destroy network for container %s in sandbox %s: %v", - podInfraContainer.Name(), sb.id, err) - } - - containers := sb.containers.List() - containers = append(containers, podInfraContainer) - - for _, c := range containers { - cStatus := s.runtime.ContainerStatus(c) - if cStatus.Status != oci.ContainerStateStopped { - if err := s.runtime.StopContainer(c); err != nil { - return nil, fmt.Errorf("failed to stop container %s in sandbox %s: %v", c.Name(), sb.id, err) - } - } - } - - resp := &pb.StopPodSandboxResponse{} - logrus.Debugf("StopPodSandboxResponse: %+v", resp) - return resp, nil -} - -// RemovePodSandbox deletes the sandbox. If there are any running containers in the -// sandbox, they should be force deleted. -func (s *Server) RemovePodSandbox(ctx context.Context, req *pb.RemovePodSandboxRequest) (*pb.RemovePodSandboxResponse, error) { - logrus.Debugf("RemovePodSandboxRequest %+v", req) - sb, err := s.getPodSandboxFromRequest(req) - if err != nil { - return nil, err - } - - podInfraContainer := sb.infraContainer - containers := sb.containers.List() - containers = append(containers, podInfraContainer) - - // Delete all the containers in the sandbox - for _, c := range containers { - if err := s.runtime.UpdateStatus(c); err != nil { - return nil, fmt.Errorf("failed to update container state: %v", err) - } - - cState := s.runtime.ContainerStatus(c) - if cState.Status == oci.ContainerStateCreated || cState.Status == oci.ContainerStateRunning { - if err := s.runtime.StopContainer(c); err != nil { - return nil, fmt.Errorf("failed to stop container %s: %v", c.Name(), err) - } - } - - if err := s.runtime.DeleteContainer(c); err != nil { - return nil, fmt.Errorf("failed to delete container %s in sandbox %s: %v", c.Name(), sb.id, err) - } - - if c == podInfraContainer { - continue - } - - containerDir := filepath.Join(s.runtime.ContainerDir(), c.ID()) - if err := os.RemoveAll(containerDir); err != nil { - return nil, fmt.Errorf("failed to remove container %s directory: %v", c.Name(), err) - } - - s.releaseContainerName(c.Name()) - s.removeContainer(c) - } - - if err := label.UnreserveLabel(sb.processLabel); err != nil { - return nil, err - } - - // Remove the files related to the sandbox - podSandboxDir := filepath.Join(s.config.SandboxDir, sb.id) - if err := os.RemoveAll(podSandboxDir); err != nil { - return nil, fmt.Errorf("failed to remove sandbox %s directory: %v", sb.id, err) - } - s.releaseContainerName(podInfraContainer.Name()) - s.removeContainer(podInfraContainer) - sb.infraContainer = nil - - s.releasePodName(sb.name) - s.removeSandbox(sb.id) - - resp := &pb.RemovePodSandboxResponse{} - logrus.Debugf("RemovePodSandboxResponse %+v", resp) - return resp, nil -} - -// PodSandboxStatus returns the Status of the PodSandbox. -func (s *Server) PodSandboxStatus(ctx context.Context, req *pb.PodSandboxStatusRequest) (*pb.PodSandboxStatusResponse, error) { - logrus.Debugf("PodSandboxStatusRequest %+v", req) - sb, err := s.getPodSandboxFromRequest(req) - if err != nil { - return nil, err - } - - podInfraContainer := sb.infraContainer - if err = s.runtime.UpdateStatus(podInfraContainer); err != nil { - return nil, err - } - - cState := s.runtime.ContainerStatus(podInfraContainer) - created := cState.Created.UnixNano() - - netNsPath, err := podInfraContainer.NetNsPath() - if err != nil { - return nil, err - } - podNamespace := "" - ip, err := s.netPlugin.GetContainerNetworkStatus(netNsPath, podNamespace, sb.id, podInfraContainer.Name()) - if err != nil { - // ignore the error on network status - ip = "" - } - - rStatus := pb.PodSandboxState_SANDBOX_NOTREADY - if cState.Status == oci.ContainerStateRunning { - rStatus = pb.PodSandboxState_SANDBOX_READY - } - - sandboxID := sb.id - resp := &pb.PodSandboxStatusResponse{ - Status: &pb.PodSandboxStatus{ - Id: &sandboxID, - CreatedAt: int64Ptr(created), - Linux: &pb.LinuxPodSandboxStatus{ - Namespaces: &pb.Namespace{ - Network: sPtr(netNsPath), - }, - }, - Network: &pb.PodSandboxNetworkStatus{Ip: &ip}, - State: &rStatus, - Labels: sb.labels, - Annotations: sb.annotations, - Metadata: sb.metadata, - }, - } - - logrus.Infof("PodSandboxStatusResponse: %+v", resp) - return resp, nil -} - -// filterSandbox returns whether passed container matches filtering criteria -func filterSandbox(p *pb.PodSandbox, filter *pb.PodSandboxFilter) bool { - if filter != nil { - if filter.State != nil { - if *p.State != *filter.State { - return false - } - } - if filter.LabelSelector != nil { - sel := fields.SelectorFromSet(filter.LabelSelector) - if !sel.Matches(fields.Set(p.Labels)) { - return false - } - } - } - return true -} - -// ListPodSandbox returns a list of SandBoxes. -func (s *Server) ListPodSandbox(ctx context.Context, req *pb.ListPodSandboxRequest) (*pb.ListPodSandboxResponse, error) { - logrus.Debugf("ListPodSandboxRequest %+v", req) - var pods []*pb.PodSandbox - var podList []*sandbox - for _, sb := range s.state.sandboxes { - podList = append(podList, sb) - } - - filter := req.Filter - // Filter by pod id first. - if filter != nil { - if filter.Id != nil { - sb := s.getSandbox(*filter.Id) - if sb == nil { - podList = []*sandbox{} - } else { - podList = []*sandbox{sb} - } - } - } - - for _, sb := range podList { - podInfraContainer := sb.infraContainer - if podInfraContainer == nil { - // this can't really happen, but if it does because of a bug - // it's better not to panic - continue - } - if err := s.runtime.UpdateStatus(podInfraContainer); err != nil { - return nil, err - } - cState := s.runtime.ContainerStatus(podInfraContainer) - created := cState.Created.UnixNano() - rStatus := pb.PodSandboxState_SANDBOX_NOTREADY - if cState.Status == oci.ContainerStateRunning { - rStatus = pb.PodSandboxState_SANDBOX_READY - } - - pod := &pb.PodSandbox{ - Id: &sb.id, - CreatedAt: int64Ptr(created), - State: &rStatus, - Labels: sb.labels, - Annotations: sb.annotations, - Metadata: sb.metadata, - } - - // Filter by other criteria such as state and labels. - if filterSandbox(pod, req.Filter) { - pods = append(pods, pod) - } - } - - resp := &pb.ListPodSandboxResponse{ - Items: pods, - } - logrus.Debugf("ListPodSandboxResponse %+v", resp) - return resp, nil -} - -func getSELinuxLabels(selinuxOptions *pb.SELinuxOption) (processLabel string, mountLabel string, err error) { - processLabel = "" - if selinuxOptions != nil { - user := selinuxOptions.GetUser() - if user == "" { - return "", "", fmt.Errorf("SELinuxOption.User is empty") - } - - role := selinuxOptions.GetRole() - if role == "" { - return "", "", fmt.Errorf("SELinuxOption.Role is empty") - } - - t := selinuxOptions.GetType() - if t == "" { - return "", "", fmt.Errorf("SELinuxOption.Type is empty") - } - - level := selinuxOptions.GetLevel() - if level == "" { - return "", "", fmt.Errorf("SELinuxOption.Level is empty") - } - processLabel = fmt.Sprintf("%s:%s:%s:%s", user, role, t, level) - } - return label.InitLabels(label.DupSecOpt(processLabel)) -} diff --git a/server/sandbox_list.go b/server/sandbox_list.go new file mode 100644 index 00000000..a21d40be --- /dev/null +++ b/server/sandbox_list.go @@ -0,0 +1,88 @@ +package server + +import ( + "github.com/Sirupsen/logrus" + "github.com/kubernetes-incubator/cri-o/oci" + "golang.org/x/net/context" + "k8s.io/kubernetes/pkg/fields" + pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" +) + +// filterSandbox returns whether passed container matches filtering criteria +func filterSandbox(p *pb.PodSandbox, filter *pb.PodSandboxFilter) bool { + if filter != nil { + if filter.State != nil { + if *p.State != *filter.State { + return false + } + } + if filter.LabelSelector != nil { + sel := fields.SelectorFromSet(filter.LabelSelector) + if !sel.Matches(fields.Set(p.Labels)) { + return false + } + } + } + return true +} + +// ListPodSandbox returns a list of SandBoxes. +func (s *Server) ListPodSandbox(ctx context.Context, req *pb.ListPodSandboxRequest) (*pb.ListPodSandboxResponse, error) { + logrus.Debugf("ListPodSandboxRequest %+v", req) + var pods []*pb.PodSandbox + var podList []*sandbox + for _, sb := range s.state.sandboxes { + podList = append(podList, sb) + } + + filter := req.Filter + // Filter by pod id first. + if filter != nil { + if filter.Id != nil { + sb := s.getSandbox(*filter.Id) + if sb == nil { + podList = []*sandbox{} + } else { + podList = []*sandbox{sb} + } + } + } + + for _, sb := range podList { + podInfraContainer := sb.infraContainer + if podInfraContainer == nil { + // this can't really happen, but if it does because of a bug + // it's better not to panic + continue + } + if err := s.runtime.UpdateStatus(podInfraContainer); err != nil { + return nil, err + } + cState := s.runtime.ContainerStatus(podInfraContainer) + created := cState.Created.UnixNano() + rStatus := pb.PodSandboxState_SANDBOX_NOTREADY + if cState.Status == oci.ContainerStateRunning { + rStatus = pb.PodSandboxState_SANDBOX_READY + } + + pod := &pb.PodSandbox{ + Id: &sb.id, + CreatedAt: int64Ptr(created), + State: &rStatus, + Labels: sb.labels, + Annotations: sb.annotations, + Metadata: sb.metadata, + } + + // Filter by other criteria such as state and labels. + if filterSandbox(pod, req.Filter) { + pods = append(pods, pod) + } + } + + resp := &pb.ListPodSandboxResponse{ + Items: pods, + } + logrus.Debugf("ListPodSandboxResponse %+v", resp) + return resp, nil +} diff --git a/server/sandbox_remove.go b/server/sandbox_remove.go new file mode 100644 index 00000000..b71cf43a --- /dev/null +++ b/server/sandbox_remove.go @@ -0,0 +1,77 @@ +package server + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/Sirupsen/logrus" + "github.com/kubernetes-incubator/cri-o/oci" + "github.com/opencontainers/runc/libcontainer/label" + "golang.org/x/net/context" + pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" +) + +// RemovePodSandbox deletes the sandbox. If there are any running containers in the +// sandbox, they should be force deleted. +func (s *Server) RemovePodSandbox(ctx context.Context, req *pb.RemovePodSandboxRequest) (*pb.RemovePodSandboxResponse, error) { + logrus.Debugf("RemovePodSandboxRequest %+v", req) + sb, err := s.getPodSandboxFromRequest(req) + if err != nil { + return nil, err + } + + podInfraContainer := sb.infraContainer + containers := sb.containers.List() + containers = append(containers, podInfraContainer) + + // Delete all the containers in the sandbox + for _, c := range containers { + if err := s.runtime.UpdateStatus(c); err != nil { + return nil, fmt.Errorf("failed to update container state: %v", err) + } + + cState := s.runtime.ContainerStatus(c) + if cState.Status == oci.ContainerStateCreated || cState.Status == oci.ContainerStateRunning { + if err := s.runtime.StopContainer(c); err != nil { + return nil, fmt.Errorf("failed to stop container %s: %v", c.Name(), err) + } + } + + if err := s.runtime.DeleteContainer(c); err != nil { + return nil, fmt.Errorf("failed to delete container %s in sandbox %s: %v", c.Name(), sb.id, err) + } + + if c == podInfraContainer { + continue + } + + containerDir := filepath.Join(s.runtime.ContainerDir(), c.ID()) + if err := os.RemoveAll(containerDir); err != nil { + return nil, fmt.Errorf("failed to remove container %s directory: %v", c.Name(), err) + } + + s.releaseContainerName(c.Name()) + s.removeContainer(c) + } + + if err := label.UnreserveLabel(sb.processLabel); err != nil { + return nil, err + } + + // Remove the files related to the sandbox + podSandboxDir := filepath.Join(s.config.SandboxDir, sb.id) + if err := os.RemoveAll(podSandboxDir); err != nil { + return nil, fmt.Errorf("failed to remove sandbox %s directory: %v", sb.id, err) + } + s.releaseContainerName(podInfraContainer.Name()) + s.removeContainer(podInfraContainer) + sb.infraContainer = nil + + s.releasePodName(sb.name) + s.removeSandbox(sb.id) + + resp := &pb.RemovePodSandboxResponse{} + logrus.Debugf("RemovePodSandboxResponse %+v", resp) + return resp, nil +} diff --git a/server/sandbox_run.go b/server/sandbox_run.go new file mode 100644 index 00000000..bfcb978e --- /dev/null +++ b/server/sandbox_run.go @@ -0,0 +1,298 @@ +package server + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + + "github.com/Sirupsen/logrus" + "github.com/kubernetes-incubator/cri-o/oci" + "github.com/kubernetes-incubator/cri-o/utils" + "github.com/opencontainers/runc/libcontainer/label" + "github.com/opencontainers/runtime-tools/generate" + "golang.org/x/net/context" + pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" +) + +// RunPodSandbox creates and runs a pod-level sandbox. +func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest) (*pb.RunPodSandboxResponse, error) { + logrus.Debugf("RunPodSandboxRequest %+v", req) + var processLabel, mountLabel string + // process req.Name + name := req.GetConfig().GetMetadata().GetName() + if name == "" { + return nil, fmt.Errorf("PodSandboxConfig.Name should not be empty") + } + + namespace := req.GetConfig().GetMetadata().GetNamespace() + attempt := req.GetConfig().GetMetadata().GetAttempt() + + var err error + id, name, err := s.generatePodIDandName(name, namespace, attempt) + if err != nil { + return nil, err + } + + defer func() { + if err != nil { + s.releasePodName(name) + } + }() + + if err = s.podIDIndex.Add(id); err != nil { + return nil, err + } + + defer func() { + if err != nil { + if err = s.podIDIndex.Delete(id); err != nil { + logrus.Warnf("couldn't delete pod id %s from idIndex", id) + } + } + }() + + podSandboxDir := filepath.Join(s.config.SandboxDir, id) + if _, err = os.Stat(podSandboxDir); err == nil { + return nil, fmt.Errorf("pod sandbox (%s) already exists", podSandboxDir) + } + + defer func() { + if err != nil { + if err2 := os.RemoveAll(podSandboxDir); err2 != nil { + logrus.Warnf("couldn't cleanup podSandboxDir %s: %v", podSandboxDir, err2) + } + } + }() + + if err = os.MkdirAll(podSandboxDir, 0755); err != nil { + return nil, err + } + + // creates a spec Generator with the default spec. + g := generate.New() + + // TODO: Make the `graph/vfs` part of this configurable once the storage + // integration has been merged. + podInfraRootfs := filepath.Join(s.config.Root, "graph/vfs/pause") + // setup defaults for the pod sandbox + g.SetRootPath(filepath.Join(podInfraRootfs, "rootfs")) + g.SetRootReadonly(true) + g.SetProcessArgs([]string{"/pause"}) + + // set hostname + hostname := req.GetConfig().GetHostname() + if hostname != "" { + g.SetHostname(hostname) + } + + // set log directory + logDir := req.GetConfig().GetLogDirectory() + if logDir == "" { + logDir = filepath.Join(s.config.LogDir, id) + } + + // set DNS options + dnsServers := req.GetConfig().GetDnsConfig().GetServers() + dnsSearches := req.GetConfig().GetDnsConfig().GetSearches() + dnsOptions := req.GetConfig().GetDnsConfig().GetOptions() + resolvPath := fmt.Sprintf("%s/resolv.conf", podSandboxDir) + err = parseDNSOptions(dnsServers, dnsSearches, dnsOptions, resolvPath) + if err != nil { + err1 := removeFile(resolvPath) + if err1 != nil { + err = err1 + return nil, fmt.Errorf("%v; failed to remove %s: %v", err, resolvPath, err1) + } + return nil, err + } + + g.AddBindMount(resolvPath, "/etc/resolv.conf", "ro") + + // add metadata + metadata := req.GetConfig().GetMetadata() + metadataJSON, err := json.Marshal(metadata) + if err != nil { + return nil, err + } + + // add labels + labels := req.GetConfig().GetLabels() + labelsJSON, err := json.Marshal(labels) + if err != nil { + return nil, err + } + + // add annotations + annotations := req.GetConfig().GetAnnotations() + annotationsJSON, err := json.Marshal(annotations) + if err != nil { + return nil, err + } + + // Don't use SELinux separation with Host Pid or IPC Namespace, + if !req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().GetHostPid() && !req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().GetHostIpc() { + processLabel, mountLabel, err = getSELinuxLabels(nil) + if err != nil { + return nil, err + } + g.SetProcessSelinuxLabel(processLabel) + } + + containerID, containerName, err := s.generateContainerIDandName(name, "infra", 0) + if err != nil { + return nil, err + } + + defer func() { + if err != nil { + s.releaseContainerName(containerName) + } + }() + + if err = s.ctrIDIndex.Add(containerID); err != nil { + return nil, err + } + + defer func() { + if err != nil { + if err = s.ctrIDIndex.Delete(containerID); err != nil { + logrus.Warnf("couldn't delete ctr id %s from idIndex", containerID) + } + } + }() + + g.AddAnnotation("ocid/metadata", string(metadataJSON)) + g.AddAnnotation("ocid/labels", string(labelsJSON)) + g.AddAnnotation("ocid/annotations", string(annotationsJSON)) + g.AddAnnotation("ocid/log_path", logDir) + g.AddAnnotation("ocid/name", name) + g.AddAnnotation("ocid/container_name", containerName) + g.AddAnnotation("ocid/container_id", containerID) + + sb := &sandbox{ + id: id, + name: name, + logDir: logDir, + labels: labels, + annotations: annotations, + containers: oci.NewMemoryStore(), + processLabel: processLabel, + mountLabel: mountLabel, + metadata: metadata, + } + + s.addSandbox(sb) + + for k, v := range annotations { + g.AddAnnotation(k, v) + } + + // setup cgroup settings + cgroupParent := req.GetConfig().GetLinux().GetCgroupParent() + if cgroupParent != "" { + g.SetLinuxCgroupsPath(cgroupParent) + } + + // set up namespaces + if req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().GetHostNetwork() { + err = g.RemoveLinuxNamespace("network") + if err != nil { + return nil, err + } + } + + if req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().GetHostPid() { + err = g.RemoveLinuxNamespace("pid") + if err != nil { + return nil, err + } + } + + if req.GetConfig().GetLinux().GetSecurityContext().GetNamespaceOptions().GetHostIpc() { + err = g.RemoveLinuxNamespace("ipc") + if err != nil { + return nil, err + } + } + + err = g.SaveToFile(filepath.Join(podSandboxDir, "config.json")) + if err != nil { + return nil, err + } + + if _, err = os.Stat(podInfraRootfs); err != nil { + if os.IsNotExist(err) { + // TODO: Replace by rootfs creation API when it is ready + if err = utils.CreateInfraRootfs(podInfraRootfs, s.config.Pause); err != nil { + return nil, err + } + } else { + return nil, err + } + } + + container, err := oci.NewContainer(containerID, containerName, podSandboxDir, podSandboxDir, labels, nil, id, false) + if err != nil { + return nil, err + } + + sb.infraContainer = container + + if err = s.runtime.CreateContainer(container); err != nil { + return nil, err + } + + if err = s.runtime.UpdateStatus(container); err != nil { + return nil, err + } + + // setup the network + podNamespace := "" + netnsPath, err := container.NetNsPath() + if err != nil { + return nil, err + } + if err = s.netPlugin.SetUpPod(netnsPath, podNamespace, id, containerName); err != nil { + return nil, fmt.Errorf("failed to create network for container %s in sandbox %s: %v", containerName, id, err) + } + + if err = s.runtime.StartContainer(container); err != nil { + return nil, err + } + + if err = s.runtime.UpdateStatus(container); err != nil { + return nil, err + } + + resp := &pb.RunPodSandboxResponse{PodSandboxId: &id} + logrus.Debugf("RunPodSandboxResponse: %+v", resp) + return resp, nil +} + +func getSELinuxLabels(selinuxOptions *pb.SELinuxOption) (processLabel string, mountLabel string, err error) { + processLabel = "" + if selinuxOptions != nil { + user := selinuxOptions.GetUser() + if user == "" { + return "", "", fmt.Errorf("SELinuxOption.User is empty") + } + + role := selinuxOptions.GetRole() + if role == "" { + return "", "", fmt.Errorf("SELinuxOption.Role is empty") + } + + t := selinuxOptions.GetType() + if t == "" { + return "", "", fmt.Errorf("SELinuxOption.Type is empty") + } + + level := selinuxOptions.GetLevel() + if level == "" { + return "", "", fmt.Errorf("SELinuxOption.Level is empty") + } + processLabel = fmt.Sprintf("%s:%s:%s:%s", user, role, t, level) + } + return label.InitLabels(label.DupSecOpt(processLabel)) +} diff --git a/server/sandbox_status.go b/server/sandbox_status.go new file mode 100644 index 00000000..d3826c3a --- /dev/null +++ b/server/sandbox_status.go @@ -0,0 +1,62 @@ +package server + +import ( + "github.com/Sirupsen/logrus" + "github.com/kubernetes-incubator/cri-o/oci" + "golang.org/x/net/context" + pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" +) + +// PodSandboxStatus returns the Status of the PodSandbox. +func (s *Server) PodSandboxStatus(ctx context.Context, req *pb.PodSandboxStatusRequest) (*pb.PodSandboxStatusResponse, error) { + logrus.Debugf("PodSandboxStatusRequest %+v", req) + sb, err := s.getPodSandboxFromRequest(req) + if err != nil { + return nil, err + } + + podInfraContainer := sb.infraContainer + if err = s.runtime.UpdateStatus(podInfraContainer); err != nil { + return nil, err + } + + cState := s.runtime.ContainerStatus(podInfraContainer) + created := cState.Created.UnixNano() + + netNsPath, err := podInfraContainer.NetNsPath() + if err != nil { + return nil, err + } + podNamespace := "" + ip, err := s.netPlugin.GetContainerNetworkStatus(netNsPath, podNamespace, sb.id, podInfraContainer.Name()) + if err != nil { + // ignore the error on network status + ip = "" + } + + rStatus := pb.PodSandboxState_SANDBOX_NOTREADY + if cState.Status == oci.ContainerStateRunning { + rStatus = pb.PodSandboxState_SANDBOX_READY + } + + sandboxID := sb.id + resp := &pb.PodSandboxStatusResponse{ + Status: &pb.PodSandboxStatus{ + Id: &sandboxID, + CreatedAt: int64Ptr(created), + Linux: &pb.LinuxPodSandboxStatus{ + Namespaces: &pb.Namespace{ + Network: sPtr(netNsPath), + }, + }, + Network: &pb.PodSandboxNetworkStatus{Ip: &ip}, + State: &rStatus, + Labels: sb.labels, + Annotations: sb.annotations, + Metadata: sb.metadata, + }, + } + + logrus.Infof("PodSandboxStatusResponse: %+v", resp) + return resp, nil +} diff --git a/server/sandbox_stop.go b/server/sandbox_stop.go new file mode 100644 index 00000000..42dac4ca --- /dev/null +++ b/server/sandbox_stop.go @@ -0,0 +1,48 @@ +package server + +import ( + "fmt" + + "github.com/Sirupsen/logrus" + "github.com/kubernetes-incubator/cri-o/oci" + "golang.org/x/net/context" + pb "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" +) + +// StopPodSandbox stops the sandbox. If there are any running containers in the +// sandbox, they should be force terminated. +func (s *Server) StopPodSandbox(ctx context.Context, req *pb.StopPodSandboxRequest) (*pb.StopPodSandboxResponse, error) { + logrus.Debugf("StopPodSandboxRequest %+v", req) + sb, err := s.getPodSandboxFromRequest(req) + if err != nil { + return nil, err + } + + podNamespace := "" + podInfraContainer := sb.infraContainer + netnsPath, err := podInfraContainer.NetNsPath() + if err != nil { + return nil, err + } + + if err := s.netPlugin.TearDownPod(netnsPath, podNamespace, sb.id, podInfraContainer.Name()); err != nil { + return nil, fmt.Errorf("failed to destroy network for container %s in sandbox %s: %v", + podInfraContainer.Name(), sb.id, err) + } + + containers := sb.containers.List() + containers = append(containers, podInfraContainer) + + for _, c := range containers { + cStatus := s.runtime.ContainerStatus(c) + if cStatus.Status != oci.ContainerStateStopped { + if err := s.runtime.StopContainer(c); err != nil { + return nil, fmt.Errorf("failed to stop container %s in sandbox %s: %v", c.Name(), sb.id, err) + } + } + } + + resp := &pb.StopPodSandboxResponse{} + logrus.Debugf("StopPodSandboxResponse: %+v", resp) + return resp, nil +} diff --git a/server/utils.go b/server/utils.go index 258dab19..812b1810 100644 --- a/server/utils.go +++ b/server/utils.go @@ -5,8 +5,6 @@ import ( "io" "os" "strings" - - "github.com/kubernetes-incubator/cri-o/oci" ) const ( @@ -15,28 +13,6 @@ const ( maxDNSSearches = 6 ) -type containerRequest interface { - GetContainerId() string -} - -func (s *Server) getContainerFromRequest(req containerRequest) (*oci.Container, error) { - ctrID := req.GetContainerId() - if ctrID == "" { - return nil, fmt.Errorf("container ID should not be empty") - } - - containerID, err := s.ctrIDIndex.Get(ctrID) - if err != nil { - return nil, fmt.Errorf("container with ID starting with %s not found: %v", ctrID, err) - } - - c := s.state.containers.Get(containerID) - if c == nil { - return nil, fmt.Errorf("specified container not found: %s", containerID) - } - return c, nil -} - func int64Ptr(i int64) *int64 { return &i }