2018-05-22 19:43:36 +00:00
|
|
|
// Copyright 2016-2018, Pulumi Corporation.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
2022-03-10 18:45:50 +00:00
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
2018-05-22 19:43:36 +00:00
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
2018-02-10 02:15:04 +00:00
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2024-08-28 09:46:54 +00:00
|
|
|
"bytes"
|
2022-01-07 04:14:09 +00:00
|
|
|
"context"
|
2024-08-28 09:46:54 +00:00
|
|
|
"fmt"
|
2022-01-07 04:14:09 +00:00
|
|
|
"os"
|
2024-07-23 16:10:43 +00:00
|
|
|
"os/exec"
|
2022-01-07 04:14:09 +00:00
|
|
|
"path/filepath"
|
2024-08-28 09:46:54 +00:00
|
|
|
"runtime"
|
2018-02-10 02:15:04 +00:00
|
|
|
"strings"
|
2024-07-25 12:14:02 +00:00
|
|
|
"syscall"
|
2018-02-10 02:15:04 +00:00
|
|
|
"testing"
|
2024-07-23 16:10:43 +00:00
|
|
|
"time"
|
2018-02-10 02:15:04 +00:00
|
|
|
|
2020-03-30 22:10:42 +00:00
|
|
|
"github.com/blang/semver"
|
2024-06-21 11:35:06 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/nodejs/npm"
|
2021-03-17 13:20:05 +00:00
|
|
|
pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go"
|
2018-02-10 02:15:04 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
2023-01-23 17:18:14 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2018-02-10 02:15:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestArgumentConstruction(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
Pass root and main info to language host methods (#14654)
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
This is two changes rolled together in a way.
Firstly passing some of the data that we pass on language runtime
startup to also pass it to Run/GetRequiredPlugins/etc. This is needed
for matrix testing, as we only get to start the language runtime up once
for that but want to execute multiple programs with it.
I feel it's also a little more consistent as we use the language
runtimes in other contexts (codegen) where there isn't really a root
directory, and aren't any options (and if we did do options the options
for codegen are not going to be the same as for execution). It also
means we can reuse a language host for shimless and substack programs,
as before they heavily relied on their current working directory to
calculate paths, and obviosly could only take one set of options at
startup. Imagine a shimless python package + a python root program, that
would have needed two startups of the python language host to deal with,
this unblocks it so we can make the engine smarter and only use one.
Secondly renaming some of the fields we pass to
Run/GetRequiredPlugins/etc today. `Pwd` and `Program` were not very
descriptive and had pretty non-obvious documentation:
```
string pwd = 3; // the program's working directory.
string program = 4; // the path to the program to execute.
```
`pwd` will remain, although probably rename it to `working_directory` at
some point, because while today we always start programs up with the
working directory equal to the program directory that definitely is
going to change in the future (at least for MLCs and substack programs).
But the name `pwd` doesn't make it clear that this was intended to be
the working directory _and_ the directory which contains the program.
`program` was in fact nearly always ".", and if it wasn't that it was
just a filename. The engine never sent a path for `program` (although we
did have some unit tests to check how that worked for the nodejs and
python hosts).
These are now replaced by a new structure with (I think) more clearly
named and documented fields (see ProgramInfo in langauge.proto).
The engine still sends the old data for now, we need to update
dotnet/yaml/java before we break the old interface and give Virtus Labs
a chance to update [besom](https://github.com/VirtusLab/besom).
## Checklist
- [x] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-12-10 17:30:51 +00:00
|
|
|
info := &pulumirpc.ProgramInfo{
|
|
|
|
RootDirectory: "/foo/bar",
|
|
|
|
ProgramDirectory: "/foo/bar",
|
|
|
|
EntryPoint: ".",
|
|
|
|
}
|
|
|
|
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Run("DryRun-NoArguments", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2018-02-10 02:15:04 +00:00
|
|
|
host := &nodeLanguageHost{}
|
Pass root and main info to language host methods (#14654)
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
This is two changes rolled together in a way.
Firstly passing some of the data that we pass on language runtime
startup to also pass it to Run/GetRequiredPlugins/etc. This is needed
for matrix testing, as we only get to start the language runtime up once
for that but want to execute multiple programs with it.
I feel it's also a little more consistent as we use the language
runtimes in other contexts (codegen) where there isn't really a root
directory, and aren't any options (and if we did do options the options
for codegen are not going to be the same as for execution). It also
means we can reuse a language host for shimless and substack programs,
as before they heavily relied on their current working directory to
calculate paths, and obviosly could only take one set of options at
startup. Imagine a shimless python package + a python root program, that
would have needed two startups of the python language host to deal with,
this unblocks it so we can make the engine smarter and only use one.
Secondly renaming some of the fields we pass to
Run/GetRequiredPlugins/etc today. `Pwd` and `Program` were not very
descriptive and had pretty non-obvious documentation:
```
string pwd = 3; // the program's working directory.
string program = 4; // the path to the program to execute.
```
`pwd` will remain, although probably rename it to `working_directory` at
some point, because while today we always start programs up with the
working directory equal to the program directory that definitely is
going to change in the future (at least for MLCs and substack programs).
But the name `pwd` doesn't make it clear that this was intended to be
the working directory _and_ the directory which contains the program.
`program` was in fact nearly always ".", and if it wasn't that it was
just a filename. The engine never sent a path for `program` (although we
did have some unit tests to check how that worked for the nodejs and
python hosts).
These are now replaced by a new structure with (I think) more clearly
named and documented fields (see ProgramInfo in langauge.proto).
The engine still sends the old data for now, we need to update
dotnet/yaml/java before we break the old interface and give Virtus Labs
a chance to update [besom](https://github.com/VirtusLab/besom).
## Checklist
- [x] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-12-10 17:30:51 +00:00
|
|
|
rr := &pulumirpc.RunRequest{DryRun: true, Info: info}
|
2022-04-03 14:54:59 +00:00
|
|
|
args := host.constructArguments(rr, "", "", "")
|
2022-03-04 08:17:41 +00:00
|
|
|
assert.Contains(t, args, "--dry-run")
|
|
|
|
assert.NotContains(t, args, "true")
|
2018-02-10 02:15:04 +00:00
|
|
|
})
|
|
|
|
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Run("OptionalArgs-PassedIfSpecified", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2018-02-10 02:15:04 +00:00
|
|
|
host := &nodeLanguageHost{}
|
Pass root and main info to language host methods (#14654)
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
This is two changes rolled together in a way.
Firstly passing some of the data that we pass on language runtime
startup to also pass it to Run/GetRequiredPlugins/etc. This is needed
for matrix testing, as we only get to start the language runtime up once
for that but want to execute multiple programs with it.
I feel it's also a little more consistent as we use the language
runtimes in other contexts (codegen) where there isn't really a root
directory, and aren't any options (and if we did do options the options
for codegen are not going to be the same as for execution). It also
means we can reuse a language host for shimless and substack programs,
as before they heavily relied on their current working directory to
calculate paths, and obviosly could only take one set of options at
startup. Imagine a shimless python package + a python root program, that
would have needed two startups of the python language host to deal with,
this unblocks it so we can make the engine smarter and only use one.
Secondly renaming some of the fields we pass to
Run/GetRequiredPlugins/etc today. `Pwd` and `Program` were not very
descriptive and had pretty non-obvious documentation:
```
string pwd = 3; // the program's working directory.
string program = 4; // the path to the program to execute.
```
`pwd` will remain, although probably rename it to `working_directory` at
some point, because while today we always start programs up with the
working directory equal to the program directory that definitely is
going to change in the future (at least for MLCs and substack programs).
But the name `pwd` doesn't make it clear that this was intended to be
the working directory _and_ the directory which contains the program.
`program` was in fact nearly always ".", and if it wasn't that it was
just a filename. The engine never sent a path for `program` (although we
did have some unit tests to check how that worked for the nodejs and
python hosts).
These are now replaced by a new structure with (I think) more clearly
named and documented fields (see ProgramInfo in langauge.proto).
The engine still sends the old data for now, we need to update
dotnet/yaml/java before we break the old interface and give Virtus Labs
a chance to update [besom](https://github.com/VirtusLab/besom).
## Checklist
- [x] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-12-10 17:30:51 +00:00
|
|
|
rr := &pulumirpc.RunRequest{Project: "foo", Info: info}
|
2022-04-03 14:54:59 +00:00
|
|
|
args := strings.Join(host.constructArguments(rr, "", "", ""), " ")
|
2022-03-04 08:17:41 +00:00
|
|
|
assert.Contains(t, args, "--project foo")
|
2018-02-10 02:15:04 +00:00
|
|
|
})
|
|
|
|
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Run("OptionalArgs-NotPassedIfNotSpecified", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2018-02-10 02:15:04 +00:00
|
|
|
host := &nodeLanguageHost{}
|
Pass root and main info to language host methods (#14654)
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
This is two changes rolled together in a way.
Firstly passing some of the data that we pass on language runtime
startup to also pass it to Run/GetRequiredPlugins/etc. This is needed
for matrix testing, as we only get to start the language runtime up once
for that but want to execute multiple programs with it.
I feel it's also a little more consistent as we use the language
runtimes in other contexts (codegen) where there isn't really a root
directory, and aren't any options (and if we did do options the options
for codegen are not going to be the same as for execution). It also
means we can reuse a language host for shimless and substack programs,
as before they heavily relied on their current working directory to
calculate paths, and obviosly could only take one set of options at
startup. Imagine a shimless python package + a python root program, that
would have needed two startups of the python language host to deal with,
this unblocks it so we can make the engine smarter and only use one.
Secondly renaming some of the fields we pass to
Run/GetRequiredPlugins/etc today. `Pwd` and `Program` were not very
descriptive and had pretty non-obvious documentation:
```
string pwd = 3; // the program's working directory.
string program = 4; // the path to the program to execute.
```
`pwd` will remain, although probably rename it to `working_directory` at
some point, because while today we always start programs up with the
working directory equal to the program directory that definitely is
going to change in the future (at least for MLCs and substack programs).
But the name `pwd` doesn't make it clear that this was intended to be
the working directory _and_ the directory which contains the program.
`program` was in fact nearly always ".", and if it wasn't that it was
just a filename. The engine never sent a path for `program` (although we
did have some unit tests to check how that worked for the nodejs and
python hosts).
These are now replaced by a new structure with (I think) more clearly
named and documented fields (see ProgramInfo in langauge.proto).
The engine still sends the old data for now, we need to update
dotnet/yaml/java before we break the old interface and give Virtus Labs
a chance to update [besom](https://github.com/VirtusLab/besom).
## Checklist
- [x] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-12-10 17:30:51 +00:00
|
|
|
rr := &pulumirpc.RunRequest{Info: info}
|
2022-04-03 14:54:59 +00:00
|
|
|
args := strings.Join(host.constructArguments(rr, "", "", ""), " ")
|
2022-03-04 08:17:41 +00:00
|
|
|
assert.NotContains(t, args, "--stack")
|
2018-02-10 02:15:04 +00:00
|
|
|
})
|
|
|
|
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Run("DotIfProgramNotSpecified", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2018-02-10 02:15:04 +00:00
|
|
|
host := &nodeLanguageHost{}
|
Pass root and main info to language host methods (#14654)
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
This is two changes rolled together in a way.
Firstly passing some of the data that we pass on language runtime
startup to also pass it to Run/GetRequiredPlugins/etc. This is needed
for matrix testing, as we only get to start the language runtime up once
for that but want to execute multiple programs with it.
I feel it's also a little more consistent as we use the language
runtimes in other contexts (codegen) where there isn't really a root
directory, and aren't any options (and if we did do options the options
for codegen are not going to be the same as for execution). It also
means we can reuse a language host for shimless and substack programs,
as before they heavily relied on their current working directory to
calculate paths, and obviosly could only take one set of options at
startup. Imagine a shimless python package + a python root program, that
would have needed two startups of the python language host to deal with,
this unblocks it so we can make the engine smarter and only use one.
Secondly renaming some of the fields we pass to
Run/GetRequiredPlugins/etc today. `Pwd` and `Program` were not very
descriptive and had pretty non-obvious documentation:
```
string pwd = 3; // the program's working directory.
string program = 4; // the path to the program to execute.
```
`pwd` will remain, although probably rename it to `working_directory` at
some point, because while today we always start programs up with the
working directory equal to the program directory that definitely is
going to change in the future (at least for MLCs and substack programs).
But the name `pwd` doesn't make it clear that this was intended to be
the working directory _and_ the directory which contains the program.
`program` was in fact nearly always ".", and if it wasn't that it was
just a filename. The engine never sent a path for `program` (although we
did have some unit tests to check how that worked for the nodejs and
python hosts).
These are now replaced by a new structure with (I think) more clearly
named and documented fields (see ProgramInfo in langauge.proto).
The engine still sends the old data for now, we need to update
dotnet/yaml/java before we break the old interface and give Virtus Labs
a chance to update [besom](https://github.com/VirtusLab/besom).
## Checklist
- [x] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-12-10 17:30:51 +00:00
|
|
|
rr := &pulumirpc.RunRequest{Info: info}
|
2022-04-03 14:54:59 +00:00
|
|
|
args := strings.Join(host.constructArguments(rr, "", "", ""), " ")
|
2022-03-04 08:17:41 +00:00
|
|
|
assert.Contains(t, args, ".")
|
2018-02-10 02:15:04 +00:00
|
|
|
})
|
|
|
|
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Run("ProgramIfProgramSpecified", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2018-02-10 02:15:04 +00:00
|
|
|
host := &nodeLanguageHost{}
|
Pass root and main info to language host methods (#14654)
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
This is two changes rolled together in a way.
Firstly passing some of the data that we pass on language runtime
startup to also pass it to Run/GetRequiredPlugins/etc. This is needed
for matrix testing, as we only get to start the language runtime up once
for that but want to execute multiple programs with it.
I feel it's also a little more consistent as we use the language
runtimes in other contexts (codegen) where there isn't really a root
directory, and aren't any options (and if we did do options the options
for codegen are not going to be the same as for execution). It also
means we can reuse a language host for shimless and substack programs,
as before they heavily relied on their current working directory to
calculate paths, and obviosly could only take one set of options at
startup. Imagine a shimless python package + a python root program, that
would have needed two startups of the python language host to deal with,
this unblocks it so we can make the engine smarter and only use one.
Secondly renaming some of the fields we pass to
Run/GetRequiredPlugins/etc today. `Pwd` and `Program` were not very
descriptive and had pretty non-obvious documentation:
```
string pwd = 3; // the program's working directory.
string program = 4; // the path to the program to execute.
```
`pwd` will remain, although probably rename it to `working_directory` at
some point, because while today we always start programs up with the
working directory equal to the program directory that definitely is
going to change in the future (at least for MLCs and substack programs).
But the name `pwd` doesn't make it clear that this was intended to be
the working directory _and_ the directory which contains the program.
`program` was in fact nearly always ".", and if it wasn't that it was
just a filename. The engine never sent a path for `program` (although we
did have some unit tests to check how that worked for the nodejs and
python hosts).
These are now replaced by a new structure with (I think) more clearly
named and documented fields (see ProgramInfo in langauge.proto).
The engine still sends the old data for now, we need to update
dotnet/yaml/java before we break the old interface and give Virtus Labs
a chance to update [besom](https://github.com/VirtusLab/besom).
## Checklist
- [x] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-12-10 17:30:51 +00:00
|
|
|
rr := &pulumirpc.RunRequest{
|
|
|
|
Program: "foobar",
|
|
|
|
Info: &pulumirpc.ProgramInfo{
|
|
|
|
RootDirectory: "/foo/bar",
|
|
|
|
ProgramDirectory: "/foo/bar",
|
|
|
|
EntryPoint: "foobar",
|
|
|
|
},
|
|
|
|
}
|
2022-04-03 14:54:59 +00:00
|
|
|
args := strings.Join(host.constructArguments(rr, "", "", ""), " ")
|
2022-03-04 08:17:41 +00:00
|
|
|
assert.Contains(t, args, "foobar")
|
2018-02-10 02:15:04 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestConfig(t *testing.T) {
|
|
|
|
t.Parallel()
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Run("Config-Empty", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
2018-02-10 02:15:04 +00:00
|
|
|
host := &nodeLanguageHost{}
|
|
|
|
rr := &pulumirpc.RunRequest{Project: "foo"}
|
|
|
|
str, err := host.constructConfig(rr)
|
2022-03-04 08:17:41 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.JSONEq(t, "{}", str)
|
2018-02-10 02:15:04 +00:00
|
|
|
})
|
|
|
|
}
|
2020-03-30 22:10:42 +00:00
|
|
|
|
|
|
|
func TestCompatibleVersions(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
cases := []struct {
|
|
|
|
a string
|
|
|
|
b string
|
|
|
|
compatible bool
|
|
|
|
errmsg string
|
|
|
|
}{
|
|
|
|
{"0.17.1", "0.16.2", false, "Differing major or minor versions are not supported."},
|
|
|
|
{"0.17.1", "1.0.0", true, ""},
|
|
|
|
{"1.0.0", "0.17.1", true, ""},
|
|
|
|
{"1.13.0", "1.13.0", true, ""},
|
|
|
|
{"1.1.1", "1.13.0", true, ""},
|
|
|
|
{"1.13.0", "1.1.1", true, ""},
|
|
|
|
{"1.1.0", "2.1.0", true, ""},
|
|
|
|
{"2.1.0", "1.1.0", true, ""},
|
|
|
|
{"1.1.0", "2.0.0-beta1", true, ""},
|
|
|
|
{"2.0.0-beta1", "1.1.0", true, ""},
|
|
|
|
{"2.1.0", "3.1.0", false, "Differing major versions are not supported."},
|
|
|
|
{"0.16.1", "1.0.0", false, "Differing major or minor versions are not supported."},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range cases {
|
|
|
|
compatible, errmsg := compatibleVersions(semver.MustParse(c.a), semver.MustParse(c.b))
|
|
|
|
assert.Equal(t, c.errmsg, errmsg)
|
|
|
|
assert.Equal(t, c.compatible, compatible)
|
|
|
|
}
|
|
|
|
}
|
2022-01-07 04:14:09 +00:00
|
|
|
|
|
|
|
func TestGetRequiredPlugins(t *testing.T) {
|
2022-03-04 08:17:41 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
2022-12-03 07:17:08 +00:00
|
|
|
dir := t.TempDir()
|
2022-01-07 04:14:09 +00:00
|
|
|
|
|
|
|
files := []struct {
|
|
|
|
path string
|
|
|
|
content string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
filepath.Join(dir, "node_modules", "@pulumi", "foo", "package.json"),
|
|
|
|
`{ "name": "@pulumi/foo", "version": "1.2.3", "pulumi": { "resource": true } }`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
filepath.Join(dir, "node_modules", "@pulumi", "bar", "package.json"),
|
|
|
|
`{ "name": "@pulumi/bar", "version": "4.5.6", "pulumi": { "resource": true } }`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
filepath.Join(dir, "node_modules", "@pulumi", "baz", "package.json"),
|
|
|
|
`{ "name": "@pulumi/baz", "version": "4.5.6", "pulumi": { "resource": false } }`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
filepath.Join(dir, "node_modules", "malformed", "tests", "malformed_test", "package.json"),
|
|
|
|
`{`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, file := range files {
|
2023-03-03 16:36:39 +00:00
|
|
|
err := os.MkdirAll(filepath.Dir(file.path), 0o755)
|
2023-01-23 19:53:29 +00:00
|
|
|
require.NoError(t, err)
|
2023-03-03 16:36:39 +00:00
|
|
|
err = os.WriteFile(file.path, []byte(file.content), 0o600)
|
2023-01-23 19:53:29 +00:00
|
|
|
require.NoError(t, err)
|
2022-01-07 04:14:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
host := &nodeLanguageHost{}
|
2023-09-25 12:25:26 +00:00
|
|
|
resp, err := host.GetRequiredPlugins(context.Background(), &pulumirpc.GetRequiredPluginsRequest{
|
2022-01-07 04:14:09 +00:00
|
|
|
Program: dir,
|
Pass root and main info to language host methods (#14654)
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
This is two changes rolled together in a way.
Firstly passing some of the data that we pass on language runtime
startup to also pass it to Run/GetRequiredPlugins/etc. This is needed
for matrix testing, as we only get to start the language runtime up once
for that but want to execute multiple programs with it.
I feel it's also a little more consistent as we use the language
runtimes in other contexts (codegen) where there isn't really a root
directory, and aren't any options (and if we did do options the options
for codegen are not going to be the same as for execution). It also
means we can reuse a language host for shimless and substack programs,
as before they heavily relied on their current working directory to
calculate paths, and obviosly could only take one set of options at
startup. Imagine a shimless python package + a python root program, that
would have needed two startups of the python language host to deal with,
this unblocks it so we can make the engine smarter and only use one.
Secondly renaming some of the fields we pass to
Run/GetRequiredPlugins/etc today. `Pwd` and `Program` were not very
descriptive and had pretty non-obvious documentation:
```
string pwd = 3; // the program's working directory.
string program = 4; // the path to the program to execute.
```
`pwd` will remain, although probably rename it to `working_directory` at
some point, because while today we always start programs up with the
working directory equal to the program directory that definitely is
going to change in the future (at least for MLCs and substack programs).
But the name `pwd` doesn't make it clear that this was intended to be
the working directory _and_ the directory which contains the program.
`program` was in fact nearly always ".", and if it wasn't that it was
just a filename. The engine never sent a path for `program` (although we
did have some unit tests to check how that worked for the nodejs and
python hosts).
These are now replaced by a new structure with (I think) more clearly
named and documented fields (see ProgramInfo in langauge.proto).
The engine still sends the old data for now, we need to update
dotnet/yaml/java before we break the old interface and give Virtus Labs
a chance to update [besom](https://github.com/VirtusLab/besom).
## Checklist
- [x] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-12-10 17:30:51 +00:00
|
|
|
Info: &pulumirpc.ProgramInfo{
|
|
|
|
RootDirectory: dir,
|
|
|
|
ProgramDirectory: dir,
|
|
|
|
EntryPoint: ".",
|
|
|
|
},
|
2022-01-07 04:14:09 +00:00
|
|
|
})
|
2023-01-23 19:53:29 +00:00
|
|
|
require.NoError(t, err)
|
2022-01-07 04:14:09 +00:00
|
|
|
|
|
|
|
actual := make(map[string]string)
|
|
|
|
for _, plugin := range resp.GetPlugins() {
|
|
|
|
actual[plugin.Name] = plugin.Version
|
|
|
|
}
|
|
|
|
assert.Equal(t, map[string]string{
|
|
|
|
"foo": "v1.2.3",
|
|
|
|
"bar": "v4.5.6",
|
|
|
|
}, actual)
|
|
|
|
}
|
2023-01-23 17:18:14 +00:00
|
|
|
|
|
|
|
func TestGetRequiredPluginsSymlinkCycles(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
dir := t.TempDir()
|
|
|
|
|
|
|
|
files := []struct {
|
|
|
|
path string
|
|
|
|
content string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
filepath.Join(dir, "node_modules", "@pulumi", "foo", "package.json"),
|
|
|
|
`{ "name": "@pulumi/foo", "version": "1.2.3", "pulumi": { "resource": true } }`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
filepath.Join(dir, "node_modules", "@pulumi", "bar", "package.json"),
|
|
|
|
`{ "name": "@pulumi/bar", "version": "4.5.6", "pulumi": { "resource": true } }`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
filepath.Join(dir, "node_modules", "@pulumi", "baz", "package.json"),
|
|
|
|
`{ "name": "@pulumi/baz", "version": "4.5.6", "pulumi": { "resource": false } }`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
filepath.Join(dir, "node_modules", "malformed", "tests", "malformed_test", "package.json"),
|
|
|
|
`{`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, file := range files {
|
2023-03-03 16:36:39 +00:00
|
|
|
err := os.MkdirAll(filepath.Dir(file.path), 0o755)
|
2023-01-23 19:27:52 +00:00
|
|
|
require.NoError(t, err)
|
2023-03-03 16:36:39 +00:00
|
|
|
err = os.WriteFile(file.path, []byte(file.content), 0o600)
|
2023-01-23 19:27:52 +00:00
|
|
|
require.NoError(t, err)
|
2023-01-23 17:18:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add a symlink cycle in
|
|
|
|
err := os.Symlink(filepath.Join(dir, "node_modules"), filepath.Join(dir, "node_modules", "@node_modules"))
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
host := &nodeLanguageHost{}
|
2023-09-25 12:25:26 +00:00
|
|
|
resp, err := host.GetRequiredPlugins(context.Background(), &pulumirpc.GetRequiredPluginsRequest{
|
2023-01-23 17:18:14 +00:00
|
|
|
Program: dir,
|
Pass root and main info to language host methods (#14654)
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
This is two changes rolled together in a way.
Firstly passing some of the data that we pass on language runtime
startup to also pass it to Run/GetRequiredPlugins/etc. This is needed
for matrix testing, as we only get to start the language runtime up once
for that but want to execute multiple programs with it.
I feel it's also a little more consistent as we use the language
runtimes in other contexts (codegen) where there isn't really a root
directory, and aren't any options (and if we did do options the options
for codegen are not going to be the same as for execution). It also
means we can reuse a language host for shimless and substack programs,
as before they heavily relied on their current working directory to
calculate paths, and obviosly could only take one set of options at
startup. Imagine a shimless python package + a python root program, that
would have needed two startups of the python language host to deal with,
this unblocks it so we can make the engine smarter and only use one.
Secondly renaming some of the fields we pass to
Run/GetRequiredPlugins/etc today. `Pwd` and `Program` were not very
descriptive and had pretty non-obvious documentation:
```
string pwd = 3; // the program's working directory.
string program = 4; // the path to the program to execute.
```
`pwd` will remain, although probably rename it to `working_directory` at
some point, because while today we always start programs up with the
working directory equal to the program directory that definitely is
going to change in the future (at least for MLCs and substack programs).
But the name `pwd` doesn't make it clear that this was intended to be
the working directory _and_ the directory which contains the program.
`program` was in fact nearly always ".", and if it wasn't that it was
just a filename. The engine never sent a path for `program` (although we
did have some unit tests to check how that worked for the nodejs and
python hosts).
These are now replaced by a new structure with (I think) more clearly
named and documented fields (see ProgramInfo in langauge.proto).
The engine still sends the old data for now, we need to update
dotnet/yaml/java before we break the old interface and give Virtus Labs
a chance to update [besom](https://github.com/VirtusLab/besom).
## Checklist
- [x] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-12-10 17:30:51 +00:00
|
|
|
Info: &pulumirpc.ProgramInfo{
|
|
|
|
RootDirectory: dir,
|
|
|
|
ProgramDirectory: dir,
|
|
|
|
EntryPoint: ".",
|
|
|
|
},
|
2023-01-23 17:18:14 +00:00
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
actual := make(map[string]string)
|
|
|
|
for _, plugin := range resp.GetPlugins() {
|
|
|
|
actual[plugin.Name] = plugin.Version
|
|
|
|
}
|
|
|
|
assert.Equal(t, map[string]string{
|
|
|
|
"foo": "v1.2.3",
|
|
|
|
"bar": "v4.5.6",
|
|
|
|
}, actual)
|
|
|
|
}
|
2023-01-23 22:02:26 +00:00
|
|
|
|
|
|
|
func TestGetRequiredPluginsSymlinkCycles2(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
dir := filepath.Join(t.TempDir(), "testdir")
|
2023-03-03 16:36:39 +00:00
|
|
|
err := os.Mkdir(dir, 0o755)
|
2023-01-23 22:02:26 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
files := []struct {
|
|
|
|
path string
|
|
|
|
content string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
filepath.Join(dir, "node_modules", "@pulumi", "foo", "package.json"),
|
|
|
|
`{ "name": "@pulumi/foo", "version": "1.2.3", "pulumi": { "resource": true } }`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
filepath.Join(dir, "node_modules", "@pulumi", "bar", "package.json"),
|
|
|
|
`{ "name": "@pulumi/bar", "version": "4.5.6", "pulumi": { "resource": true } }`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
filepath.Join(dir, "node_modules", "@pulumi", "baz", "package.json"),
|
|
|
|
`{ "name": "@pulumi/baz", "version": "4.5.6", "pulumi": { "resource": false } }`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
filepath.Join(dir, "node_modules", "malformed", "tests", "malformed_test", "package.json"),
|
|
|
|
`{`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, file := range files {
|
2023-03-03 16:36:39 +00:00
|
|
|
err := os.MkdirAll(filepath.Dir(file.path), 0o755)
|
2023-01-23 22:02:26 +00:00
|
|
|
require.NoError(t, err)
|
2023-03-03 16:36:39 +00:00
|
|
|
err = os.WriteFile(file.path, []byte(file.content), 0o600)
|
2023-01-23 22:02:26 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add a symlink cycle in
|
|
|
|
err = os.Symlink(filepath.Join("..", ".."), filepath.Join(dir, "node_modules", "@node_modules"))
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
host := &nodeLanguageHost{}
|
2023-09-25 12:25:26 +00:00
|
|
|
resp, err := host.GetRequiredPlugins(context.Background(), &pulumirpc.GetRequiredPluginsRequest{
|
2023-01-23 22:02:26 +00:00
|
|
|
Program: dir,
|
Pass root and main info to language host methods (#14654)
<!---
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->
# Description
<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->
This is two changes rolled together in a way.
Firstly passing some of the data that we pass on language runtime
startup to also pass it to Run/GetRequiredPlugins/etc. This is needed
for matrix testing, as we only get to start the language runtime up once
for that but want to execute multiple programs with it.
I feel it's also a little more consistent as we use the language
runtimes in other contexts (codegen) where there isn't really a root
directory, and aren't any options (and if we did do options the options
for codegen are not going to be the same as for execution). It also
means we can reuse a language host for shimless and substack programs,
as before they heavily relied on their current working directory to
calculate paths, and obviosly could only take one set of options at
startup. Imagine a shimless python package + a python root program, that
would have needed two startups of the python language host to deal with,
this unblocks it so we can make the engine smarter and only use one.
Secondly renaming some of the fields we pass to
Run/GetRequiredPlugins/etc today. `Pwd` and `Program` were not very
descriptive and had pretty non-obvious documentation:
```
string pwd = 3; // the program's working directory.
string program = 4; // the path to the program to execute.
```
`pwd` will remain, although probably rename it to `working_directory` at
some point, because while today we always start programs up with the
working directory equal to the program directory that definitely is
going to change in the future (at least for MLCs and substack programs).
But the name `pwd` doesn't make it clear that this was intended to be
the working directory _and_ the directory which contains the program.
`program` was in fact nearly always ".", and if it wasn't that it was
just a filename. The engine never sent a path for `program` (although we
did have some unit tests to check how that worked for the nodejs and
python hosts).
These are now replaced by a new structure with (I think) more clearly
named and documented fields (see ProgramInfo in langauge.proto).
The engine still sends the old data for now, we need to update
dotnet/yaml/java before we break the old interface and give Virtus Labs
a chance to update [besom](https://github.com/VirtusLab/besom).
## Checklist
- [x] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [ ] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @Pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
2023-12-10 17:30:51 +00:00
|
|
|
Info: &pulumirpc.ProgramInfo{
|
|
|
|
RootDirectory: dir,
|
|
|
|
ProgramDirectory: dir,
|
|
|
|
EntryPoint: ".",
|
|
|
|
},
|
2023-01-23 22:02:26 +00:00
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
actual := make(map[string]string)
|
|
|
|
for _, plugin := range resp.GetPlugins() {
|
|
|
|
actual[plugin.Name] = plugin.Version
|
|
|
|
}
|
|
|
|
assert.Equal(t, map[string]string{
|
|
|
|
"foo": "v1.2.3",
|
|
|
|
"bar": "v4.5.6",
|
|
|
|
}, actual)
|
|
|
|
}
|
2024-06-21 11:35:06 +00:00
|
|
|
|
2024-07-11 16:01:31 +00:00
|
|
|
func TestGetRequiredPluginsNestedPolicyPack(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
dir := filepath.Join(t.TempDir(), "testdir")
|
|
|
|
err := os.Mkdir(dir, 0o755)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
files := []struct {
|
|
|
|
path string
|
|
|
|
content string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
filepath.Join(dir, "node_modules", "@pulumi", "foo", "package.json"),
|
|
|
|
`{ "name": "@pulumi/foo", "version": "1.2.3", "pulumi": { "resource": true } }`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
filepath.Join(dir, "node_modules", "@pulumi", "bar", "package.json"),
|
|
|
|
`{ "name": "@pulumi/bar", "version": "4.5.6", "pulumi": { "resource": true } }`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
filepath.Join(dir, "policy", "PulumiPolicy.yaml"),
|
|
|
|
`name: my-policy`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
filepath.Join(dir, "policy", "node_modules", "@pulumi", "baz", "package.json"),
|
|
|
|
`{ "name": "@pulumi/baz", "version": "7.8.9", "pulumi": { "resource": true } }`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, file := range files {
|
|
|
|
err := os.MkdirAll(filepath.Dir(file.path), 0o755)
|
|
|
|
require.NoError(t, err)
|
|
|
|
err = os.WriteFile(file.path, []byte(file.content), 0o600)
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
host := &nodeLanguageHost{}
|
|
|
|
resp, err := host.GetRequiredPlugins(context.Background(), &pulumirpc.GetRequiredPluginsRequest{
|
|
|
|
Program: dir,
|
|
|
|
Info: &pulumirpc.ProgramInfo{
|
|
|
|
RootDirectory: dir,
|
|
|
|
ProgramDirectory: dir,
|
|
|
|
EntryPoint: ".",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
actual := make(map[string]string)
|
|
|
|
for _, plugin := range resp.GetPlugins() {
|
|
|
|
actual[plugin.Name] = plugin.Version
|
|
|
|
}
|
|
|
|
assert.Equal(t, map[string]string{
|
|
|
|
"foo": "v1.2.3",
|
|
|
|
"bar": "v4.5.6",
|
|
|
|
// baz: v7.8.9 is not included because it is in a nested policy pack
|
|
|
|
}, actual)
|
|
|
|
}
|
|
|
|
|
2024-06-21 11:35:06 +00:00
|
|
|
func TestParseOptions(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
opts, err := parseOptions(nil)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, npm.AutoPackageManager, opts.packagemanager)
|
|
|
|
|
|
|
|
_, err = parseOptions(map[string]interface{}{
|
|
|
|
"typescript": 123,
|
|
|
|
})
|
|
|
|
require.ErrorContains(t, err, "typescript option must be a boolean")
|
|
|
|
|
|
|
|
_, err = parseOptions(map[string]interface{}{
|
|
|
|
"packagemanager": "poetry",
|
|
|
|
})
|
|
|
|
require.ErrorContains(t, err, "packagemanager option must be one of")
|
|
|
|
|
|
|
|
for _, tt := range []struct {
|
|
|
|
input string
|
|
|
|
expected npm.PackageManagerType
|
|
|
|
}{
|
|
|
|
{"auto", npm.AutoPackageManager},
|
|
|
|
{"npm", npm.NpmPackageManager},
|
|
|
|
{"yarn", npm.YarnPackageManager},
|
|
|
|
{"pnpm", npm.PnpmPackageManager},
|
|
|
|
} {
|
|
|
|
opts, err = parseOptions(map[string]interface{}{
|
|
|
|
"packagemanager": tt.input,
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, tt.expected, opts.packagemanager)
|
|
|
|
}
|
|
|
|
}
|
2024-07-23 16:10:43 +00:00
|
|
|
|
2024-07-25 12:14:02 +00:00
|
|
|
// Nodejs sometimes sets stdout/stderr to non-blocking mode. When a nodejs subprocess is directly
|
|
|
|
// handed the go process's stdout/stderr file descriptors, nodejs's non-blocking configuration goes
|
|
|
|
// unnoticed by go, and a write from go can result in an error `write /dev/stdout: resource
|
|
|
|
// temporarily unavailable`. See runWithOutput for more details.
|
2024-07-23 16:10:43 +00:00
|
|
|
func TestNonblockingStdout(t *testing.T) {
|
|
|
|
// Regression test for https://github.com/pulumi/pulumi/issues/16503
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
script := `import os, time
|
|
|
|
os.set_blocking(1, False) # set stdout to non-blocking
|
|
|
|
time.sleep(3)
|
|
|
|
`
|
2024-07-25 12:14:02 +00:00
|
|
|
|
|
|
|
// Create a named pipe to use as stdout
|
|
|
|
tmp := os.TempDir()
|
|
|
|
p := filepath.Join(tmp, "fake-stdout")
|
|
|
|
err := syscall.Mkfifo(p, 0o644)
|
|
|
|
defer os.Remove(p)
|
|
|
|
require.NoError(t, err)
|
|
|
|
// Open fd without O_NONBLOCK, ensuring that os.NewFile does not return a pollable file.
|
|
|
|
// When our python script changes the file to non-blocking, Go does not notice and continues to
|
|
|
|
// expect the file to be blocking, and we can trigger the bug.
|
|
|
|
fd, err := syscall.Open(p, syscall.O_CREAT|syscall.O_RDWR, 0o644)
|
|
|
|
require.NoError(t, err)
|
|
|
|
fakeStdout := os.NewFile(uintptr(fd), p)
|
|
|
|
defer fakeStdout.Close()
|
|
|
|
require.NotNil(t, fakeStdout)
|
|
|
|
|
2024-07-23 16:10:43 +00:00
|
|
|
cmd := exec.Command("python3", "-c", script)
|
|
|
|
|
2024-07-25 12:14:02 +00:00
|
|
|
var done bool
|
2024-07-23 16:10:43 +00:00
|
|
|
go func() {
|
|
|
|
time.Sleep(2 * time.Second)
|
2024-07-25 12:14:02 +00:00
|
|
|
for !done {
|
2024-07-23 16:10:43 +00:00
|
|
|
s := "....................\n"
|
2024-07-25 12:14:02 +00:00
|
|
|
n, err := fakeStdout.Write([]byte(s))
|
2024-07-23 16:10:43 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, n, len(s))
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2024-07-25 12:14:02 +00:00
|
|
|
require.NoError(t, runWithOutput(cmd, fakeStdout, os.Stderr))
|
|
|
|
done = true
|
2024-07-23 16:10:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type slowWriter struct {
|
|
|
|
nWrites *int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s slowWriter) Write(b []byte) (int, error) {
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
2024-07-25 12:14:02 +00:00
|
|
|
l := len(b)
|
|
|
|
*s.nWrites += l
|
|
|
|
return l, nil
|
2024-07-23 16:10:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestRunWithOutputDoesNotMissData(t *testing.T) {
|
|
|
|
// This test ensures that runWithOutput writes all the data from the command and does not miss
|
|
|
|
// any data that might be buffered when the command exits.
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
// Write `o` to stdout 100 times at 10 ms interval, followed by `x\n`
|
|
|
|
// Write `e` to stderr 100 times at 10 ms interval, followed by `x\n`
|
|
|
|
script := `let i = 0;
|
|
|
|
let interval = setInterval(() => {
|
|
|
|
process.stdout.write("o");
|
|
|
|
process.stderr.write("e");
|
|
|
|
i++;
|
|
|
|
if (i == 100) {
|
|
|
|
process.stdout.write("x\n");
|
|
|
|
process.stderr.write("x\n");
|
|
|
|
clearInterval(interval);
|
|
|
|
}
|
|
|
|
}, 10)
|
|
|
|
`
|
2024-07-25 12:14:02 +00:00
|
|
|
|
2024-07-23 16:10:43 +00:00
|
|
|
cmd := exec.Command("node", "-e", script)
|
2024-07-25 12:14:02 +00:00
|
|
|
stdout := slowWriter{nWrites: new(int)}
|
|
|
|
stderr := slowWriter{nWrites: new(int)}
|
2024-07-23 16:10:43 +00:00
|
|
|
|
|
|
|
require.NoError(t, runWithOutput(cmd, stdout, stderr))
|
|
|
|
|
|
|
|
require.Equal(t, 100+2 /* "x\n" */, *stdout.nWrites)
|
|
|
|
require.Equal(t, 100+2 /* "x\n" */, *stderr.nWrites)
|
|
|
|
}
|
2024-08-28 09:46:54 +00:00
|
|
|
|
|
|
|
//nolint:paralleltest // mutates environment variables
|
|
|
|
func TestUseFnm(t *testing.T) {
|
|
|
|
// Set $PATH to to $TMPDIR/bin so that no `fnm` executable can be found.
|
|
|
|
tmpDir := t.TempDir()
|
|
|
|
require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "bin"), 0o755))
|
|
|
|
t.Setenv("PATH", filepath.Join(tmpDir, "bin"))
|
|
|
|
|
|
|
|
_, err := useFnm(tmpDir)
|
|
|
|
require.ErrorIs(t, err, errFnmNotFound)
|
|
|
|
|
|
|
|
// Add a fake fnm binary to $TMPDIR/bin for the rest of the tests.
|
|
|
|
//nolint:gosec // we want this file to be executable
|
|
|
|
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "bin", "fnm"), []byte("#!/bin/sh\nexit 0;\n"), 0o700))
|
|
|
|
|
|
|
|
t.Run("no version files", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
tmpDir := t.TempDir()
|
|
|
|
_, err := useFnm(tmpDir)
|
|
|
|
require.ErrorIs(t, err, errVersionFileNotFound)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run(".node-version in cwd", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
tmpDir := t.TempDir()
|
|
|
|
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, ".node-version"), []byte("22.7.3"), 0o600))
|
|
|
|
version, err := useFnm(tmpDir)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "22.7.3", version)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run(".nvmrc in cwd", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
tmpDir := t.TempDir()
|
|
|
|
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, ".nvmrc"), []byte("20.1.1"), 0o600))
|
|
|
|
version, err := useFnm(tmpDir)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "20.1.1", version)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run(".nvmrc & .node-version in cwd", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
tmpDir := t.TempDir()
|
|
|
|
// .nvmrc should take precedence
|
|
|
|
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, ".nvmrc"), []byte("20.1.1"), 0o600))
|
|
|
|
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, ".node-version"), []byte("22.7.3"), 0o600))
|
|
|
|
version, err := useFnm(tmpDir)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "20.1.1", version)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run(".node-version in parent folder", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
tmpDir := t.TempDir()
|
|
|
|
tmpDirNested := filepath.Join(tmpDir, "nested")
|
|
|
|
require.NoError(t, os.MkdirAll(tmpDirNested, 0o700))
|
|
|
|
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, ".node-version"), []byte("20.1.1"), 0o600))
|
|
|
|
version, err := useFnm(tmpDirNested)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "20.1.1", version)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run(".node-version in cwd & parent", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
tmpDir := t.TempDir()
|
|
|
|
tmpDirNested := filepath.Join(tmpDir, "nested")
|
|
|
|
require.NoError(t, os.MkdirAll(tmpDirNested, 0o700))
|
|
|
|
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, ".node-version"), []byte("20.1.1"), 0o600))
|
|
|
|
// This should take precedence over the parent folder's .node-version
|
|
|
|
require.NoError(t, os.WriteFile(filepath.Join(tmpDirNested, ".node-version"), []byte("20.7.3"), 0o600))
|
|
|
|
version, err := useFnm(tmpDirNested)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "20.7.3", version)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
//nolint:paralleltest // mutates environment variables
|
|
|
|
func TestNodeInstall(t *testing.T) {
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
t.Skip()
|
|
|
|
}
|
|
|
|
tmpDir := t.TempDir()
|
|
|
|
t.Setenv("PATH", filepath.Join(tmpDir, "bin"))
|
|
|
|
|
|
|
|
fmt.Println(os.Getenv("PATH"))
|
|
|
|
|
|
|
|
// There's no fnm executable in PATH, installNodeVersion is a no-op
|
|
|
|
stdout := &bytes.Buffer{}
|
|
|
|
err := installNodeVersion(tmpDir, stdout)
|
|
|
|
require.ErrorIs(t, err, errFnmNotFound)
|
|
|
|
|
|
|
|
// Add a mock fnm executable to $tmp/bin. For each execution, the mock fnm executable will
|
|
|
|
// append a line with its arguments to a file. We read back the file to verify that it was
|
|
|
|
// called with the expected arguments.
|
|
|
|
require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "bin"), 0o755))
|
|
|
|
outPath := filepath.Join(tmpDir, "out.txt")
|
|
|
|
script := fmt.Sprintf("#!/bin/sh\necho $@ >> %s\n", outPath)
|
|
|
|
//nolint:gosec // we want this file to be executable
|
|
|
|
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "bin", "fnm"), []byte(script), 0o700))
|
|
|
|
|
|
|
|
// There's no .node-version or .nvmrc file, so the binary should not be called.
|
|
|
|
// We expect the file written by our mock fnm executable to not exist.
|
|
|
|
stdout = &bytes.Buffer{}
|
|
|
|
err = installNodeVersion(tmpDir, stdout)
|
|
|
|
require.Error(t, err, errVersionFileNotFound)
|
|
|
|
|
|
|
|
// Create a .node-version file
|
|
|
|
// The mock fnm executable should be called with a command to install the requested version,
|
|
|
|
// and a command to set the default version to this version.
|
|
|
|
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, ".node-version"), []byte("20.1.2"), 0o600))
|
|
|
|
stdout = &bytes.Buffer{}
|
|
|
|
err = installNodeVersion(tmpDir, stdout)
|
|
|
|
require.NoError(t, err)
|
|
|
|
b, err := os.ReadFile(outPath)
|
|
|
|
require.NoError(t, err)
|
|
|
|
commands := strings.Split(strings.TrimSpace(string(b)), "\n")
|
|
|
|
require.Equal(t, "install 20.1.2 --progress never", commands[0])
|
|
|
|
require.Equal(t, "alias 20.1.2 default", commands[1])
|
|
|
|
}
|