// SaveConfig writes the provided configuration to the config file. func SaveConfig(c *Config, fname string) error { for k, _ := range StaticRemotes { delete(c.Remotes, k) } // Ignore errors on these two calls. Create will report any problems. os.Remove(fname + ".new") os.Mkdir(filepath.Dir(fname), 0700) f, err := os.Create(fname + ".new") if err != nil { return fmt.Errorf("cannot create config file: %v", err) } // If there are any errors, do not leave it around. defer f.Close() defer os.Remove(fname + ".new") data, err := yaml.Marshal(c) _, err = f.Write(data) if err != nil { return fmt.Errorf("cannot write configuration: %v", err) } f.Close() err = shared.FileMove(fname+".new", fname) if err != nil { return fmt.Errorf("cannot rename temporary config file: %v", err) } return nil }
/* * This is called by lxd when called as "lxd forkstart <container>" * 'forkstart' is used instead of just 'start' in the hopes that people * do not accidentally type 'lxd start' instead of 'lxc start' * * We expect to read the lxcconfig over fd 3. */ func startContainer(args []string) error { if len(args) != 4 { return fmt.Errorf("Bad arguments: %q\n", args) } name := args[1] lxcpath := args[2] configPath := args[3] c, err := lxc.NewContainer(name, lxcpath) if err != nil { return fmt.Errorf("Error initializing container for start: %q", err) } err = c.LoadConfigFile(configPath) if err != nil { return fmt.Errorf("Error opening startup config file: %q", err) } err = c.Start() if err != nil { os.Remove(configPath) } else { shared.FileMove(configPath, shared.LogPath(name, "lxc.conf")) } return err }
func containerPost(d *Daemon, r *http.Request) Response { name := mux.Vars(r)["name"] c, err := newLxdContainer(name, d) if err != nil { return SmartError(err) } buf, err := ioutil.ReadAll(r.Body) if err != nil { return InternalError(err) } body := containerPostBody{} if err := json.Unmarshal(buf, &body); err != nil { return BadRequest(err) } if body.Migration { ws, err := migration.NewMigrationSource(c.c) if err != nil { return InternalError(err) } return AsyncResponseWithWs(ws, nil) } if c.c.Running() { return BadRequest(fmt.Errorf("renaming of running container not allowed")) } args := containerLXDArgs{ Ctype: cTypeRegular, Config: c.config, Profiles: c.profiles, Ephemeral: c.ephemeral, BaseImage: c.config["volatile.baseImage"], Architecture: c.architecture, } _, err = dbContainerCreate(d.db, body.Name, args) if err != nil { return SmartError(err) } run := func() error { oldPath := fmt.Sprintf("%s/", shared.VarPath("containers", c.name)) newPath := fmt.Sprintf("%s/", shared.VarPath("containers", body.Name)) if err := shared.FileMove(oldPath, newPath); err != nil { return err } if err = removeContainer(d, c); err != nil { return fmt.Errorf("error removing container after rename: %v", err) } return nil } return AsyncResponse(shared.OperationWrap(run), nil) }
/* * This is called by lxd when called as "lxd forkstart <container>" * 'forkstart' is used instead of just 'start' in the hopes that people * do not accidentally type 'lxd start' instead of 'lxc start' * * We expect to read the lxcconfig over fd 3. */ func startContainer(args []string) error { if len(args) != 4 { return fmt.Errorf("Bad arguments: %q", args) } name := args[1] lxcpath := args[2] configPath := args[3] c, err := lxc.NewContainer(name, lxcpath) if err != nil { return fmt.Errorf("Error initializing container for start: %q", err) } err = c.LoadConfigFile(configPath) if err != nil { return fmt.Errorf("Error opening startup config file: %q", err) } /* due to https://github.com/golang/go/issues/13155 and the * CollectOutput call we make for the forkstart process, we need to * close our stdin/stdout/stderr here. Collecting some of the logs is * better than collecting no logs, though. */ os.Stdin.Close() os.Stderr.Close() os.Stdout.Close() err = c.Start() if err != nil { os.Remove(configPath) } else { shared.FileMove(configPath, shared.LogPath(name, "lxc.conf")) } return err }
/* * This is called by lxd when called as "lxd forkstart <container>" * 'forkstart' is used instead of just 'start' in the hopes that people * do not accidentally type 'lxd start' instead of 'lxc start' * * We expect to read the lxcconfig over fd 3. */ func startContainer(args []string) error { if len(args) != 4 { return fmt.Errorf("Bad arguments: %q", args) } name := args[1] lxcpath := args[2] configPath := args[3] c, err := lxc.NewContainer(name, lxcpath) if err != nil { return fmt.Errorf("Error initializing container for start: %q", err) } err = c.LoadConfigFile(configPath) if err != nil { return fmt.Errorf("Error opening startup config file: %q", err) } /* due to https://github.com/golang/go/issues/13155 and the * CollectOutput call we make for the forkstart process, we need to * close our stdin/stdout/stderr here. Collecting some of the logs is * better than collecting no logs, though. */ os.Stdin.Close() os.Stderr.Close() os.Stdout.Close() // Redirect stdout and stderr to a log file logPath := shared.LogPath(name, "forkstart.log") if shared.PathExists(logPath) { os.Remove(logPath) } logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0644) if err == nil { syscall.Dup3(int(logFile.Fd()), 1, 0) syscall.Dup3(int(logFile.Fd()), 2, 0) } // Move the config so we can inspect it on failure shared.FileMove(configPath, shared.LogPath(name, "lxc.conf")) return c.Start() }
func getImgPostInfo(d *Daemon, r *http.Request, builddir string, post *os.File) (info shared.ImageInfo, err error) { var imageMeta *imageMetadata logger := logging.AddContext(shared.Log, log.Ctx{"function": "getImgPostInfo"}) public, _ := strconv.Atoi(r.Header.Get("X-LXD-public")) info.Public = public == 1 propHeaders := r.Header[http.CanonicalHeaderKey("X-LXD-properties")] ctype, ctypeParams, err := mime.ParseMediaType(r.Header.Get("Content-Type")) if err != nil { ctype = "application/octet-stream" } sha256 := sha256.New() var size int64 // Create a temporary file for the image tarball imageTarf, err := ioutil.TempFile(builddir, "lxd_tar_") if err != nil { return info, err } if ctype == "multipart/form-data" { // Parse the POST data post.Seek(0, 0) mr := multipart.NewReader(post, ctypeParams["boundary"]) // Get the metadata tarball part, err := mr.NextPart() if err != nil { return info, err } if part.FormName() != "metadata" { return info, fmt.Errorf("Invalid multipart image") } size, err = io.Copy(io.MultiWriter(imageTarf, sha256), part) info.Size += size imageTarf.Close() if err != nil { logger.Error( "Failed to copy the image tarfile", log.Ctx{"err": err}) return info, err } // Get the rootfs tarball part, err = mr.NextPart() if err != nil { logger.Error( "Failed to get the next part", log.Ctx{"err": err}) return info, err } if part.FormName() != "rootfs" { logger.Error( "Invalid multipart image") return info, fmt.Errorf("Invalid multipart image") } // Create a temporary file for the rootfs tarball rootfsTarf, err := ioutil.TempFile(builddir, "lxd_tar_") if err != nil { return info, err } size, err = io.Copy(io.MultiWriter(rootfsTarf, sha256), part) info.Size += size rootfsTarf.Close() if err != nil { logger.Error( "Failed to copy the rootfs tarfile", log.Ctx{"err": err}) return info, err } info.Filename = part.FileName() info.Fingerprint = fmt.Sprintf("%x", sha256.Sum(nil)) expectedFingerprint := r.Header.Get("X-LXD-fingerprint") if expectedFingerprint != "" && info.Fingerprint != expectedFingerprint { err = fmt.Errorf("fingerprints don't match, got %s expected %s", info.Fingerprint, expectedFingerprint) return info, err } imgfname := shared.VarPath("images", info.Fingerprint) err = shared.FileMove(imageTarf.Name(), imgfname) if err != nil { logger.Error( "Failed to move the image tarfile", log.Ctx{ "err": err, "source": imageTarf.Name(), "dest": imgfname}) return info, err } rootfsfname := shared.VarPath("images", info.Fingerprint+".rootfs") err = shared.FileMove(rootfsTarf.Name(), rootfsfname) if err != nil { logger.Error( "Failed to move the rootfs tarfile", log.Ctx{ "err": err, "source": rootfsTarf.Name(), "dest": imgfname}) return info, err } imageMeta, err = getImageMetadata(imgfname) if err != nil { logger.Error( "Failed to get image metadata", log.Ctx{"err": err}) return info, err } } else { post.Seek(0, 0) size, err = io.Copy(io.MultiWriter(imageTarf, sha256), post) info.Size = size imageTarf.Close() logger.Debug("Tar size", log.Ctx{"size": size}) if err != nil { logger.Error( "Failed to copy the tarfile", log.Ctx{"err": err}) return info, err } info.Filename = r.Header.Get("X-LXD-filename") info.Fingerprint = fmt.Sprintf("%x", sha256.Sum(nil)) expectedFingerprint := r.Header.Get("X-LXD-fingerprint") if expectedFingerprint != "" && info.Fingerprint != expectedFingerprint { logger.Error( "Fingerprints don't match", log.Ctx{ "got": info.Fingerprint, "expected": expectedFingerprint}) err = fmt.Errorf( "fingerprints don't match, got %s expected %s", info.Fingerprint, expectedFingerprint) return info, err } imgfname := shared.VarPath("images", info.Fingerprint) err = shared.FileMove(imageTarf.Name(), imgfname) if err != nil { logger.Error( "Failed to move the tarfile", log.Ctx{ "err": err, "source": imageTarf.Name(), "dest": imgfname}) return info, err } imageMeta, err = getImageMetadata(imgfname) if err != nil { logger.Error( "Failed to get image metadata", log.Ctx{"err": err}) return info, err } } info.Architecture, _ = shared.ArchitectureId(imageMeta.Architecture) info.CreationDate = imageMeta.CreationDate info.ExpiryDate = imageMeta.ExpiryDate info.Properties = imageMeta.Properties if len(propHeaders) > 0 { for _, ph := range propHeaders { p, _ := url.ParseQuery(ph) for pkey, pval := range p { info.Properties[pkey] = pval[0] } } } return info, nil }
/* * This function takes a container or snapshot from the local image server and * exports it as an image. */ func imgPostContInfo(d *Daemon, r *http.Request, req imagePostReq, builddir string) (info shared.ImageInfo, err error) { info.Properties = map[string]string{} name := req.Source["name"] ctype := req.Source["type"] if ctype == "" || name == "" { return info, fmt.Errorf("No source provided") } switch ctype { case "snapshot": if !shared.IsSnapshot(name) { return info, fmt.Errorf("Not a snapshot") } case "container": if shared.IsSnapshot(name) { return info, fmt.Errorf("This is a snapshot") } default: return info, fmt.Errorf("Bad type") } info.Filename = req.Filename switch req.Public { case true: info.Public = true case false: info.Public = false } c, err := containerLoadByName(d, name) if err != nil { return info, err } // Build the actual image file tarfile, err := ioutil.TempFile(builddir, "lxd_build_tar_") if err != nil { return info, err } defer os.Remove(tarfile.Name()) if err := c.Export(tarfile); err != nil { tarfile.Close() return info, fmt.Errorf("imgPostContInfo: export failed: %s", err) } tarfile.Close() compress, err := d.ConfigValueGet("images.compression_algorithm") if err != nil { return info, err } // Default to gzip for this if compress == "" { compress = "gzip" } var compressedPath string if compress != "none" { compressedPath, err = compressFile(tarfile.Name(), compress) if err != nil { return info, err } } else { compressedPath = tarfile.Name() } defer os.Remove(compressedPath) sha256 := sha256.New() tarf, err := os.Open(compressedPath) if err != nil { return info, err } info.Size, err = io.Copy(sha256, tarf) tarf.Close() if err != nil { return info, err } info.Fingerprint = fmt.Sprintf("%x", sha256.Sum(nil)) _, err = dbImageGet(d.db, info.Fingerprint, false, true) if err == nil { return info, fmt.Errorf("The image already exists: %s", info.Fingerprint) } /* rename the the file to the expected name so our caller can use it */ finalName := shared.VarPath("images", info.Fingerprint) err = shared.FileMove(compressedPath, finalName) if err != nil { return info, err } info.Architecture = c.Architecture() info.Properties = req.Properties return info, nil }
/* * This function takes a container or snapshot from the local image server and * exports it as an image. */ func imgPostContInfo(d *Daemon, r *http.Request, req imagePostReq, builddir string) (info shared.ImageInfo, err error) { info.Properties = map[string]string{} name := req.Source["name"] ctype := req.Source["type"] if ctype == "" || name == "" { return info, fmt.Errorf("No source provided") } switch ctype { case "snapshot": if !shared.IsSnapshot(name) { return info, fmt.Errorf("Not a snapshot") } case "container": if shared.IsSnapshot(name) { return info, fmt.Errorf("This is a snapshot") } default: return info, fmt.Errorf("Bad type") } info.Filename = req.Filename switch req.Public { case true: info.Public = 1 case false: info.Public = 0 } snap := "" if ctype == "snapshot" { fields := strings.SplitN(name, "/", 2) if len(fields) != 2 { return info, fmt.Errorf("Not a snapshot") } name = fields[0] snap = fields[1] } c, err := newLxdContainer(name, d) if err != nil { return info, err } // Build the actual image file tarfile, err := ioutil.TempFile(builddir, "lxd_build_tar_") if err != nil { return info, err } if err := c.exportToTar(snap, tarfile); err != nil { tarfile.Close() return info, fmt.Errorf("imgPostContInfo: exportToTar failed: %s\n", err) } tarfile.Close() _, err = exec.Command("gzip", tarfile.Name()).CombinedOutput() if err != nil { shared.Debugf("image compression\n") return info, err } gztarpath := fmt.Sprintf("%s.gz", tarfile.Name()) sha256 := sha256.New() tarf, err := os.Open(gztarpath) if err != nil { return info, err } info.Size, err = io.Copy(sha256, tarf) tarf.Close() if err != nil { return info, err } info.Fingerprint = fmt.Sprintf("%x", sha256.Sum(nil)) /* rename the the file to the expected name so our caller can use it */ finalName := shared.VarPath("images", info.Fingerprint) err = shared.FileMove(gztarpath, finalName) if err != nil { return info, err } info.Architecture = c.architecture info.Properties = req.Properties return info, nil }