-
Notifications
You must be signed in to change notification settings - Fork 131
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce list module providers command (#712)
* add new module providers workspace command * add state store to sevice and session mocking * add integration test for module providers command * Review feedback * add docs link * check meta data state * Apply suggestions from code review Co-authored-by: Radek Simko <radek.simko@gmail.com> Co-authored-by: Radek Simko <radek.simko@gmail.com>
- Loading branch information
1 parent
57f6f05
commit 1620784
Showing
5 changed files
with
268 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package command | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/creachadair/jrpc2/code" | ||
lsctx "github.com/hashicorp/terraform-ls/internal/context" | ||
"github.com/hashicorp/terraform-ls/internal/langserver/cmd" | ||
op "github.com/hashicorp/terraform-ls/internal/terraform/module/operation" | ||
"github.com/hashicorp/terraform-ls/internal/uri" | ||
tfaddr "github.com/hashicorp/terraform-registry-address" | ||
) | ||
|
||
const moduleProvidersVersion = 0 | ||
|
||
type moduleProvidersResponse struct { | ||
FormatVersion int `json:"v"` | ||
ProviderRequirements map[string]providerRequirement `json:"provider_requirements"` | ||
InstalledProviders map[string]string `json:"installed_providers"` | ||
} | ||
|
||
type providerRequirement struct { | ||
DisplayName string `json:"display_name"` | ||
VersionConstraint string `json:"version_constraint,omitempty"` | ||
DocsLink string `json:"docs_link,omitempty"` | ||
} | ||
|
||
func ModuleProvidersHandler(ctx context.Context, args cmd.CommandArgs) (interface{}, error) { | ||
response := moduleProvidersResponse{ | ||
FormatVersion: moduleProvidersVersion, | ||
ProviderRequirements: make(map[string]providerRequirement), | ||
InstalledProviders: make(map[string]string), | ||
} | ||
|
||
modUri, ok := args.GetString("uri") | ||
if !ok || modUri == "" { | ||
return response, fmt.Errorf("%w: expected module uri argument to be set", code.InvalidParams.Err()) | ||
} | ||
|
||
if !uri.IsURIValid(modUri) { | ||
return response, fmt.Errorf("URI %q is not valid", modUri) | ||
} | ||
|
||
modPath, err := uri.PathFromURI(modUri) | ||
if err != nil { | ||
return response, err | ||
} | ||
|
||
mm, err := lsctx.ModuleFinder(ctx) | ||
if err != nil { | ||
return response, err | ||
} | ||
|
||
mod, _ := mm.ModuleByPath(modPath) | ||
if mod == nil { | ||
return response, nil | ||
} | ||
|
||
if mod.MetaState == op.OpStateUnknown || mod.MetaErr != nil { | ||
return response, nil | ||
} | ||
|
||
for provider, version := range mod.Meta.ProviderRequirements { | ||
response.ProviderRequirements[provider.String()] = providerRequirement{ | ||
DisplayName: provider.ForDisplay(), | ||
VersionConstraint: version.String(), | ||
DocsLink: getProviderDocumentationLink(provider), | ||
} | ||
} | ||
|
||
for provider, version := range mod.InstalledProviders { | ||
response.InstalledProviders[provider.String()] = version.String() | ||
} | ||
|
||
return response, nil | ||
} | ||
|
||
func getProviderDocumentationLink(provider tfaddr.Provider) string { | ||
if provider.IsLegacy() || provider.IsBuiltIn() || provider.Hostname != "registry.terraform.io" { | ||
return "" | ||
} | ||
|
||
return fmt.Sprintf(`https://registry.terraform.io/providers/%s/latest`, provider.ForDisplay()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
162 changes: 162 additions & 0 deletions
162
internal/langserver/handlers/execute_command_module_providers_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
package handlers | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/creachadair/jrpc2/code" | ||
"github.com/hashicorp/go-version" | ||
"github.com/hashicorp/terraform-ls/internal/langserver" | ||
"github.com/hashicorp/terraform-ls/internal/langserver/cmd" | ||
"github.com/hashicorp/terraform-ls/internal/state" | ||
"github.com/hashicorp/terraform-ls/internal/terraform/exec" | ||
"github.com/hashicorp/terraform-ls/internal/uri" | ||
tfaddr "github.com/hashicorp/terraform-registry-address" | ||
tfmod "github.com/hashicorp/terraform-schema/module" | ||
"github.com/stretchr/testify/mock" | ||
) | ||
|
||
func TestLangServer_workspaceExecuteCommand_moduleProviders_argumentError(t *testing.T) { | ||
rootDir := t.TempDir() | ||
rootUri := uri.FromPath(rootDir) | ||
|
||
ls := langserver.NewLangServerMock(t, NewMockSession(&MockSessionInput{ | ||
TerraformCalls: &exec.TerraformMockCalls{ | ||
PerWorkDir: map[string][]*mock.Call{ | ||
rootDir: validTfMockCalls(), | ||
}, | ||
}, | ||
})) | ||
stop := ls.Start(t) | ||
defer stop() | ||
|
||
ls.Call(t, &langserver.CallRequest{ | ||
Method: "initialize", | ||
ReqParams: fmt.Sprintf(`{ | ||
"capabilities": {}, | ||
"rootUri": %q, | ||
"processId": 12345 | ||
}`, rootUri)}) | ||
ls.Notify(t, &langserver.CallRequest{ | ||
Method: "initialized", | ||
ReqParams: "{}", | ||
}) | ||
ls.Call(t, &langserver.CallRequest{ | ||
Method: "textDocument/didOpen", | ||
ReqParams: fmt.Sprintf(`{ | ||
"textDocument": { | ||
"version": 0, | ||
"languageId": "terraform", | ||
"text": "provider \"github\" {}", | ||
"uri": %q | ||
} | ||
}`, fmt.Sprintf("%s/main.tf", rootUri))}) | ||
|
||
ls.CallAndExpectError(t, &langserver.CallRequest{ | ||
Method: "workspace/executeCommand", | ||
ReqParams: fmt.Sprintf(`{ | ||
"command": %q | ||
}`, cmd.Name("module.providers"))}, code.InvalidParams.Err()) | ||
} | ||
|
||
func TestLangServer_workspaceExecuteCommand_moduleProviders_basic(t *testing.T) { | ||
modDir := t.TempDir() | ||
modUri := uri.FromPath(modDir) | ||
|
||
s, err := state.NewStateStore() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
err = s.Modules.Add(modDir) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
metadata := &tfmod.Meta{ | ||
Path: modDir, | ||
CoreRequirements: testConstraint(t, "~> 0.15"), | ||
ProviderRequirements: map[tfaddr.Provider]version.Constraints{ | ||
tfaddr.NewDefaultProvider("aws"): testConstraint(t, "1.2.3"), | ||
tfaddr.NewDefaultProvider("google"): testConstraint(t, ">= 2.0.0"), | ||
}, | ||
ProviderReferences: map[tfmod.ProviderRef]tfaddr.Provider{ | ||
{LocalName: "aws"}: tfaddr.NewDefaultProvider("aws"), | ||
{LocalName: "google"}: tfaddr.NewDefaultProvider("google"), | ||
}, | ||
} | ||
|
||
err = s.Modules.UpdateMetadata(modDir, metadata, nil) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
pVersions := map[tfaddr.Provider]*version.Version{ | ||
tfaddr.NewDefaultProvider("aws"): version.Must(version.NewVersion("1.2.3")), | ||
tfaddr.NewDefaultProvider("google"): version.Must(version.NewVersion("2.5.5")), | ||
} | ||
err = s.Modules.UpdateInstalledProviders(modDir, pVersions) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
ls := langserver.NewLangServerMock(t, NewMockSession(&MockSessionInput{ | ||
TerraformCalls: &exec.TerraformMockCalls{ | ||
PerWorkDir: map[string][]*mock.Call{ | ||
modDir: validTfMockCalls(), | ||
}, | ||
}, | ||
StateStore: s, | ||
})) | ||
stop := ls.Start(t) | ||
defer stop() | ||
|
||
ls.Call(t, &langserver.CallRequest{ | ||
Method: "initialize", | ||
ReqParams: fmt.Sprintf(`{ | ||
"capabilities": {}, | ||
"rootUri": %q, | ||
"processId": 12345 | ||
}`, modUri)}) | ||
ls.Notify(t, &langserver.CallRequest{ | ||
Method: "initialized", | ||
ReqParams: "{}", | ||
}) | ||
|
||
ls.CallAndExpectResponse(t, &langserver.CallRequest{ | ||
Method: "workspace/executeCommand", | ||
ReqParams: fmt.Sprintf(`{ | ||
"command": %q, | ||
"arguments": ["uri=%s"] | ||
}`, cmd.Name("module.providers"), modUri)}, `{ | ||
"jsonrpc": "2.0", | ||
"id": 2, | ||
"result": { | ||
"v": 0, | ||
"provider_requirements": { | ||
"registry.terraform.io/hashicorp/aws": { | ||
"display_name": "hashicorp/aws", | ||
"version_constraint":"1.2.3", | ||
"docs_link": "https://registry.terraform.io/providers/hashicorp/aws/latest" | ||
}, | ||
"registry.terraform.io/hashicorp/google": { | ||
"display_name": "hashicorp/google", | ||
"version_constraint": "\u003e= 2.0.0", | ||
"docs_link": "https://registry.terraform.io/providers/hashicorp/google/latest" | ||
} | ||
}, | ||
"installed_providers":{ | ||
"registry.terraform.io/hashicorp/aws": "1.2.3", | ||
"registry.terraform.io/hashicorp/google": "2.5.5" | ||
} | ||
} | ||
}`) | ||
} | ||
|
||
func testConstraint(t *testing.T, v string) version.Constraints { | ||
constraints, err := version.NewConstraint(v) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
return constraints | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters