Do a version check and emit a warning if major/minor versions of @pulumi/pulumi do not match. (#2503)

This commit is contained in:
CyrusNajmabadi 2019-03-01 17:43:26 -08:00 committed by GitHub
parent 1b6fe6271f
commit 66ada50ccf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 68 additions and 14 deletions

View file

@ -16,6 +16,12 @@
- Fixes incorrect closure serialization issue (https://github.com/pulumi/pulumi/pull/2497)
- `pulumi` will now check that all versions of `@pulumi/pulumi` are compatible in your node_modules
folder, and will issue a warning message if not. To be compatible, the versions of
`@pulumi/pulumi` must agree on their major and minor versions. Running incompatible versions is
not something that will be blocked, but it is discouraged as it may lead to subtle problems if one
version of `@pulumi/pulumi` is loaded and passes objects to/from an incompatible version.
## 0.16.17 (Released February 27th, 2019)
### Improvements

View file

@ -52,6 +52,8 @@ import (
"github.com/pulumi/pulumi/pkg/version"
pulumirpc "github.com/pulumi/pulumi/sdk/proto/go"
"google.golang.org/grpc"
"github.com/blang/semver"
)
const (
@ -157,11 +159,40 @@ func newLanguageHost(nodePath, runPath, engineAddress,
// GetRequiredPlugins computes the complete set of anticipated plugins required by a program.
func (host *nodeLanguageHost) GetRequiredPlugins(ctx context.Context,
req *pulumirpc.GetRequiredPluginsRequest) (*pulumirpc.GetRequiredPluginsResponse, error) {
// To get the plugins required by a program, find all node_modules/ packages that contain { "pulumi": true }
// inside of their package.json files. We begin this search in the same directory that contains the project.
// It's possible that a developer would do a `require("../../elsewhere")` and that we'd miss this as a
// dependency, however the solution for that is simple: install the package in the project root.
plugins, err := getPluginsFromDir(req.GetProgram(), false)
// To get the plugins required by a program, find all node_modules/ packages that contain {
// "pulumi": true } inside of their package.json files. We begin this search in the same
// directory that contains the project. It's possible that a developer would do a
// `require("../../elsewhere")` and that we'd miss this as a dependency, however the solution
// for that is simple: install the package in the project root.
// Keep track of the versions of @pulumi/pulumi that are pulled in. If they differ on
// minor version, we will issue a warning to the user.
pulumiPackagePathToVersionMap := make(map[string]semver.Version)
plugins, err := getPluginsFromDir(req.GetProgram(), pulumiPackagePathToVersionMap, false /*inNodeModules*/)
if err == nil {
first := true
var firstPath string
var firstVersion semver.Version
for path, version := range pulumiPackagePathToVersionMap {
if first {
first = false
firstPath = path
firstVersion = version
continue
}
if version.Major != firstVersion.Major || version.Minor != firstVersion.Minor {
fmt.Fprintf(os.Stderr,
`Found incompatible versions of @pulumi/pulumi. Differing major or minor versions are not supported.
Version %s referenced at %s
Version %s referenced at %s
`, firstVersion, firstPath, version, path)
break
}
}
}
if err != nil {
glog.V(3).Infof("one or more errors while discovering plugins: %s", err)
}
@ -171,7 +202,10 @@ func (host *nodeLanguageHost) GetRequiredPlugins(ctx context.Context,
}
// getPluginsFromDir enumerates all node_modules/ directories, deeply, and returns the fully concatenated results.
func getPluginsFromDir(dir string, inNodeModules bool) ([]*pulumirpc.PluginDependency, error) {
func getPluginsFromDir(
dir string, pulumiPackagePathToVersionMap map[string]semver.Version,
inNodeModules bool) ([]*pulumirpc.PluginDependency, error) {
files, err := ioutil.ReadDir(dir)
if err != nil {
return nil, errors.Wrapf(err, "reading plugin dir %s", dir)
@ -191,7 +225,8 @@ func getPluginsFromDir(dir string, inNodeModules bool) ([]*pulumirpc.PluginDepen
}
if file.IsDir() {
// if a directory, recurse.
more, err := getPluginsFromDir(curr, inNodeModules || filepath.Base(dir) == "node_modules")
more, err := getPluginsFromDir(
curr, pulumiPackagePathToVersionMap, inNodeModules || filepath.Base(dir) == "node_modules")
if err != nil {
allErrors = multierror.Append(allErrors, err)
continue
@ -204,7 +239,25 @@ func getPluginsFromDir(dir string, inNodeModules bool) ([]*pulumirpc.PluginDepen
allErrors = multierror.Append(allErrors, errors.Wrapf(err, "reading package.json %s", curr))
continue
}
ok, name, version, err := getPackageInfo(b)
var info packageJSON
if err := json.Unmarshal(b, &info); err != nil {
allErrors = multierror.Append(allErrors, errors.Wrapf(err, "unmarshaling package.json %s", curr))
continue
}
if info.Name == "@pulumi/pulumi" {
version, err := semver.Parse(info.Version)
if err != nil {
allErrors = multierror.Append(
allErrors, errors.Wrapf(err, "Could not understand version %s in '%s'", info.Version, curr))
continue
}
pulumiPackagePathToVersionMap[curr] = version
}
ok, name, version, err := getPackageInfo(info)
if err != nil {
allErrors = multierror.Append(allErrors, errors.Wrapf(err, "unmarshaling package.json %s", curr))
} else if ok {
@ -230,12 +283,7 @@ type packageJSON struct {
// getPackageInfo returns a bool indicating whether the given package.json package has an associated Pulumi
// resource provider plugin. If it does, two strings are returned, the plugin name, and its semantic version.
func getPackageInfo(b []byte) (bool, string, string, error) {
var info packageJSON
if err := json.Unmarshal(b, &info); err != nil {
return false, "", "", err
}
func getPackageInfo(info packageJSON) (bool, string, string, error) {
if info.Pulumi.Resource {
name, err := getPluginName(info)
if err != nil {