commit
46dce5918a
7 changed files with 108 additions and 33 deletions
13
container.go
13
container.go
|
@ -51,12 +51,13 @@ type Container struct {
|
|||
}
|
||||
|
||||
type Config struct {
|
||||
Hostname string
|
||||
User string
|
||||
Ram int64
|
||||
Ports []int
|
||||
Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
|
||||
OpenStdin bool // Open stdin
|
||||
Hostname string
|
||||
User string
|
||||
Memory int64 // Memory limit (in bytes)
|
||||
MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap
|
||||
Ports []int
|
||||
Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
|
||||
OpenStdin bool // Open stdin
|
||||
}
|
||||
|
||||
type NetworkSettings struct {
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -21,7 +24,7 @@ func TestStart(t *testing.T) {
|
|||
[]string{"-al"},
|
||||
[]string{testLayerPath},
|
||||
&Config{
|
||||
Ram: 33554432,
|
||||
Memory: 33554432,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -57,7 +60,7 @@ func TestRun(t *testing.T) {
|
|||
[]string{"-al"},
|
||||
[]string{testLayerPath},
|
||||
&Config{
|
||||
Ram: 33554432,
|
||||
Memory: 33554432,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -561,6 +564,58 @@ func TestEnv(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func grepFile(t *testing.T, path string, pattern string) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
r := bufio.NewReader(f)
|
||||
var (
|
||||
line string
|
||||
)
|
||||
err = nil
|
||||
for err == nil {
|
||||
line, err = r.ReadString('\n')
|
||||
if strings.Contains(line, pattern) == true {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Fatalf("grepFile: pattern \"%s\" not found in \"%s\"", pattern, path)
|
||||
}
|
||||
|
||||
func TestLXCConfig(t *testing.T) {
|
||||
docker, err := newTestDocker()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Memory is allocated randomly for testing
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
memMin := 33554432
|
||||
memMax := 536870912
|
||||
mem := memMin + rand.Intn(memMax-memMin)
|
||||
container, err := docker.Create(
|
||||
"config_test",
|
||||
"/bin/true",
|
||||
[]string{},
|
||||
[]string{testLayerPath},
|
||||
&Config{
|
||||
Hostname: "foobar",
|
||||
Memory: int64(mem),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer docker.Destroy(container)
|
||||
container.generateLXCConfig()
|
||||
grepFile(t, container.lxcConfigPath, "lxc.utsname = foobar")
|
||||
grepFile(t, container.lxcConfigPath,
|
||||
fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem))
|
||||
grepFile(t, container.lxcConfigPath,
|
||||
fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", mem*2))
|
||||
}
|
||||
|
||||
func BenchmarkRunSequencial(b *testing.B) {
|
||||
docker, err := newTestDocker()
|
||||
if err != nil {
|
||||
|
|
|
@ -85,16 +85,32 @@ lxc.mount.entry = /etc/resolv.conf {{$ROOTFS}}/etc/resolv.conf none bind,ro 0 0
|
|||
lxc.cap.drop = audit_control audit_write mac_admin mac_override mknod net_raw setfcap setpcap sys_admin sys_boot sys_module sys_nice sys_pacct sys_rawio sys_resource sys_time sys_tty_config
|
||||
|
||||
# limits
|
||||
{{if .Config.Ram}}
|
||||
lxc.cgroup.memory.limit_in_bytes = {{.Config.Ram}}
|
||||
{{if .Config.Memory}}
|
||||
lxc.cgroup.memory.limit_in_bytes = {{.Config.Memory}}
|
||||
lxc.cgroup.memory.soft_limit_in_bytes = {{.Config.Memory}}
|
||||
{{with $memSwap := getMemorySwap .Config}}
|
||||
lxc.cgroup.memory.memsw.limit_in_bytes = {{$memSwap}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
`
|
||||
|
||||
var LxcTemplateCompiled *template.Template
|
||||
|
||||
func getMemorySwap(config *Config) int64 {
|
||||
// By default, MemorySwap is set to twice the size of RAM.
|
||||
// If you want to omit MemorySwap, set it to `-1'.
|
||||
if config.MemorySwap < 0 {
|
||||
return 0
|
||||
}
|
||||
return config.Memory * 2
|
||||
}
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
LxcTemplateCompiled, err = template.New("lxc").Parse(LxcTemplate)
|
||||
funcMap := template.FuncMap{
|
||||
"getMemorySwap": getMemorySwap,
|
||||
}
|
||||
LxcTemplateCompiled, err = template.New("lxc").Funcs(funcMap).Parse(LxcTemplate)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
package rcli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
|
||||
// Use this key to encode an RPC call into an URL,
|
||||
// eg. domain.tld/path/to/method?q=get_user&q=gordon
|
||||
const ARG_URL_KEY = "q"
|
||||
|
@ -16,18 +15,16 @@ func URLToCall(u *url.URL) (method string, args []string) {
|
|||
return path.Base(u.Path), u.Query()[ARG_URL_KEY]
|
||||
}
|
||||
|
||||
|
||||
func ListenAndServeHTTP(addr string, service Service) error {
|
||||
return http.ListenAndServe(addr, http.HandlerFunc(
|
||||
func (w http.ResponseWriter, r *http.Request) {
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
cmd, args := URLToCall(r.URL)
|
||||
if err := call(service, r.Body, &AutoFlush{w}, append([]string{cmd}, args...)...); err != nil {
|
||||
fmt.Fprintf(w, "Error: " + err.Error() + "\n")
|
||||
fmt.Fprintf(w, "Error: "+err.Error()+"\n")
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
type AutoFlush struct {
|
||||
http.ResponseWriter
|
||||
}
|
||||
|
|
12
rcli/tcp.go
12
rcli/tcp.go
|
@ -1,13 +1,13 @@
|
|||
package rcli
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"log"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"bufio"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Connect to a remote endpoint using protocol `proto` and address `addr`,
|
||||
|
@ -44,7 +44,7 @@ func ListenAndServe(proto, addr string, service Service) error {
|
|||
go func() {
|
||||
if err := Serve(conn, service); err != nil {
|
||||
log.Printf("Error: " + err.Error() + "\n")
|
||||
fmt.Fprintf(conn, "Error: " + err.Error() + "\n")
|
||||
fmt.Fprintf(conn, "Error: "+err.Error()+"\n")
|
||||
}
|
||||
conn.Close()
|
||||
}()
|
||||
|
@ -53,7 +53,6 @@ func ListenAndServe(proto, addr string, service Service) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
|
||||
// Parse an rcli call on a new connection, and pass it to `service` if it
|
||||
// is valid.
|
||||
func Serve(conn io.ReadWriter, service Service) error {
|
||||
|
@ -68,4 +67,3 @@ func Serve(conn io.ReadWriter, service Service) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -8,13 +8,13 @@ package rcli
|
|||
// are the usual suspects.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"flag"
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
"errors"
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
|
@ -25,7 +25,6 @@ type Service interface {
|
|||
type Cmd func(io.ReadCloser, io.Writer, ...string) error
|
||||
type CmdMethod func(Service, io.ReadCloser, io.Writer, ...string) error
|
||||
|
||||
|
||||
func call(service Service, stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
||||
if len(args) == 0 {
|
||||
args = []string{"help"}
|
||||
|
@ -63,7 +62,7 @@ func getMethod(service Service, name string) Cmd {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
methodName := "Cmd"+strings.ToUpper(name[:1])+strings.ToLower(name[1:])
|
||||
methodName := "Cmd" + strings.ToUpper(name[:1]) + strings.ToLower(name[1:])
|
||||
method, exists := reflect.TypeOf(service).MethodByName(methodName)
|
||||
if !exists {
|
||||
return nil
|
||||
|
@ -91,4 +90,3 @@ func Subcmd(output io.Writer, name, signature, description string) *flag.FlagSet
|
|||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
|
|
|
@ -721,10 +721,18 @@ func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string
|
|||
return errors.New("No such container: " + cmd.Arg(0))
|
||||
}
|
||||
|
||||
func (srv *Server) CreateContainer(img *image.Image, ports []int, user string, tty bool, openStdin bool, comment string, cmd string, args ...string) (*docker.Container, error) {
|
||||
func (srv *Server) CreateContainer(img *image.Image, ports []int, user string,
|
||||
tty bool, openStdin bool, memory int64, comment string, cmd string, args ...string) (*docker.Container, error) {
|
||||
id := future.RandomId()[:8]
|
||||
container, err := srv.containers.Create(id, cmd, args, img.Layers,
|
||||
&docker.Config{Hostname: id, Ports: ports, User: user, Tty: tty, OpenStdin: openStdin})
|
||||
&docker.Config{
|
||||
Hostname: id,
|
||||
Ports: ports,
|
||||
User: user,
|
||||
Tty: tty,
|
||||
OpenStdin: openStdin,
|
||||
Memory: memory,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -808,6 +816,7 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
|
|||
fl_stdin := cmd.Bool("i", false, "Keep stdin open even if not attached")
|
||||
fl_tty := cmd.Bool("t", false, "Allocate a pseudo-tty")
|
||||
fl_comment := cmd.String("c", "", "Comment")
|
||||
fl_memory := cmd.Int64("m", 0, "Memory limit (in bytes)")
|
||||
var fl_ports ports
|
||||
cmd.Var(&fl_ports, "p", "Map a network port to the container")
|
||||
if err := cmd.Parse(args); err != nil {
|
||||
|
@ -835,7 +844,8 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
|
|||
return errors.New("No such image: " + name)
|
||||
}
|
||||
// Create new container
|
||||
container, err := srv.CreateContainer(img, fl_ports, *fl_user, *fl_tty, *fl_stdin, *fl_comment, cmdline[0], cmdline[1:]...)
|
||||
container, err := srv.CreateContainer(img, fl_ports, *fl_user, *fl_tty,
|
||||
*fl_stdin, *fl_memory, *fl_comment, cmdline[0], cmdline[1:]...)
|
||||
if err != nil {
|
||||
return errors.New("Error creating container: " + err.Error())
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue