Skip to content

Commit

Permalink
Merge #11611
Browse files Browse the repository at this point in the history
11611: Improve ProgramTest errors and path handling for Go programs r=AaronFriel a=AaronFriel

This improves and expands on ProgramTest behavior for initializing Go programs, enabling better relative path handling and adding some safeguards. These are documented in `TestGoModEdits`.



Co-authored-by: Aaron Friel <mayreply@aaronfriel.com>
  • Loading branch information
bors[bot] and AaronFriel committed Dec 13, 2022
2 parents feac805 + 6642fb5 commit 898bc4f
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 9 deletions.
1 change: 1 addition & 0 deletions pkg/go.mod
Expand Up @@ -77,6 +77,7 @@ require (
github.com/rivo/uniseg v0.2.0
github.com/segmentio/encoding v0.3.5
github.com/shirou/gopsutil/v3 v3.22.3
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
)

Expand Down
69 changes: 61 additions & 8 deletions pkg/testing/integration/program.go
Expand Up @@ -36,6 +36,7 @@ import (
"time"

multierror "github.com/hashicorp/go-multierror"
"golang.org/x/mod/modfile"
"gopkg.in/yaml.v3"

"github.com/pulumi/pulumi/pkg/v3/backend/filestate"
Expand Down Expand Up @@ -2242,14 +2243,10 @@ func (pt *ProgramTester) prepareGoProject(projinfo *engine.Projinfo) error {
}

// link local dependencies
for _, pkg := range pt.opts.Dependencies {
var editStr string
if strings.ContainsRune(pkg, '=') {
// Use a literal replacement path.
editStr = pkg
} else {
dep := getRewritePath(pkg, gopath, depRoot)
editStr = fmt.Sprintf("%s=%s", pkg, dep)
for _, dep := range pt.opts.Dependencies {
editStr, err := getEditStr(dep, gopath, depRoot)
if err != nil {
return fmt.Errorf("error generating go mod replacement for dep %q: %w", dep, err)
}
err = pt.runCommand("go-mod-edit", []string{goBin, "mod", "edit", "-replace", editStr}, cwd)
if err != nil {
Expand Down Expand Up @@ -2282,6 +2279,62 @@ func (pt *ProgramTester) prepareGoProject(projinfo *engine.Projinfo) error {
return nil
}

func getEditStr(dep string, gopath string, depRoot string) (string, error) {
checkModName := true
var err error
var modName string
var modDir string
if strings.ContainsRune(dep, '=') {
parts := strings.Split(dep, "=")
modName = parts[0]
modDir = parts[1]
} else if !modfile.IsDirectoryPath(dep) {
modName = dep
modDir = getRewritePath(dep, gopath, depRoot)
} else {
modDir = dep
modName, err = getModName(modDir)
if err != nil {
return "", err
}
// We've read the package name from the go.mod file, skip redundant check below.
checkModName = false
}

modDir, err = filepath.Abs(modDir)
if err != nil {
return "", err
}

if checkModName {
actualModName, err := getModName(modDir)
if err != nil {
return "", fmt.Errorf("no go.mod at directory, set the path to the module explicitly or place "+
"the dependency in the path specified by PULUMI_GO_DEP_ROOT or the default GOPATH: %w", err)
}
if actualModName != modName {
return "", fmt.Errorf("found module %s, expected %s", actualModName, modName)
}
}

editStr := fmt.Sprintf("%s=%s", modName, modDir)
return editStr, nil
}

func getModName(dir string) (string, error) {
pkgModPath := filepath.Join(dir, "go.mod")
pkgModData, err := os.ReadFile(pkgModPath)
if err != nil {
return "", fmt.Errorf("error reading go.mod at %s: %w", dir, err)
}
pkgMod, err := modfile.Parse(pkgModPath, pkgModData, nil)
if err != nil {
return "", fmt.Errorf("error parsing go.mod at %s: %w", dir, err)
}

return pkgMod.Module.Mod.Path, nil
}

// prepareDotNetProject runs setup necessary to get a .NET project ready for `pulumi` commands.
func (pt *ProgramTester) prepareDotNetProject(projinfo *engine.Projinfo) error {
dotNetBin, err := pt.getDotNetBin()
Expand Down
84 changes: 84 additions & 0 deletions pkg/testing/integration/program_test.go
Expand Up @@ -22,6 +22,7 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// Test that RunCommand writes the command's output to a log file.
Expand Down Expand Up @@ -91,3 +92,86 @@ func TestDepRootCalc(t *testing.T) {
dep = getRewritePath("github.com/pulumi/pulumi-auth0/sdk", "gopath", "/my-go-src")
assert.Equal(t, "/my-go-src/pulumi-auth0/sdk", filepath.ToSlash(dep))
}

func TestGoModEdits(t *testing.T) {
t.Parallel()

depRoot := os.Getenv("PULUMI_GO_DEP_ROOT")
gopath, err := GoPath()
require.NoError(t, err)

cwd, err := os.Getwd()
require.NoError(t, err)

// Were we to commit this go.mod file, `make tidy` would fail, and we should keep the complexity
// of tests constrained to the test itself.

// The dir must be a relative path as well, so we make it relative to cwd (which is absolute).
badModDir := t.TempDir()
badModDir, err = filepath.Rel(cwd, badModDir)
require.NoError(t, err)
badModFile := filepath.Join(badModDir, "go.mod")
err = os.WriteFile(badModFile, []byte(`
# invalid go.mod
`), 0600)
require.NoError(t, err)

tests := []struct {
name string
dep string
expectedValue string
expectedError string
}{
{
name: "valid-path",
dep: "../../../sdk",
expectedValue: "github.com/pulumi/pulumi/sdk/v3=" + filepath.Join(cwd, "../../../sdk"),
},
{
name: "invalid-path-non-existent",
dep: "../../../.tmp.non-existent-dir",
expectedError: "open ../../../.tmp.non-existent-dir/go.mod: no such file or directory",
},
{
name: "invalid-path-bad-go-mod",
dep: badModDir,
expectedError: "error parsing go.mod",
},
{
name: "valid-module-name",
dep: "github.com/pulumi/pulumi/sdk/v3",
expectedValue: "github.com/pulumi/pulumi/sdk/v3=" + filepath.Join(cwd, "../../../sdk"),
},
{
name: "invalid-module-name",
dep: "github.com/pulumi/pulumi/sdk/v2", // v2 not v3
expectedError: "found module github.com/pulumi/pulumi/sdk/v3, expected github.com/pulumi/pulumi/sdk/v2",
},
{
name: "valid-rel-path",
dep: "github.com/pulumi/pulumi/sdk/v3=../../../sdk",
expectedValue: "github.com/pulumi/pulumi/sdk/v3=" + filepath.Join(cwd, "../../../sdk"),
},
{
name: "invalid-rel-path",
dep: "github.com/pulumi/pulumi/sdk/v2=../../../sdk",
expectedError: "found module github.com/pulumi/pulumi/sdk/v3, expected github.com/pulumi/pulumi/sdk/v2",
},
}

for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()

editStr, err := getEditStr(test.dep, gopath, depRoot)

if test.expectedError != "" {
assert.ErrorContains(t, err, test.expectedError)
} else {
assert.NoError(t, err)
assert.Equal(t, test.expectedValue, editStr)
}
})
}
}
1 change: 1 addition & 0 deletions tests/go.mod
Expand Up @@ -159,6 +159,7 @@ require (
gocloud.dev v0.27.0 // indirect
gocloud.dev/secrets/hashivault v0.24.0 // indirect
golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b // indirect
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c // indirect
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
Expand Down
1 change: 1 addition & 0 deletions tests/go.sum
Expand Up @@ -1818,6 +1818,7 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/aliases/aliases_go_test.go
Expand Up @@ -28,7 +28,7 @@ func TestGoAliases(t *testing.T) {
integration.ProgramTest(t, &integration.ProgramTestOptions{
Dir: filepath.Join(d, "step1"),
Dependencies: []string{
"github.com/pulumi/pulumi/sdk/v3",
"github.com/pulumi/pulumi/sdk/v3=../../../sdk",
},
Quick: true,
EditDirs: []integration.EditDir{
Expand Down

0 comments on commit 898bc4f

Please sign in to comment.