Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
11378: First pass of adding the convert mapper r=Frassle a=Frassle I'm having to do a dance here because it changes the Provider interface, which has implementations in terraform-bridge, which is now linked to pulumi for tfconvert. So we'll need to publish a version of Pulumi that defines the request/response types (this PR) then update tfbridge and implement the method (even though the service doesn't actually define it yet) , then update pulumi to that tfbridge and add the service method. So fun build dances. But I think the nterface is about right. For each "from" which is pretty much just a plugin name, so for terraform we're assuming we'll eventually have a plugin like "pulumi-convert-tf", we take that plugin name and loop over all the providers we've got installed and ask if they have an "tf" specific mapping data to pass to the tf plugin to help it do conversions. Then in tfconvert we can just ask for that mapping data and marshal it into the structure that tfbridge defines (happens to be a json struct). This is nice because it means users can just write their own mapping files (assuming the convert plugin documents the format) and override the conversion process. I can imagine someone taking like the pulumi-aws mapping json, doing a search replace to change it to point to aws-native and then running a tfconvert. The reason this has to ask every installed provider upfront is so that they can return the mapping from terraform name to pulumi name. So tfbridge will just go "oh there's some hcl that says use this 'azurerm' resource, go get me the mapping data for 'azurerm'" and the engine will have already asked our pulumi-azure plugin for its map info which will have included that it's the mapping from 'azurerm' allowing the engine to join up the tfconvert request to a response. `@AaronFriel's` suggestion of a registry for this stuff would work as well, in that case we'd simple have an optional path that if there's no local mapping for 'azurerm' (say if the user hasn't installed pulumi-azure yet) we'd ask the registry if it knows a mapping, and it could then return back either "yes go look at the pulumi-azure plugin", or just return the full mapping back itself. Co-authored-by: Fraser Waters <fraser@pulumi.com>
- Loading branch information
Showing
15 changed files
with
935 additions
and
186 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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
// Copyright 2016-2022, 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 | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// 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. | ||
|
||
package convert | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/blang/semver" | ||
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin" | ||
"github.com/pulumi/pulumi/sdk/v3/go/common/workspace" | ||
) | ||
|
||
type Mapper interface { | ||
GetMapping(provider string) ([]byte, error) | ||
} | ||
|
||
type pluginMapper struct { | ||
entries map[string][]byte | ||
} | ||
|
||
func NewPluginMapper(host plugin.Host, key string, mappings []string) (Mapper, error) { | ||
entries := map[string][]byte{} | ||
|
||
// Enumerate _all_ our installed plugins to ask for any mappings they provider. This allows users to | ||
// convert aws terraform code for example by just having 'pulumi-aws' plugin locally, without needing to | ||
// specify it anywhere on the command line, and without tf2pulumi needing to know about every possible plugin. | ||
plugins, err := workspace.GetPlugins() | ||
if err != nil { | ||
return nil, fmt.Errorf("could not get plugins: %w", err) | ||
} | ||
// We only care about the latest version of each plugin | ||
latestVersions := make(map[string]semver.Version) | ||
for _, plugin := range plugins { | ||
if plugin.Kind != workspace.ResourcePlugin { | ||
continue | ||
} | ||
|
||
if cur, has := latestVersions[plugin.Name]; has { | ||
if plugin.Version.GT(cur) { | ||
latestVersions[plugin.Name] = *plugin.Version | ||
} | ||
} else { | ||
latestVersions[plugin.Name] = *plugin.Version | ||
} | ||
} | ||
// Now go through each of those plugins and ask for any conversion data they have for the given key we're | ||
// looking for. | ||
//for pkg, version := range latestVersions { | ||
// TODO: We have to do a dance here where first we publish a version of pulumi with these RPC structures | ||
// then add methods to terraform-bridge to implement this method as if it did exist, and then actually add | ||
// the RPC method and uncomment out the code below. This is all because we currently build these in a loop | ||
// (pulumi include terraform-bridge, which includes pulumi). | ||
|
||
//provider, err := host.Provider(tokens.Package(pkg), &version) | ||
//if err != nil { | ||
// return nil, fmt.Errorf("could not create provider '%s': %w", pkg, err) | ||
//} | ||
|
||
//data, mappedProvider, err := provider.GetMapping(key) | ||
//if err != nil { | ||
// return nil, fmt.Errorf("could not get mapping for provider '%s': %w", pkg, err) | ||
//} | ||
//entries[mappedProvider] = data | ||
//} | ||
|
||
// These take precedence over any plugin returned mappings so we do them last and just overwrite the | ||
// entries | ||
for _, path := range mappings { | ||
data, err := os.ReadFile(path) | ||
if err != nil { | ||
return nil, fmt.Errorf("could not read mapping file '%s': %w", path, err) | ||
} | ||
|
||
// Mapping file names are assumed to be the provider key. | ||
provider := filepath.Base(path) | ||
// strip the extension | ||
dotIndex := strings.LastIndex(provider, ".") | ||
if dotIndex != -1 { | ||
provider = provider[0:dotIndex] | ||
} | ||
|
||
entries[provider] = data | ||
} | ||
return &pluginMapper{ | ||
entries: entries, | ||
}, nil | ||
} | ||
|
||
func (l *pluginMapper) GetMapping(provider string) ([]byte, error) { | ||
entry, ok := l.entries[provider] | ||
if ok { | ||
return entry, nil | ||
} | ||
return nil, fmt.Errorf("could not find any conversion mapping for %s", provider) | ||
} |
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
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
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
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
Oops, something went wrong.