Пример #1
0
// Write implements EnvironInfo.Write.
func (info *environInfo) Write() error {
	info.mu.Lock()
	defer info.mu.Unlock()
	data, err := goyaml.Marshal(info.EnvInfo)
	if err != nil {
		return errors.Annotate(err, "cannot marshal environment info")
	}
	// Create a temporary file and rename it, so that the data
	// changes atomically.
	parent, _ := filepath.Split(info.path)
	tmpFile, err := ioutil.TempFile(parent, "")
	if err != nil {
		return errors.Annotate(err, "cannot create temporary file")
	}
	_, err = tmpFile.Write(data)
	// N.B. We need to close the file before renaming it
	// otherwise it will fail under Windows with a file-in-use
	// error.
	tmpFile.Close()
	if err != nil {
		return errors.Annotate(err, "cannot write temporary file")
	}
	if err := utils.ReplaceFile(tmpFile.Name(), info.path); err != nil {
		os.Remove(tmpFile.Name())
		return errors.Annotate(err, "cannot rename new environment info file")
	}
	info.initialized = true
	return nil
}
Пример #2
0
// Unlock releases a held lock.  If the lock is not held ErrLockNotHeld is
// returned.
func (lock *Lock) Unlock() error {
	if !lock.IsLockHeld() {
		return ErrLockNotHeld
	}
	// To ensure reasonable unlocking, we should rename to a temp name, and delete that.
	lock.declareDead()
	tempLockName := fmt.Sprintf(".%s.%s", lock.name, lock.nonce)
	tempDirName := path.Join(lock.parent, tempLockName)
	// Now move the lock directory to the temp directory to release the lock.
	for i := 0; ; i++ {
		err := utils.ReplaceFile(lock.lockDir(), tempDirName)
		if err == nil {
			break
		}
		if i == 100 {
			logger.Debugf("Failed to replace lock, giving up: (%s)", err)
			return err
		}
		logger.Debugf("Failed to replace lock, retrying: (%s)", err)
		runtime.Gosched()
	}
	// And now cleanup.
	if err := os.RemoveAll(tempDirName); err != nil {
		logger.Debugf("Failed to remove lock: %s", err)
		return err
	}
	return nil
}
Пример #3
0
func (f *fileStorageWriter) Put(name string, r io.Reader, length int64) error {
	if isInternalPath(name) {
		return &os.PathError{
			Op:   "Put",
			Path: name,
			Err:  os.ErrPermission,
		}
	}
	fullpath := f.fullPath(name)
	dir := filepath.Dir(fullpath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}
	tmpdir := filepath.Join(f.path, ".tmp")
	if err := os.MkdirAll(tmpdir, 0755); err != nil {
		return err
	}
	defer os.Remove(tmpdir)
	// Write to a temporary file first, and then move (atomically).
	file, err := ioutil.TempFile(tmpdir, "juju-filestorage-")
	if err != nil {
		return err
	}
	_, err = io.CopyN(file, r, length)
	file.Close()
	if err != nil {
		os.Remove(file.Name())
		return err
	}
	return utils.ReplaceFile(file.Name(), fullpath)
}
Пример #4
0
// Get returns the charm referenced by curl.
// CacheDir must have been set, otherwise Get will panic.
func (s *LegacyCharmStore) Get(curl *charm.URL) (charm.Charm, error) {
	// The cache location must have been previously set.
	if CacheDir == "" {
		panic("charm cache directory path is empty")
	}
	if err := os.MkdirAll(CacheDir, os.FileMode(0755)); err != nil {
		return nil, err
	}
	revInfo, err := s.revisions(curl)
	if err != nil {
		return nil, err
	}
	if len(revInfo) != 1 {
		return nil, fmt.Errorf("expected 1 result, got %d", len(revInfo))
	}
	if revInfo[0].Err != nil {
		return nil, revInfo[0].Err
	}
	rev, digest := revInfo[0].Revision, revInfo[0].Sha256
	if curl.Revision == -1 {
		curl = curl.WithRevision(rev)
	} else if curl.Revision != rev {
		return nil, fmt.Errorf("store returned charm with wrong revision %d for %q", rev, curl.String())
	}
	path := filepath.Join(CacheDir, charm.Quote(curl.String())+".charm")
	if verify(path, digest) != nil {
		store_url := s.BaseURL + "/charm/" + url.QueryEscape(curl.Path())
		if s.testMode {
			store_url = store_url + "?stats=0"
		}
		resp, err := s.get(store_url)
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()
		f, err := ioutil.TempFile(CacheDir, "charm-download")
		if err != nil {
			return nil, err
		}
		dlPath := f.Name()
		_, err = io.Copy(f, resp.Body)
		if cerr := f.Close(); err == nil {
			err = cerr
		}
		if err != nil {
			os.Remove(dlPath)
			return nil, err
		}
		if err := utils.ReplaceFile(dlPath, path); err != nil {
			return nil, err
		}
	}
	if err := verify(path, digest); err != nil {
		return nil, err
	}
	return charm.ReadCharmArchive(path)
}
Пример #5
0
// Get implements Interface.Get.
func (s *CharmStore) Get(curl *charm.URL) (charm.Charm, error) {
	// The cache location must have been previously set.
	if CacheDir == "" {
		panic("charm cache directory path is empty")
	}
	if curl.Series == "bundle" {
		return nil, errgo.Newf("expected a charm URL, got bundle URL %q", curl)
	}

	// Prepare the cache directory and retrieve the charm.
	if err := os.MkdirAll(CacheDir, 0755); err != nil {
		return nil, errgo.Notef(err, "cannot create the cache directory")
	}
	r, id, expectHash, expectSize, err := s.client.GetArchive(curl.Reference())
	if err != nil {
		if errgo.Cause(err) == params.ErrNotFound {
			// Make a prettier error message for the user.
			return nil, errgo.WithCausef(nil, params.ErrNotFound, "cannot retrieve charm %q: charm not found", curl)
		}
		return nil, errgo.NoteMask(err, fmt.Sprintf("cannot retrieve charm %q", curl), errgo.Any)
	}
	defer r.Close()

	// Check if the archive already exists in the cache.
	path := filepath.Join(CacheDir, charm.Quote(id.String())+".charm")
	if verifyHash384AndSize(path, expectHash, expectSize) == nil {
		return charm.ReadCharmArchive(path)
	}

	// Verify and save the new archive.
	f, err := ioutil.TempFile(CacheDir, "charm-download")
	if err != nil {
		return nil, errgo.Notef(err, "cannot make temporary file")
	}
	defer f.Close()
	hash := sha512.New384()
	size, err := io.Copy(io.MultiWriter(hash, f), r)
	if err != nil {
		return nil, errgo.Notef(err, "cannot read charm archive")
	}
	if size != expectSize {
		return nil, errgo.Newf("size mismatch; network corruption?")
	}
	if fmt.Sprintf("%x", hash.Sum(nil)) != expectHash {
		return nil, errgo.Newf("hash mismatch; network corruption?")
	}

	// Move the archive to the expected place, and return the charm.
	if err := utils.ReplaceFile(f.Name(), path); err != nil {
		return nil, errgo.Notef(err, "cannot move the charm archive")
	}
	return charm.ReadCharmArchive(path)
}
Пример #6
0
// Unlock releases a held lock.  If the lock is not held ErrLockNotHeld is
// returned.
func (lock *Lock) Unlock() error {
	if !lock.IsLockHeld() {
		return ErrLockNotHeld
	}
	// To ensure reasonable unlocking, we should rename to a temp name, and delete that.
	tempLockName := fmt.Sprintf(".%s.%x", lock.name, lock.nonce)
	tempDirName := path.Join(lock.parent, tempLockName)
	// Now move the lock directory to the temp directory to release the lock.
	if err := utils.ReplaceFile(lock.lockDir(), tempDirName); err != nil {
		return err
	}
	// And now cleanup.
	return os.RemoveAll(tempDirName)
}
Пример #7
0
// If message is set, it will write the message to the lock directory as the
// lock is taken.
func (lock *Lock) acquire(message string) (bool, error) {
	// If the lockDir exists, then the lock is held by someone else.
	_, err := os.Stat(lock.lockDir())
	if err == nil {
		return false, nil
	}
	if !os.IsNotExist(err) {
		return false, err
	}
	// Create a temporary directory (in the parent dir), and then move it to
	// the right name.  Using the same directory to make sure the directories
	// are on the same filesystem.  Use a directory name starting with "." as
	// it isn't a valid lock name.
	tempLockName := fmt.Sprintf(".%s", lock.nonce)
	tempDirName, err := ioutil.TempDir(lock.parent, tempLockName)
	if err != nil {
		return false, err // this shouldn't really fail...
	}

	// write lock into the temp dir
	l := onDisk{
		PID:     lock.PID,
		Nonce:   lock.nonce,
		Message: message,
	}
	lockInfo, err := goyaml.Marshal(&l)
	if err != nil {
		return false, err // this shouldn't fail either...
	}
	err = ioutil.WriteFile(path.Join(tempDirName, heldFilename), lockInfo, 0664)
	if err != nil {
		return false, err
	}
	// Now move the temp directory to the lock directory.
	err = utils.ReplaceFile(tempDirName, lock.lockDir())
	if err != nil {
		// Any error on rename means we failed.
		// Beaten to it, clean up temporary directory.
		os.RemoveAll(tempDirName)
		return false, nil
	}
	// We now have the lock.
	lock.createAliveFile()
	return true, nil
}
Пример #8
0
// finishDeploy persists the fact that we've finished deploying the staged bundle.
func (d *manifestDeployer) finishDeploy() error {
	logger.Debugf("finishing deploy of charm %q", d.staged.url)
	oldPath := d.CharmPath(deployingURLPath)
	newPath := d.CharmPath(charmURLPath)
	return utils.ReplaceFile(oldPath, newPath)
}