mirror of
https://github.com/lxc/incus
synced 2026-08-02 05:26:46 +00:00
add container creation support
Signed-off-by: Tycho Andersen <tycho.andersen@canonical.com> Signed-off-by: Serge E. Hallyn <serge.hallyn@ubuntu.com>
This commit is contained in:
parent
85e265a9f8
commit
07998bc386
13
client.go
13
client.go
@ -109,3 +109,16 @@ func (c *Client) Ping() error {
|
||||
Debugf("pong received")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) Create(name string, distro string, release string, arch string) (string, error) {
|
||||
data, err := c.getstr("/create", map[string]string{
|
||||
"name": name,
|
||||
"distro": distro,
|
||||
"release": release,
|
||||
"arch": arch,
|
||||
})
|
||||
if err != nil {
|
||||
return "fail", err
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
|
||||
59
lxc/create.go
Normal file
59
lxc/create.go
Normal file
@ -0,0 +1,59 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/lxc/lxd"
|
||||
)
|
||||
|
||||
type createCmd struct{}
|
||||
|
||||
const createUsage = `
|
||||
lxc create images:ubuntu/$release/$arch
|
||||
|
||||
Creates a container using the specified release and arch
|
||||
`
|
||||
|
||||
func (c *createCmd) usage() string {
|
||||
return createUsage
|
||||
}
|
||||
|
||||
func (c *createCmd) flags() {}
|
||||
|
||||
func (c *createCmd) run(args []string) error {
|
||||
if len(args) > 2 {
|
||||
return errArgs
|
||||
}
|
||||
|
||||
if len(args) < 1 {
|
||||
return errArgs
|
||||
}
|
||||
|
||||
if args[0] != "images:ubuntu" {
|
||||
return fmt.Errorf("only the default ubuntu image is supported currently.")
|
||||
}
|
||||
|
||||
var containerRef string
|
||||
if len(args) == 2 {
|
||||
containerRef = args[1]
|
||||
} else {
|
||||
// TODO: come up with a random name a. la. juju/maas
|
||||
containerRef = "foo"
|
||||
}
|
||||
|
||||
config, err := lxd.LoadConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d, name, err := lxd.NewClient(config, containerRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: implement the syntax for supporting other image types/remotes
|
||||
l, err := d.Create(name, "ubuntu", "trusty", "amd64")
|
||||
if err == nil {
|
||||
fmt.Println(l)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -58,6 +58,7 @@ var commands = map[string]command{
|
||||
"version": &versionCmd{},
|
||||
"help": &helpCmd{},
|
||||
"ping": &pingCmd{},
|
||||
"create": &createCmd{},
|
||||
}
|
||||
|
||||
var errArgs = fmt.Errorf("too many subcommand arguments")
|
||||
|
||||
36
lxd/byname.go
Normal file
36
lxd/byname.go
Normal file
@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"gopkg.in/lxc/go-lxc.v2"
|
||||
|
||||
"github.com/lxc/lxd"
|
||||
)
|
||||
|
||||
type byname func(*lxc.Container) error
|
||||
|
||||
func buildByNameServe(function string, f byname, d *Daemon) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
lxd.Debugf("responding to %s", function)
|
||||
|
||||
name := r.FormValue("name")
|
||||
if name == "" {
|
||||
fmt.Fprintf(w, "failed parsing name")
|
||||
return
|
||||
}
|
||||
|
||||
c, err := lxc.NewContainer(name, d.lxcpath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "failed getting container")
|
||||
return
|
||||
}
|
||||
|
||||
err = f(c)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "operation failed")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
89
lxd/create.go
Normal file
89
lxd/create.go
Normal file
@ -0,0 +1,89 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"gopkg.in/lxc/go-lxc.v2"
|
||||
|
||||
"github.com/lxc/lxd"
|
||||
)
|
||||
|
||||
func (d *Daemon) serveCreate(w http.ResponseWriter, r *http.Request) {
|
||||
lxd.Debugf("responding to create")
|
||||
|
||||
name := r.FormValue("name")
|
||||
if name == "" {
|
||||
fmt.Fprintf(w, "failed parsing name")
|
||||
return
|
||||
}
|
||||
|
||||
distro := r.FormValue("distro")
|
||||
if distro == "" {
|
||||
fmt.Fprintf(w, "failed parsing distro")
|
||||
return
|
||||
}
|
||||
|
||||
release := r.FormValue("release")
|
||||
if release == "" {
|
||||
fmt.Fprintf(w, "failed parsing release")
|
||||
return
|
||||
}
|
||||
|
||||
arch := r.FormValue("arch")
|
||||
if arch == "" {
|
||||
fmt.Fprintf(w, "failed parsing arch")
|
||||
return
|
||||
}
|
||||
|
||||
opts := lxc.TemplateOptions{
|
||||
Template: "download",
|
||||
Distro: distro,
|
||||
Release: release,
|
||||
Arch: arch,
|
||||
}
|
||||
|
||||
c, err := lxc.NewContainer(name, d.lxcpath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the id mapping. This may not be how we want to do it, but it's a
|
||||
* start. First, we remove any id_map lines in the config which might
|
||||
* have come from ~/.config/lxc/default.conf. Then add id mapping based
|
||||
* on Domain.id_map
|
||||
*/
|
||||
if d.id_map != nil {
|
||||
lxd.Debugf("setting custom idmap")
|
||||
err = c.SetConfigItem("lxc.id_map", "")
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "Failed to clear id mapping, continuing")
|
||||
}
|
||||
uidstr := fmt.Sprintf("u 0 %d %d\n", d.id_map.Uidmin, d.id_map.Uidrange)
|
||||
lxd.Debugf("uidstr is %s\n", uidstr)
|
||||
err = c.SetConfigItem("lxc.id_map", uidstr)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "Failed to set uid mapping")
|
||||
return
|
||||
}
|
||||
gidstr := fmt.Sprintf("g 0 %d %d\n", d.id_map.Gidmin, d.id_map.Gidrange)
|
||||
err = c.SetConfigItem("lxc.id_map", gidstr)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "Failed to set gid mapping")
|
||||
return
|
||||
}
|
||||
c.SaveConfigFile("/tmp/c")
|
||||
}
|
||||
|
||||
/*
|
||||
* Actually create the container
|
||||
*/
|
||||
err = c.Create(opts)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "fail!")
|
||||
} else {
|
||||
fmt.Fprintf(w, "success!")
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ type Daemon struct {
|
||||
tomb tomb.Tomb
|
||||
unixl net.Listener
|
||||
tcpl net.Listener
|
||||
id_map *Idmap
|
||||
lxcpath string
|
||||
mux *http.ServeMux
|
||||
}
|
||||
@ -24,8 +25,18 @@ func StartDaemon(listenAddr string) (*Daemon, error) {
|
||||
d := &Daemon{}
|
||||
d.mux = http.NewServeMux()
|
||||
d.mux.HandleFunc("/ping", d.servePing)
|
||||
d.mux.HandleFunc("/create", d.serveCreate)
|
||||
|
||||
var err error
|
||||
d.id_map, err = NewIdmap()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lxd.Debugf("idmap is %d %d %d %d\n",
|
||||
d.id_map.Uidmin,
|
||||
d.id_map.Uidrange,
|
||||
d.id_map.Gidmin,
|
||||
d.id_map.Gidrange)
|
||||
|
||||
d.lxcpath = lxd.VarPath("lxc")
|
||||
err = os.MkdirAll(lxd.VarPath("/"), 0755)
|
||||
@ -95,4 +106,3 @@ func (d *Daemon) Stop() error {
|
||||
//
|
||||
// Together, these ideas ensure that we have a proper daemon, and a proper client,
|
||||
// which can both be used independently and also embedded into other applications.
|
||||
|
||||
|
||||
85
lxd/idmap.go
Normal file
85
lxd/idmap.go
Normal file
@ -0,0 +1,85 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/*
|
||||
* We'll flesh this out to be lists of ranges
|
||||
* We will want a list of available ranges (all ranges
|
||||
* which lxd may use) and taken range (parts of the
|
||||
* available ranges which are already in use by containers)
|
||||
*
|
||||
* We also may want some way of deciding which containers may
|
||||
* or perhaps must not share ranges
|
||||
*
|
||||
* For now, we simply have a single range, shared by all
|
||||
* containers
|
||||
*/
|
||||
type Idmap struct {
|
||||
Uidmin, Uidrange uint
|
||||
Gidmin, Gidrange uint
|
||||
}
|
||||
|
||||
func checkmap(fname string, username string) (uint, uint, error) {
|
||||
f, err := os.Open(fname)
|
||||
var min uint
|
||||
var idrange uint
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
defer f.Close()
|
||||
scanner := bufio.NewScanner(f)
|
||||
min = 0
|
||||
idrange = 0
|
||||
for scanner.Scan() {
|
||||
s := strings.Split(scanner.Text(), ":")
|
||||
fmt.Println(s)
|
||||
if len(s) < 3 {
|
||||
return 0, 0, fmt.Errorf("unexpected values in %q: %q", fname, s)
|
||||
}
|
||||
if strings.EqualFold(s[0], username) {
|
||||
bigmin, err := strconv.ParseUint(s[1], 10, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
biGidrange, err := strconv.ParseUint(s[2], 10, 32)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
min = uint(bigmin)
|
||||
idrange = uint(biGidrange)
|
||||
return min, idrange, nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, 0, fmt.Errorf("User %q has no %ss.", username, path.Base(fname))
|
||||
}
|
||||
|
||||
func NewIdmap() (*Idmap, error) {
|
||||
me, err := user.Current()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := new(Idmap)
|
||||
umin, urange, err := checkmap("/etc/subuid", me.Username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gmin, grange, err := checkmap("/etc/subgid", me.Username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.Uidmin = umin
|
||||
m.Uidrange = urange
|
||||
m.Gidmin = gmin
|
||||
m.Gidrange = grange
|
||||
return m, nil
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user