// setRaw interprets a name=value string and sets the supplied value. func (v *Value) setRaw(raw string) error { eq := strings.Index(raw, "=") if eq <= 0 { return fmt.Errorf("malformed constraint %q", raw) } name, str := raw[:eq], raw[eq+1:] var err error switch name { case Arch: err = v.setArch(str) case Container: err = v.setContainer(str) case CpuCores: err = v.setCpuCores(str) case CpuPower: err = v.setCpuPower(str) case Mem: err = v.setMem(str) case RootDisk: err = v.setRootDisk(str) case Tags: err = v.setTags(str) case InstanceType: err = v.setInstanceType(str) default: return fmt.Errorf("unknown constraint %q", name) } if err != nil { return errgo.Annotatef(err, "bad %q constraint", name) } return nil }
func (cfg *MachineConfig) addMachineAgentToBoot(c *cloudinit.Config, tag, machineId string) error { // Make the agent run via a symbolic link to the actual tools // directory, so it can upgrade itself without needing to change // the upstart script. toolsDir := agenttools.ToolsDir(cfg.DataDir, tag) // TODO(dfc) ln -nfs, so it doesn't fail if for some reason that the target already exists c.AddScripts(fmt.Sprintf("ln -s %v %s", cfg.Tools.Version, shquote(toolsDir))) name := cfg.MachineAgentServiceName conf := upstart.MachineAgentUpstartService(name, toolsDir, cfg.DataDir, cfg.LogDir, tag, machineId, nil) cmds, err := conf.InstallCommands() if err != nil { return errgo.Annotatef(err, "cannot make cloud-init upstart script for the %s agent", tag) } c.AddRunCmd(cloudinit.LogProgressCmd("Starting Juju machine agent (%s)", name)) c.AddScripts(cmds...) return nil }
// startMachine creates a new data value for tracking details of the // machine and starts watching the machine for units added or removed. func (fw *Firewaller) startMachine(tag string) error { machined := &machineData{ fw: fw, tag: tag, unitds: make(map[string]*unitData), ports: make([]instance.Port, 0), } m, err := machined.machine() if params.IsCodeNotFound(err) { return nil } else if err != nil { return errgo.Annotate(err, "cannot watch machine units") } unitw, err := m.WatchUnits() if err != nil { return err } select { case <-fw.tomb.Dying(): stop("units watcher", unitw) return tomb.ErrDying case change, ok := <-unitw.Changes(): if !ok { stop("units watcher", unitw) return watcher.MustErr(unitw) } fw.machineds[tag] = machined err = fw.unitsChanged(&unitsChange{machined, change}) if err != nil { stop("units watcher", unitw) delete(fw.machineds, tag) return errgo.Annotatef(err, "cannot respond to units changes for %q", tag) } } go machined.watchLoop(unitw) return nil }
// AddCharm adds the given charm URL (which must include revision) to // the environment, if it does not exist yet. Local charms are not // supported, only charm store URLs. See also AddLocalCharm(). func (c *Client) AddCharm(args params.CharmURL) error { charmURL, err := charm.ParseURL(args.URL) if err != nil { return err } if charmURL.Schema != "cs" { return fmt.Errorf("only charm store charm URLs are supported, with cs: schema") } if charmURL.Revision < 0 { return fmt.Errorf("charm URL must include revision") } // First, check if a pending or a real charm exists in state. stateCharm, err := c.api.state.PrepareStoreCharmUpload(charmURL) if err == nil && stateCharm.IsUploaded() { // Charm already in state (it was uploaded already). return nil } else if err != nil { return err } // Get the charm and its information from the store. envConfig, err := c.api.state.EnvironConfig() if err != nil { return err } store := config.SpecializeCharmRepo(CharmStore, envConfig) downloadedCharm, err := store.Get(charmURL) if err != nil { return errgo.Annotatef(err, "cannot download charm %q", charmURL.String()) } // Open it and calculate the SHA256 hash. downloadedBundle, ok := downloadedCharm.(*charm.Bundle) if !ok { return errgo.New("expected a charm archive, got %T", downloadedCharm) } archive, err := os.Open(downloadedBundle.Path) if err != nil { return errgo.Annotate(err, "cannot read downloaded charm") } defer archive.Close() bundleSHA256, size, err := utils.ReadSHA256(archive) if err != nil { return errgo.Annotate(err, "cannot calculate SHA256 hash of charm") } if _, err := archive.Seek(0, 0); err != nil { return errgo.Annotate(err, "cannot rewind charm archive") } // Get the environment storage and upload the charm. env, err := environs.New(envConfig) if err != nil { return errgo.Annotate(err, "cannot access environment") } storage := env.Storage() archiveName, err := CharmArchiveName(charmURL.Name, charmURL.Revision) if err != nil { return errgo.Annotate(err, "cannot generate charm archive name") } if err := storage.Put(archiveName, archive, size); err != nil { return errgo.Annotate(err, "cannot upload charm to provider storage") } storageURL, err := storage.URL(archiveName) if err != nil { return errgo.Annotate(err, "cannot get storage URL for charm") } bundleURL, err := url.Parse(storageURL) if err != nil { return errgo.Annotate(err, "cannot parse storage URL") } // Finally, update the charm data in state and mark it as no longer pending. _, err = c.api.state.UpdateUploadedCharm(downloadedCharm, charmURL, bundleURL, bundleSHA256) if err == state.ErrCharmRevisionAlreadyModified || state.IsCharmAlreadyUploadedError(err) { // This is not an error, it just signifies somebody else // managed to upload and update the charm in state before // us. This means we have to delete what we just uploaded // to storage. if err := storage.Remove(archiveName); err != nil { errgo.Annotate(err, "cannot remove duplicated charm from storage") } return nil } return err }
// UnpackTools reads a set of juju tools in gzipped tar-archive // format and unpacks them into the appropriate tools directory // within dataDir. If a valid tools directory already exists, // UnpackTools returns without error. func UnpackTools(dataDir string, tools *coretools.Tools, r io.Reader) (err error) { // Unpack the gzip file and compute the checksum. sha256hash := sha256.New() zr, err := gzip.NewReader(io.TeeReader(r, sha256hash)) if err != nil { return err } defer zr.Close() f, err := ioutil.TempFile(os.TempDir(), "tools-tar") if err != nil { return err } _, err = io.Copy(f, zr) if err != nil { return err } defer os.Remove(f.Name()) // TODO(wallyworld) - 2013-09-24 bug=1229512 // When we can ensure all tools records have valid checksums recorded, // we can remove this test short circuit. gzipSHA256 := fmt.Sprintf("%x", sha256hash.Sum(nil)) if tools.SHA256 != "" && tools.SHA256 != gzipSHA256 { return fmt.Errorf("tarball sha256 mismatch, expected %s, got %s", tools.SHA256, gzipSHA256) } // Make a temporary directory in the tools directory, // first ensuring that the tools directory exists. toolsDir := path.Join(dataDir, "tools") err = os.MkdirAll(toolsDir, 0755) if err != nil { return err } dir, err := ioutil.TempDir(toolsDir, "unpacking-") if err != nil { return err } defer removeAll(dir) // Checksum matches, now reset the file and untar it. _, err = f.Seek(0, 0) if err != nil { return err } tr := tar.NewReader(f) for { hdr, err := tr.Next() if err == io.EOF { break } if err != nil { return err } if strings.ContainsAny(hdr.Name, "/\\") { return fmt.Errorf("bad name %q in tools archive", hdr.Name) } if hdr.Typeflag != tar.TypeReg { return fmt.Errorf("bad file type %c in file %q in tools archive", hdr.Typeflag, hdr.Name) } name := path.Join(dir, hdr.Name) if err := writeFile(name, os.FileMode(hdr.Mode&0777), tr); err != nil { return errgo.Annotatef(err, "tar extract %q failed", name) } } toolsMetadataData, err := json.Marshal(tools) if err != nil { return err } err = ioutil.WriteFile(path.Join(dir, toolsFile), []byte(toolsMetadataData), 0644) if err != nil { return err } // The tempdir is created with 0700, so we need to make it more // accessable for juju-run. err = os.Chmod(dir, 0755) if err != nil { return err } err = os.Rename(dir, SharedToolsDir(dataDir, tools.Version)) // If we've failed to rename the directory, it may be because // the directory already exists - if ReadTools succeeds, we // assume all's ok. if err != nil { if _, err := ReadTools(dataDir, tools.Version); err == nil { return nil } } return err }