Skip to content

Commit 0e639f6

Browse files
authored
Refactor: Move *Info objects out of /pkg/tfbridge to their own package: /pkg/tfbridge/info (#1879)
This PR is a refactor to separate out the user's declaration of a provider (via `tfbridge.*Info` structs and the runtime behavior of the provider (via various `tfbridge.*` methods) This PR moves `/pkg/tfbridge.*Info` and supporting types to `/pkg/tfbridge/info.*`. This allows finer grain dependency management, and crucially it allows a package to depend on user information `/pkg/tfbridge/info` but be importable by the runtime `/pkg/tfbridge`. To the greatest degree possible, this is a pure refactor. There are 3 unavoidable breaking changes: 1. `tfbridge.MetadataInfo.ExtractRuntimeMetadata` has been moved from a method on `tfbridge.MetadataInfo` (now `info.Metdata`) to a stand alone function: `tfbridge.ExtractRuntimeMetadata`. This function is public only to share with `/pkg/tfgen` and I do not expect users will call it. A [GH search](https://github.com/search?q=ExtractRuntimeMetadata+lang%3Ago&type=code) shows that only the bridge calls this function. I don't expect other repo to be impacted. 2. `tfbridge.ProviderInfo.(Must)?TraverseProperties` has been moved from a method to a standalone function: `tfbridge.(Must)?TraverseProperties`. A [GH code search](https://github.com/search?q=%28MustTraverseProperties+OR+TraverseProperties%29+lang%3Ago&type=code) shows that this is only used in `pulumi-gcp`. `pulumi-gcp` is the only repo I expect to be impacted. 3. `tfbridge.ElementStrategy[T Resource | DataSource] func(tfToken string, elem *T) error` could not be re-exported since go does not allow type aliases for generic types. This type is largely internal to the bridge and a [GH code search](https://github.com/search?q=tfbridge.ElementStrategy+lang%3Ago&type=code) does not show any external usage. I don't expect any users to be effected here. All other moved types and methods have been back-ported into `tfbridge` to avoid breaking existing providers. Consumers of the bridge shouldn't observe any changes, they are welcome to continue to use `tfbridge.ProviderInfo` and it will continue to work exactly as it previously did. This PR consists entirely of name changes. There are no logic changes, alterations, improvements, etc. The bodies of all functions and types are unchanged.
1 parent 45d8f28 commit 0e639f6

26 files changed

+2230
-1717
lines changed

pkg/tfbridge/assets.go

Lines changed: 9 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -14,167 +14,24 @@
1414

1515
package tfbridge
1616

17-
import (
18-
"io"
19-
"os"
20-
"path/filepath"
17+
// This file provides backward compatibility for moving [info.AssetTranslation] and
18+
// [info.AssetTranslationKind] to [info].
2119

22-
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
23-
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
24-
)
20+
import "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge/info"
2521

2622
// AssetTranslation instructs the bridge how to translate assets into something Terraform can use.
27-
type AssetTranslation struct {
28-
Kind AssetTranslationKind // the kind of translation to perform.
29-
Format resource.ArchiveFormat // an archive format, required if this is an archive.
30-
HashField string // a field to store the hash into, if any.
31-
}
23+
type AssetTranslation = info.AssetTranslation
3224

3325
// AssetTranslationKind may be used to choose from various source and dest translation targets.
34-
type AssetTranslationKind int
26+
type AssetTranslationKind = info.AssetTranslationKind
3527

3628
const (
3729
// FileAsset turns the asset into a file on disk and passes the filename in its place.
38-
FileAsset AssetTranslationKind = iota
30+
FileAsset = info.FileAsset
3931
// BytesAsset turns the asset into a []byte and passes it directly in-memory.
40-
BytesAsset
32+
BytesAsset = info.BytesAsset
4133
// FileArchive turns the archive into a file on disk and passes the filename in its place.
42-
FileArchive
34+
FileArchive = info.FileArchive
4335
// BytesArchive turns the asset into a []byte and passes that directly in-memory.
44-
BytesArchive
36+
BytesArchive = info.BytesArchive
4537
)
46-
47-
// Type fetches the Pulumi runtime type corresponding to values of this asset kind.
48-
func (a *AssetTranslation) Type() string {
49-
switch a.Kind {
50-
case FileAsset, BytesAsset:
51-
return "Asset"
52-
case FileArchive, BytesArchive:
53-
return "Archive"
54-
default:
55-
contract.Failf("Unrecognized asset translation kind: %v", a.Kind)
56-
return ""
57-
}
58-
}
59-
60-
// writeToTempFile creates a temporary file and passes it to the provided function, which will fill in the file's
61-
// contents. Upon success, this function returns the path of the temporary file and a nil error.
62-
func writeToTempFile(writeFunc func(w io.Writer) error) (string, error) {
63-
f, err := os.CreateTemp("", "pulumi-temp-asset")
64-
if err != nil {
65-
return "", err
66-
}
67-
defer contract.IgnoreClose(f)
68-
69-
if err := writeFunc(f); err != nil {
70-
return "", err
71-
}
72-
73-
return f.Name(), nil
74-
}
75-
76-
// translateToFile translates an asset or archive to a filename. If possible, it attempts to reuse previously spilled
77-
// assets/archives with the same identity.
78-
func translateToFile(hash string, hasContents bool, writeFunc func(w io.Writer) error) (string, error) {
79-
// If possible, we want to produce a predictable filename in order to avoid spurious diffs and spilling the same
80-
// asset multiple times.
81-
memoPath := ""
82-
if hash != "" {
83-
memoPath = filepath.Join(os.TempDir(), "pulumi-asset-"+hash)
84-
}
85-
86-
// If we have no contents, just return the file path. Note that this may be the empty string if we were also
87-
// missing a hash.
88-
if !hasContents {
89-
return memoPath, nil
90-
}
91-
92-
// If we have no translation path, just write the asset to a temporary file and return.
93-
if memoPath == "" {
94-
return writeToTempFile(writeFunc)
95-
}
96-
97-
// If the translation file already exists, assume it has the appropriate contents and return the file path.
98-
info, err := os.Stat(memoPath)
99-
if err == nil && info.Mode().IsRegular() && info.Size() > 0 {
100-
return memoPath, nil
101-
}
102-
103-
// Otherwise, write the asset to a temporary file, then attempt to move the temp file to the expected path.
104-
// If the move fails, we'll use the temp file name.
105-
tempName, err := writeToTempFile(writeFunc)
106-
if err != nil {
107-
return "", err
108-
}
109-
if err := os.Rename(tempName, memoPath); err != nil && !os.IsExist(err) {
110-
return tempName, nil
111-
}
112-
return memoPath, nil
113-
}
114-
115-
// IsAsset returns true if the translation deals with an asset (rather than archive).
116-
func (a *AssetTranslation) IsAsset() bool {
117-
return a.Kind == FileAsset || a.Kind == BytesAsset
118-
}
119-
120-
// IsArchive returns true if the translation deals with an archive (rather than asset).
121-
func (a *AssetTranslation) IsArchive() bool {
122-
return a.Kind == FileArchive || a.Kind == BytesArchive
123-
}
124-
125-
// TranslateAsset translates the given asset using the directives provided by the translation info.
126-
func (a *AssetTranslation) TranslateAsset(asset *resource.Asset) (interface{}, error) {
127-
contract.Assertf(a.IsAsset(), "a.IsAsset()")
128-
129-
// TODO[pulumi/pulumi#153]: support HashField.
130-
131-
// Now produce either a temp file or a binary blob, as requested.
132-
switch a.Kind {
133-
case FileAsset:
134-
path, err := translateToFile(asset.Hash, asset.HasContents(), func(w io.Writer) error {
135-
blob, err := asset.Read()
136-
if err != nil {
137-
return err
138-
}
139-
defer contract.IgnoreClose(blob)
140-
141-
_, err = io.Copy(w, blob)
142-
return err
143-
})
144-
return path, err
145-
case BytesAsset:
146-
if !asset.HasContents() {
147-
return []byte{}, nil
148-
}
149-
return asset.Bytes()
150-
default:
151-
contract.Failf("Unrecognized asset translation kind: %v", a.Kind)
152-
return nil, nil
153-
}
154-
}
155-
156-
// TranslateArchive translates the given archive using the directives provided by the translation info.
157-
func (a *AssetTranslation) TranslateArchive(archive *resource.Archive) (interface{}, error) {
158-
// TODO[pulumi/pulumi#153]: support HashField.
159-
160-
// Produce either a temp file or an in-memory representation, as requested.
161-
format := a.Format
162-
if format == resource.NotArchive {
163-
format = resource.ZIPArchive
164-
}
165-
switch a.Kind {
166-
case FileArchive, FileAsset:
167-
path, err := translateToFile(archive.Hash, archive.HasContents(), func(w io.Writer) error {
168-
return archive.Archive(format, w)
169-
})
170-
return path, err
171-
case BytesArchive, BytesAsset:
172-
if !archive.HasContents() {
173-
return []byte{}, nil
174-
}
175-
return archive.Bytes(format)
176-
default:
177-
contract.Failf("Unrecognized asset translation kind: %v", a.Kind)
178-
return nil, nil
179-
}
180-
}

pkg/tfbridge/auto_aliasing.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ package tfbridge
1717
import (
1818
"sort"
1919

20+
_ "unsafe" // Needed for linkname
21+
2022
"github.com/Masterminds/semver"
2123

2224
"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
@@ -51,14 +53,8 @@ import (
5153
// detected, and then overrides are cleared. Effectively this makes sure that upstream
5254
// MaxItems changes are deferred until the next major version.
5355
//
54-
// Panics if [ProviderInfo.ApplyAutoAliases] would return an error.
55-
func (info *ProviderInfo) MustApplyAutoAliases() {
56-
err := info.ApplyAutoAliases()
57-
contract.AssertNoErrorf(err, "Failed to apply aliases")
58-
}
59-
60-
// See [MustApplyAutoAliases].
61-
func (info *ProviderInfo) ApplyAutoAliases() error {
56+
//go:linkname ApplyAutoAliases
57+
func ApplyAutoAliases(info *ProviderInfo) error {
6258
// Do minimal work at runtime to avoid adding to provider startup delay.
6359
if currentRuntimeStage == runningProviderStage {
6460
autoSettings, err := loadAutoSettings(info.GetMetadata())

pkg/tfbridge/examples.go

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,12 @@ import (
55
"os"
66
"path/filepath"
77
"text/template"
8+
9+
"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge/info"
810
)
911

1012
// HclExampler represents a supplemental HCL example for a given resource or function.
11-
type HclExampler interface {
12-
// GetToken returns the fully qualified path to the resource or function in the schema, e.g.
13-
// "provider:module/getFoo:getFoo" (function), or
14-
// "provider:module/bar:Bar" (resource)
15-
GetToken() string
16-
// GetMarkdown returns the Markdown that comprises the entire example, including the header.
17-
//
18-
// Headers should be an H3 ("###") and the header content should not contain any prefix, e.g. "Foo with Bar" not,
19-
// "Example Usage - Foo with Bar".
20-
//
21-
// Code should be surrounded with code fences with an indicator of the language on the opening fence, e.g. "```hcl".
22-
GetMarkdown() (string, error)
23-
}
13+
type HclExampler = info.HclExampler
2414

2515
// LocalFileHclExample represents a supplemental HCL example that is on a relative path within the Pulumi provider repo.
2616
type LocalFileHclExample struct {

0 commit comments

Comments
 (0)