From faff805f1ecf9de6eb7add5f0c0286a69f85b1dc Mon Sep 17 00:00:00 2001 From: Matt Ellis Date: Fri, 16 Nov 2018 17:53:57 -0800 Subject: [PATCH] Fix an issue where plugin install would fail on windows The issue is related to this code: https://github.com/pulumi/pulumi/blob/v0.16.4/pkg/workspace/plugins.go#L155-L195 Note that we use `defer` to ensure we close our handle to the file we are unpacking when we encounter a file in the tarball. However, the defers don't run until the containing function ends, so when we go to do the rename, or process still has a bunch of open file handles, which prevents the directory from being renamed because it is "in use". By doing all of the work in an anonymous function, we ensure that the defer statements run before we go to rename the directory Fixes #2217 --- CHANGELOG.md | 6 +++- pkg/workspace/plugins.go | 72 ++++++++++++++++++++++------------------ 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b8eb9dc8..cd1b7f482 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ -## 0.16.5 (unreleased) +## 0.16.6 (Unreleased) + +## 0.16.5 (Released Novemeber 16th, 2018) + +- Fix an issue where `pulumi plugin install` would fail on Windows with an access deined message. ## 0.16.4 (Released Novemeber 12th, 2018) diff --git a/pkg/workspace/plugins.go b/pkg/workspace/plugins.go index 4ef2fca6b..d164a452e 100644 --- a/pkg/workspace/plugins.go +++ b/pkg/workspace/plugins.go @@ -152,44 +152,52 @@ func (info PluginInfo) Install(tarball io.ReadCloser) error { return errors.Wrapf(err, "creating plugin directory %s", tempDir) } - // Unzip and untar the file as we go. - defer contract.IgnoreClose(tarball) - gzr, err := gzip.NewReader(tarball) - if err != nil { - return errors.Wrapf(err, "unzipping") - } - r := tar.NewReader(gzr) - for { - header, err := r.Next() - if err == io.EOF { - break - } else if err != nil { - return errors.Wrapf(err, "untarring") + // Unzip and untar the file as we go. We do this inside a function so that the `defer`'s to close files happen + // before we later try to rename the directory. Otherwise, the open file handles cause issues on Windows. + err = (func() error { + defer contract.IgnoreClose(tarball) + gzr, err := gzip.NewReader(tarball) + if err != nil { + return errors.Wrapf(err, "unzipping") } + r := tar.NewReader(gzr) + for { + header, err := r.Next() + if err == io.EOF { + break + } else if err != nil { + return errors.Wrapf(err, "untarring") + } - path := filepath.Join(tempDir, header.Name) + path := filepath.Join(tempDir, header.Name) - switch header.Typeflag { - case tar.TypeDir: - // Create any directories as needed. - if _, err := os.Stat(path); err != nil { - if err = os.MkdirAll(path, 0700); err != nil { - return errors.Wrapf(err, "untarring dir %s", path) + switch header.Typeflag { + case tar.TypeDir: + // Create any directories as needed. + if _, err := os.Stat(path); err != nil { + if err = os.MkdirAll(path, 0700); err != nil { + return errors.Wrapf(err, "untarring dir %s", path) + } } + case tar.TypeReg: + // Expand files into the target directory. + dst, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)) + if err != nil { + return errors.Wrapf(err, "opening file %s for untar", path) + } + defer contract.IgnoreClose(dst) + if _, err = io.Copy(dst, r); err != nil { + return errors.Wrapf(err, "untarring file %s", path) + } + default: + return errors.Errorf("unexpected plugin file type %s (%v)", header.Name, header.Typeflag) } - case tar.TypeReg: - // Expand files into the target directory. - dst, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)) - if err != nil { - return errors.Wrapf(err, "opening file %s for untar", path) - } - defer contract.IgnoreClose(dst) - if _, err = io.Copy(dst, r); err != nil { - return errors.Wrapf(err, "untarring file %s", path) - } - default: - return errors.Errorf("unexpected plugin file type %s (%v)", header.Name, header.Typeflag) } + + return nil + })() + if err != nil { + return err } return os.Rename(tempDir, finalDir)