From 218029989c4c3e1086b576dada10f77ccff30cf9 Mon Sep 17 00:00:00 2001 From: Ian Wahbe Date: Fri, 3 Nov 2023 14:20:11 -0700 Subject: [PATCH] Add missing docs override (#1500) During provider updates, we occasionally have missing docs that we accept. We also set `PULUMI_MISSING_DOCS_ERROR=true` during updates so we are made aware. Right now, the solution is to set `tfbridge.DocInfo{Markdown: []byte(" ")}`, which prevents docs from ever showing up. This PR adds a flag for this scenario, that mitigates the error for missing docs but doesn't preclude the doc showing up in the future. --- pkg/tfbridge/info.go | 5 +++ pkg/tfgen/docs.go | 2 +- pkg/tfgen/docs_test.go | 92 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 91 insertions(+), 8 deletions(-) diff --git a/pkg/tfbridge/info.go b/pkg/tfbridge/info.go index 0b55da79c..037779c8f 100644 --- a/pkg/tfbridge/info.go +++ b/pkg/tfbridge/info.go @@ -495,6 +495,11 @@ type DocInfo struct { // this document will satisfy the criteria `docs/pulumiToken.md` // The examples need to wrapped in the correct shortcodes ReplaceExamplesSection bool + + // Don't error when this doc is missing. + // + // This applies when PULUMI_MISSING_DOCS_ERROR="true". + AllowMissing bool } // GetImportDetails returns a string of import instructions defined in the Pulumi provider. Defaults to empty. diff --git a/pkg/tfgen/docs.go b/pkg/tfgen/docs.go index 4d13f04c0..4e22ab0a1 100644 --- a/pkg/tfgen/docs.go +++ b/pkg/tfgen/docs.go @@ -259,7 +259,7 @@ func getDocsForResource(g *Generator, source DocsSource, kind DocKind, msg := fmt.Sprintf("could not find docs for %v %v. Override the Docs property in the %v mapping. See "+ "type tfbridge.DocInfo for details.", kind, formatEntityName(rawname), kind) - if cmdutil.IsTruthy(os.Getenv("PULUMI_MISSING_DOCS_ERROR")) { + if cmdutil.IsTruthy(os.Getenv("PULUMI_MISSING_DOCS_ERROR")) && !info.GetDocs().AllowMissing { g.error(msg) return entityDocs{}, fmt.Errorf(msg) } diff --git a/pkg/tfgen/docs_test.go b/pkg/tfgen/docs_test.go index 3e0d99f6b..f68818cc1 100644 --- a/pkg/tfgen/docs_test.go +++ b/pkg/tfgen/docs_test.go @@ -1133,11 +1133,91 @@ func TestParseTFMarkdown(t *testing.T) { } } +func TestErrorMissingDocs(t *testing.T) { + tests := []struct { + docs tfbridge.DocInfo + forbidMissingDocsEnv string + source DocsSource + expectErr bool + }{ + // No Error, since the docs can be found + {source: mockSource{"raw_name": "some-docs"}}, + { + source: mockSource{"raw_name": "some-docs"}, + forbidMissingDocsEnv: "true", + }, + + // Docs are missing, but we don't ask to error on missing + {source: mockSource{}}, + + // Docs are missing, and we ask to error on missing, so error + { + source: mockSource{}, + forbidMissingDocsEnv: "true", + expectErr: true, + }, + + // Docs are missing and we ask globally to error on missing, but we + // override locally, so no error + { + source: mockSource{}, + docs: tfbridge.DocInfo{AllowMissing: true}, + forbidMissingDocsEnv: "true", + }, + } + + for _, tt := range tests { + tt := tt + t.Run("", func(t *testing.T) { + g := &Generator{ + sink: mockSink{t}, + } + rawName := "raw_name" + t.Setenv("PULUMI_MISSING_DOCS_ERROR", tt.forbidMissingDocsEnv) + _, err := getDocsForResource(g, tt.source, ResourceDocs, rawName, &mockResource{ + token: tokens.Token(rawName), + docs: tt.docs, + }) + if tt.expectErr { + assert.NotNil(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +type mockSource map[string]string + +func (m mockSource) getResource(rawname string, info *tfbridge.DocInfo) (*DocFile, error) { + f, ok := m[rawname] + if !ok { + return nil, nil + } + return &DocFile{ + Content: []byte(f), + FileName: rawname + ".md", + }, nil +} +func (m mockSource) getDatasource(rawname string, info *tfbridge.DocInfo) (*DocFile, error) { + return nil, nil +} + type mockSink struct{ t *testing.T } -func (mockSink) warn(string, ...interface{}) {} -func (mockSink) debug(string, ...interface{}) {} -func (mockSink) error(string, ...interface{}) {} +func (mockSink) warn(string, ...interface{}) {} +func (mockSink) debug(string, ...interface{}) {} +func (mockSink) error(string, ...interface{}) {} +func (mockSink) Logf(sev diag.Severity, diag *diag.Diag, args ...interface{}) {} +func (mockSink) Debugf(diag *diag.Diag, args ...interface{}) {} +func (mockSink) Infof(diag *diag.Diag, args ...interface{}) {} +func (mockSink) Infoerrf(diag *diag.Diag, args ...interface{}) {} +func (mockSink) Errorf(diag *diag.Diag, args ...interface{}) {} +func (mockSink) Warningf(diag *diag.Diag, args ...interface{}) {} + +func (mockSink) Stringify(sev diag.Severity, diag *diag.Diag, args ...interface{}) (string, string) { + return "", "" +} type mockResource struct { docs tfbridge.DocInfo @@ -1145,13 +1225,11 @@ type mockResource struct { } func (r *mockResource) GetFields() map[string]*tfbridge.SchemaInfo { - //TODO implement me - panic("implement me") + return map[string]*tfbridge.SchemaInfo{} } func (r *mockResource) ReplaceExamplesSection() bool { - //TODO implement me - panic("implement me") + return false } func (r *mockResource) GetDocs() *tfbridge.DocInfo {