diff --git a/cmd/up.go b/cmd/up.go index 39d22c0..cd8b78d 100644 --- a/cmd/up.go +++ b/cmd/up.go @@ -20,14 +20,27 @@ func runUp(_ *cobra.Command, _ []string) error { pkg.Service{ Name: "faas-containerd", Env: []string{}, - Image: "docker.io/functions/figlet:latest", + Image: "docker.io/alexellis2/faas-containerd:0.2.0", Mounts: []pkg.Mount{ pkg.Mount{ Src: "/run/containerd/containerd.sock", Dest: "/run/containerd/containerd.sock", }, }, - }} + }, + pkg.Service{ + Name: "gateway", + Env: []string{}, + Image: "docker.io/openfaas/gateway:0.18.7", + Mounts: []pkg.Mount{}, + }, + pkg.Service{ + Name: "prometheus", + Env: []string{}, + Image: "docker.io/prom/prometheus:v2.14.0", + Mounts: []pkg.Mount{}, + }, + } start := time.Now() supervisor, err := pkg.NewSupervisor("/run/containerd/containerd.sock") diff --git a/pkg/supervisor.go b/pkg/supervisor.go index 100d082..a56799b 100644 --- a/pkg/supervisor.go +++ b/pkg/supervisor.go @@ -3,9 +3,20 @@ package pkg import ( "context" "fmt" + "log" + "os" + "os/exec" + "path" + "syscall" + "time" "github.com/containerd/containerd" + "github.com/containerd/containerd/api/services/tasks/v1" + "github.com/containerd/containerd/cio" + "github.com/containerd/containerd/containers" "github.com/containerd/containerd/namespaces" + "github.com/containerd/containerd/oci" + "github.com/opencontainers/runtime-spec/specs-go" ) type Supervisor struct { @@ -28,30 +39,163 @@ func (s *Supervisor) Close() { } func (s *Supervisor) Start(svcs []Service) error { + ctx := namespaces.WithNamespace(context.Background(), "default") + + images := map[string]containerd.Image{} + for _, svc := range svcs { fmt.Printf("Preparing: %s", svc.Name) fmt.Printf("Pulling: %s\n", svc.Image) - bytesOut, err := pullImage(s.client, svc.Image) + img, err := pullImage(ctx, s.client, svc.Image) if err != nil { return err } - fmt.Printf("Pull done for: %s, %d bytes\n", svc.Image, bytesOut) + images[svc.Name] = img + size, _ := img.Size(ctx) + fmt.Printf("Pull done for: %s, %d bytes\n", svc.Image, size) + + } + + for _, svc := range svcs { + fmt.Printf("Starting: %s\n", svc.Name) + + image := images[svc.Name] + + container, containerErr := s.client.ContainerService().Get(ctx, svc.Name) + + if containerErr == nil { + taskReq := &tasks.GetRequest{ + ContainerID: container.ID, + } + + task, err := s.client.TaskService().Get(ctx, taskReq) + + if task != nil && task.Process != nil { + zero := time.Time{} + fmt.Println(task.Process.ExitedAt, "=", zero) + + if task.Process.ExitedAt == zero { + log.Println("need to kill", svc.Name) + killReq := tasks.KillRequest{ + ContainerID: container.ID, + Signal: uint32(syscall.SIGTERM), + } + em, err := s.client.TaskService().Kill(ctx, &killReq) + if err != nil { + return fmt.Errorf("error killing task %s, %s, %s", container.ID, svc.Name, err) + } + time.Sleep(1 * time.Second) + log.Println("em", em) + } + + deleteReq := tasks.DeleteTaskRequest{ + ContainerID: container.ID, + } + _, err = s.client.TaskService().Delete(ctx, &deleteReq) + if err != nil { + return fmt.Errorf("error deleting task %s, %s, %s", container.ID, svc.Name, err) + } + + } + + err = s.client.ContainerService().Delete(ctx, svc.Name) + fmt.Println(err) + + err = s.client.SnapshotService("").Remove(ctx, svc.Name+"-snapshot") + fmt.Println(err) + } + + mounts := []specs.Mount{} + if len(svc.Mounts) > 0 { + for _, mnt := range svc.Mounts { + mounts = append(mounts, specs.Mount{ + Source: mnt.Src, + Destination: mnt.Dest, + Type: "bind", + Options: []string{"rbind", "rw"}, + }) + } + + } + + wd, _ := os.Getwd() + resolv := path.Join(wd, "resolv.conf") + log.Println("Using ", resolv) + mounts = append(mounts, specs.Mount{ + Destination: "/etc/resolv.conf", + Type: "bind", + Source: resolv, + Options: []string{"rbind", "ro"}, + }) + + hook := func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error { + if s.Hooks == nil { + s.Hooks = &specs.Hooks{} + } + netnsPath, err := exec.LookPath("netns") + if err != nil { + return err + } + + s.Hooks.Prestart = []specs.Hook{ + { + Path: netnsPath, + Args: []string{ + "netns", + }, + Env: os.Environ(), + }, + } + return nil + } + + newContainer, containerCreateErr := s.client.NewContainer( + ctx, + svc.Name, + containerd.WithImage(image), + containerd.WithNewSnapshot(svc.Name+"-snapshot", image), + containerd.WithNewSpec(oci.WithImageConfig(image), oci.WithCapabilities([]string{"CAP_NET_RAW"}), oci.WithMounts(mounts), hook), + ) + + if containerCreateErr != nil { + log.Println(containerCreateErr) + return containerCreateErr + } + fmt.Println("created", newContainer.ID()) + + task, err := newContainer.NewTask(ctx, cio.NewCreator(cio.WithStdio)) + if err != nil { + log.Println(err) + return err + } + + exitStatusC, err := task.Wait(ctx) + if err != nil { + log.Println(err) + return err + } + log.Println("Exited: ", exitStatusC) + + // call start on the task to execute the redis server + if err := task.Start(ctx); err != nil { + log.Println("Task err: ", err) + return err + } } return nil } -func pullImage(client *containerd.Client, image string) (int64, error) { - ctx := namespaces.WithNamespace(context.Background(), "default") +func pullImage(ctx context.Context, client *containerd.Client, image string) (containerd.Image, error) { pulled, err := client.Pull(ctx, image, containerd.WithPullUnpack) if err != nil { - return 0, err + return nil, err } - bytesOut, _ := pulled.Size(ctx) - return bytesOut, nil + + return pulled, nil } type Service struct { diff --git a/resolv.conf b/resolv.conf new file mode 100644 index 0000000..d5a33c7 --- /dev/null +++ b/resolv.conf @@ -0,0 +1,2 @@ + +nameserver 8.8.8.8