Skip to content

Instantly share code, notes, and snippets.

@proppy
Last active October 8, 2015 00:39
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save proppy/04f1270e015c0179b2d4 to your computer and use it in GitHub Desktop.
Save proppy/04f1270e015c0179b2d4 to your computer and use it in GitHub Desktop.
podlet

podlet pod de lait

"pot-de-lait"

podlet is a tiny CLI tool & daemon to launch kubernetes pods on a bare docker host.

This is a proof of concept, not affiliated to the main kubernetes project, and it only supports a subset of the v1beta3 PodSpec.

Usage

podlet [-daemon] [-addr=:8000] [-dockerhost=unix://var/run/docker.sock] [PODS...]

Examples

  • launch a pod from stdin
$ cat pod.yaml | podlet
962009ab45ee0baa5f5dfbd4a900241151f47fc7d4f2efa9e1c6ee3a84bfeae8
5e70817c999ed5e7ff6ebb4c8e5d238f1aa48b958b4fdf27aa40fd3194dcbc7a
  • launch multiple pods from files
$ podlet podi.yaml poda.yaml
podi.yaml: 6d01dfd8edc91d02c6497224e31bd2988a05526234f08afe1cb0d0ad29187578
podi.yaml: 3507edb1ae9cf69f607ce398752fedcdd06866d4553f1a63abc7e9424c3928b4
poda.yaml: 8c118d1a4d8daec3cdf5047c04adf1c9cf4f68d00b97ed658d475140bc89349b
poda.yaml: 0b9a6f7a57c89c6689f08384044ae2b35d7f033dd2dd983d24773f4d093c82ad
  • launch pods from HTTP
$ podlet -daemon &
$ curl -X POST --data-binary @pod.yaml localhost:8000/pods
[{"Id":"6a173b0d475d8fce2880ce95ede6b8461c6a11d708b53859699d524fe607c1e6","Created":"0001-01-01T00:00:00Z","State":{"StartedAt":"0001-01-01T00:00:00Z","FinishedAt":"0001-01-01T00:00:00Z"}},{"Id":"d1355c04c5533dd1e1648d541191ad17440da2324d76fedb5d7803897a291e05","Created":"0001-01-01T00:00:00Z","State":{"StartedAt":"0001-01-01T00:00:00Z","FinishedAt":"0001-01-01T00:00:00Z"}}]

TODOs

  • Add check for ApiVersion
  • Import PodSpec from kubernetes/pkg/api/v1beta3
  • Add support for Command
  • Add support for RestartPolicy
  • Add support for HostNetwork
  • Add support for Device
  • Add support for EmptyDir volumes
  • Add support for ports Protocol
  • Add support for WorkingDir
  • Add support for Privileged
  • Add support for Capabilities
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// podlet is a tiny CLI tool & daemon to launch kubernetes pods on a bare docker host.
// Usage:
// podlet [PODS...] [-daemon]
// Examples:
// launch a pod from stdin
// $ cat pod.yaml | podlet
// 962009ab45ee0baa5f5dfbd4a900241151f47fc7d4f2efa9e1c6ee3a84bfeae8
// 5e70817c999ed5e7ff6ebb4c8e5d238f1aa48b958b4fdf27aa40fd3194dcbc7a
// launch multiple pods from files
// $ podlet podi.yaml poda.yaml
// podi.yaml: 6d01dfd8edc91d02c6497224e31bd2988a05526234f08afe1cb0d0ad29187578
// podi.yaml: 3507edb1ae9cf69f607ce398752fedcdd06866d4553f1a63abc7e9424c3928b4
// poda.yaml: 8c118d1a4d8daec3cdf5047c04adf1c9cf4f68d00b97ed658d475140bc89349b
// poda.yaml: 0b9a6f7a57c89c6689f08384044ae2b35d7f033dd2dd983d24773f4d093c82ad
// launch pods from HTTP
// $ podlet -daemon
// $ curl -X POST --data-binary @pod.yaml localhost:8000/pods
// [{"Id":"6a173b0d475d8fce2880ce95ede6b8461c6a11d708b53859699d524fe607c1e6","Created":"0001-01-01T00:00:00Z","State":{"StartedAt":"0001-01-01T00:00:00Z","FinishedAt":"0001-01-01T00:00:00Z"}},{"Id":"d1355c04c5533dd1e1648d541191ad17440da2324d76fedb5d7803897a291e05","Created":"0001-01-01T00:00:00Z","State":{"StartedAt":"0001-01-01T00:00:00Z","FinishedAt":"0001-01-01T00:00:00Z"}}]
package main
import (
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
docker "github.com/fsouza/go-dockerclient"
"github.com/ghodss/yaml"
)
var addr = flag.String("addr", ":8000", "address to listen on")
var dockerEndpoint = flag.String("dockerhost", "unix://var/run/docker.sock", "docker host endpoint")
var daemon = flag.Bool("daemon", false, "daemon mode listen over HTTP for pod definitions")
type Pod struct {
Kind string
ApiVersion string `json:"apiVersion"`
Metadata struct {
Name string
}
Spec struct {
Containers []struct {
Name string
Image string
Env []struct {
Name string
Value string
}
Ports []struct {
HostIP string `json:"hostIP"`
HostPort string `json:"hostPort"`
ContainerPort string `json:"containerPort"`
}
VolumeMounts []struct {
Name string
MountPath string `json:"mountPath"`
} `json:"volumeMounts"`
}
Volumes []struct {
Name string
HostPath struct {
Path string
} `json:"hostPath"`
}
}
}
func runPod(reader io.Reader) ([]*docker.Container, error) {
// decode pod
content, err := ioutil.ReadAll(reader)
if err != nil {
return nil, fmt.Errorf("failed to read pod: %v", err)
}
var pod Pod
if err := yaml.Unmarshal(content, &pod); err != nil {
return nil, fmt.Errorf("failed to decode pod: %v", err)
}
// compute port bindings
exposedPorts := make(map[docker.Port]struct{})
portBindings := make(map[docker.Port][]docker.PortBinding)
for _, c := range pod.Spec.Containers {
for _, p := range c.Ports {
exposedPorts[docker.Port(p.ContainerPort)] = struct{}{}
portBindings[docker.Port(p.ContainerPort)] = []docker.PortBinding{
{
HostIP: p.HostIP,
HostPort: p.HostPort,
},
}
}
}
// create net container
netContainer, err := dockerClient.CreateContainer(docker.CreateContainerOptions{
Config: &docker.Config{
Image: "kubernetes/pause",
ExposedPorts: exposedPorts,
},
HostConfig: &docker.HostConfig{
PortBindings: portBindings,
},
})
if err != nil {
return nil, fmt.Errorf("failed to create net container: %v", err)
}
// create host volume mapp
volumeMap := make(map[string]string)
for _, v := range pod.Spec.Volumes {
volumeMap[v.Name] = v.HostPath.Path
}
containers := []*docker.Container{
netContainer,
}
// create pods container
for _, c := range pod.Spec.Containers {
var env []string
for _, e := range c.Env {
env = append(env, fmt.Sprintf("%s=%s", e.Name, e.Value))
}
var binds []string
for _, v := range c.VolumeMounts {
hostPath, ok := volumeMap[v.Name]
if !ok {
return nil, fmt.Errorf("hostPath not found for %q:", v.Name)
}
binds = append(binds, fmt.Sprintf("%s:%s", hostPath, v.MountPath))
}
container, err := dockerClient.CreateContainer(docker.CreateContainerOptions{
Config: &docker.Config{
Image: c.Image,
Env: env,
},
HostConfig: &docker.HostConfig{
Binds: binds,
NetworkMode: fmt.Sprintf("container:%s", netContainer.ID),
},
})
if err != nil {
return nil, fmt.Errorf("failed to create container %q: %v", c.Name, err)
}
containers = append(containers, container)
}
// start containers
for _, c := range containers {
if err := dockerClient.StartContainer(c.ID, nil); err != nil {
return nil, fmt.Errorf("failed to start container %q: %v", c.ID, err)
}
}
return containers, nil
}
func handlePods(w http.ResponseWriter, r *http.Request) error {
containers, err := runPod(r.Body)
if err != nil {
return fmt.Errorf("api error: %v", err)
}
if err := json.NewEncoder(w).Encode(containers); err != nil {
return fmt.Errorf("failed to marshall containers list in json: %v", err)
}
return nil
}
var dockerClient *docker.Client
func init() {
flag.Parse()
var err error
dockerClient, err = docker.NewClient(*dockerEndpoint)
if err != nil {
log.Fatal("failed to connect to docker endpoint %q: %v", *dockerEndpoint, err)
}
http.Handle("/pods", Handler(handlePods))
}
type Handler func(w http.ResponseWriter, r *http.Request) error
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
err := h(w, r)
if err != nil {
log.Println(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func main() {
if *daemon {
log.Fatal(http.ListenAndServe(*addr, nil))
return
}
if flag.NArg() == 0 {
containers, err := runPod(os.Stdin)
if err != nil {
log.Printf("failed to run pod from stdin: %v", err)
}
for _, c := range containers {
fmt.Println(c.ID)
}
}
for _, arg := range flag.Args() {
f, err := os.Open(arg)
if err != nil {
log.Fatalf("failed to open file %q: %v", arg, err)
}
defer f.Close()
containers, err := runPod(f)
if err != nil {
log.Printf("failed to run pod %q: %v", arg, err)
continue
}
for _, c := range containers {
fmt.Printf("%s: %s\n", arg, c.ID)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment