Merge branch 'master' of https://github.com/pulumi/pulumi into evan/gomod

This commit is contained in:
evanboyle 2020-03-19 13:01:38 -07:00
commit ce1928988b
7 changed files with 134 additions and 60 deletions

View file

@ -787,11 +787,16 @@ func (b *cloudBackend) RenameStack(ctx context.Context, stack backend.Stack, new
return err
}
// Transferring stack ownership is available to Pulumi admins, but isn't quite ready
// for general use yet.
if stackID.Owner != newIdentity.Owner {
url := "."
if consoleURL, err := b.StackConsoleURL(stack.Ref()); err == nil {
url = ":\n" + consoleURL + "/settings/options"
}
errMsg := "You cannot transfer stack ownership via a rename. If you wish to transfer ownership\n" +
"of a stack to another organization, please contact support@pulumi.com."
"of a stack to another organization, you can do so in the Pulumi Console by going to the\n" +
"\"Settings\" page of the stack and then clicking the \"Transfer Stack\" button" + url
return errors.Errorf(errMsg)
}

View file

@ -46,6 +46,12 @@ var (
templates *template.Template
packagedTemplates map[string][]byte
docHelpers map[string]codegen.DocLanguageHelper
// The following property case maps are for rendering property
// names of nested properties in Python language with the correct
// casing.
snakeCaseToCamelCase map[string]string
camelCaseToSnakeCase map[string]string
)
func init() {
@ -62,6 +68,9 @@ func init() {
docHelpers[lang] = python.DocLanguageHelper{}
}
}
snakeCaseToCamelCase = map[string]string{}
camelCaseToSnakeCase = map[string]string{}
}
// header represents the header of each resource markdown file.
@ -446,7 +455,7 @@ func (mod *modContext) genNestedTypes(member interface{}, resourceType bool) []d
InputType: inputTypeDocLink,
OutputType: outputTypeDocLink,
}
props[lang] = mod.getProperties(obj.Properties, lang, true)
props[lang] = mod.getProperties(obj.Properties, lang, true, true)
}
objs = append(objs, docNestedType{
@ -467,7 +476,7 @@ func (mod *modContext) genNestedTypes(member interface{}, resourceType bool) []d
// getProperties returns a slice of properties that can be rendered for docs for
// the provided slice of properties in the schema.
func (mod *modContext) getProperties(properties []*schema.Property, lang string, isInput bool) []property {
func (mod *modContext) getProperties(properties []*schema.Property, lang string, input, nested bool) []property {
if len(properties) == 0 {
return nil
}
@ -479,15 +488,40 @@ func (mod *modContext) getProperties(properties []*schema.Property, lang string,
}
characteristics := propertyCharacteristics{
input: isInput,
input: input,
optional: !prop.IsRequired,
}
propLangName := prop.Name
switch lang {
case "python":
pyName := python.PyName(prop.Name)
// The default casing for a Python property name is snake_case unless
// it is a property of a nested object, in which case, we should check the property
// case maps.
propLangName = pyName
if nested {
if snakeCase, ok := camelCaseToSnakeCase[prop.Name]; ok {
propLangName = snakeCase
} else if camelCase, ok := snakeCaseToCamelCase[pyName]; ok {
propLangName = camelCase
} else {
// If neither of the property case maps have the property
// then use the default name of the property.
propLangName = prop.Name
}
}
case "go", "csharp":
propLangName = strings.Title(prop.Name)
}
docProperties = append(docProperties, property{
Name: getLanguagePropertyName(prop.Name, lang, true),
Name: wbr(propLangName),
Comment: prop.Comment,
DeprecationMessage: prop.DeprecationMessage,
IsRequired: prop.IsRequired,
IsInput: isInput,
IsInput: input,
Type: mod.typeString(prop.Type, lang, characteristics, true),
})
}
@ -765,13 +799,13 @@ func (mod *modContext) genResource(r *schema.Resource) resourceDocArgs {
outputProps := make(map[string][]property)
stateInputs := make(map[string][]property)
for _, lang := range supportedLanguages {
inputProps[lang] = mod.getProperties(r.InputProperties, lang, true)
inputProps[lang] = mod.getProperties(r.InputProperties, lang, true, false)
if r.IsProvider {
continue
}
outputProps[lang] = mod.getProperties(r.Properties, lang, false)
outputProps[lang] = mod.getProperties(r.Properties, lang, false, false)
if r.StateInputs != nil {
stateInputs[lang] = mod.getProperties(r.StateInputs.Properties, lang, true)
stateInputs[lang] = mod.getProperties(r.StateInputs.Properties, lang, true, false)
}
}
@ -876,6 +910,7 @@ func (mod *modContext) genHeader(w io.Writer, title string) {
fmt.Fprintf(w, "---\n")
fmt.Fprintf(w, "title: %q\n", title)
fmt.Fprintf(w, "block_external_search_index: true\n")
fmt.Fprintf(w, "---\n\n")
fmt.Fprintf(w, "<!-- WARNING: this file was generated by %v. -->\n", mod.tool)
@ -1064,6 +1099,8 @@ func GeneratePackage(tool string, pkg *schema.Package) (map[string][]byte, error
}
types := &modContext{pkg: pkg, mod: "types", tool: tool}
docHelper := getLanguageDocHelper("python")
pyHelper := docHelper.(python.DocLanguageHelper)
for _, v := range pkg.Config {
visitObjectTypes(v.Type, func(t *schema.ObjectType) { types.details(t).outputType = true })
@ -1072,10 +1109,16 @@ func GeneratePackage(tool string, pkg *schema.Package) (map[string][]byte, error
scanResource := func(r *schema.Resource) {
mod := getMod(r.Token)
mod.resources = append(mod.resources, r)
for _, p := range r.Properties {
pyHelper.GenPropertyCaseMap(mod.pkg, mod.mod, tool, p, snakeCaseToCamelCase, camelCaseToSnakeCase)
visitObjectTypes(p.Type, func(t *schema.ObjectType) { types.details(t).outputType = true })
}
for _, p := range r.InputProperties {
pyHelper.GenPropertyCaseMap(mod.pkg, mod.mod, tool, p, snakeCaseToCamelCase, camelCaseToSnakeCase)
visitObjectTypes(p.Type, func(t *schema.ObjectType) {
if r.IsProvider {
types.details(t).outputType = true
@ -1083,6 +1126,7 @@ func GeneratePackage(tool string, pkg *schema.Package) (map[string][]byte, error
types.details(t).inputType = true
})
}
if r.StateInputs != nil {
visitObjectTypes(r.StateInputs, func(t *schema.ObjectType) { types.details(t).inputType = true })
}

View file

@ -285,10 +285,10 @@ func (mod *modContext) genFunction(f *schema.Function) functionDocArgs {
outputProps := make(map[string][]property)
for _, lang := range supportedLanguages {
if f.Inputs != nil {
inputProps[lang] = mod.getProperties(f.Inputs.Properties, lang, true)
inputProps[lang] = mod.getProperties(f.Inputs.Properties, lang, true, false)
}
if f.Outputs != nil {
outputProps[lang] = mod.getProperties(f.Outputs.Properties, lang, false)
outputProps[lang] = mod.getProperties(f.Outputs.Properties, lang, false, false)
}
}

View file

@ -1,8 +1,9 @@
{{ define "header" }}
---
title: "{{ .Title }}"
block_external_search_index: true
---
<style>
table td p { margin-top: 0; margin-bottom: 0; }
</style>
{{ end }}
{{- end }}

View file

@ -22,7 +22,6 @@ import (
"strings"
"unicode"
"github.com/pulumi/pulumi/pkg/codegen/python"
"github.com/pulumi/pulumi/sdk/go/common/util/contract"
)
@ -64,19 +63,3 @@ func tokenToName(tok string) string {
contract.Assertf(len(components) == 3, "malformed token %v", tok)
return strings.Title(components[2])
}
// getLanguagePropertyName returns a language-specific representation of a given
// property name.
func getLanguagePropertyName(name string, lang string, insertWordBreak bool) string {
switch lang {
case "python":
name = python.PyName(name)
case "go", "csharp":
name = strings.Title(name)
}
if !insertWordBreak {
return name
}
return wbr(name)
}

View file

@ -22,6 +22,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"go/format"
"io"
"path"
"reflect"
@ -31,6 +32,7 @@ import (
"unicode"
"github.com/pkg/errors"
"github.com/pulumi/pulumi/pkg/codegen/schema"
"github.com/pulumi/pulumi/sdk/go/common/util/contract"
)
@ -96,6 +98,7 @@ type pkgContext struct {
functionNames map[*schema.Function]string
needsUtils bool
tool string
packages map[string]*pkgContext
// Name overrides set in GoInfo
modToPkg map[string]string // Module name -> package name
@ -123,14 +126,18 @@ func (pkg *pkgContext) tokenToType(tok string) string {
mod = override
}
// If the package containing the type's token already has a resource with the
// same name, add a `Type` suffix.
modPkg := pkg.getPkg(mod)
name = title(name)
if modPkg.names.has(name) {
name += "Type"
}
if mod == pkg.mod {
name := title(name)
if pkg.names.has(name) {
name += "Type"
}
return name
}
return strings.Replace(mod, "/", "", -1) + "." + title(name)
return strings.Replace(mod, "/", "", -1) + "." + name
}
func tokenToName(tok string) string {
@ -749,16 +756,16 @@ func (pkg *pkgContext) genTypeRegistrations(w io.Writer, types []*schema.ObjectT
fmt.Fprintf(w, "}\n")
}
func (pkg *pkgContext) getTypeImports(t schema.Type, recurse bool, imports stringSet) {
func (pkg *pkgContext) getTypeImports(t schema.Type, recurse bool, imports stringSet, seen map[schema.Type]struct{}) {
if _, ok := seen[t]; ok {
return
}
seen[t] = struct{}{}
switch t := t.(type) {
case *schema.ArrayType:
if recurse {
pkg.getTypeImports(t.ElementType, false, imports)
}
pkg.getTypeImports(t.ElementType, recurse, imports, seen)
case *schema.MapType:
if recurse {
pkg.getTypeImports(t.ElementType, false, imports)
}
pkg.getTypeImports(t.ElementType, recurse, imports, seen)
case *schema.ObjectType:
mod := pkg.pkg.TokenToModule(t.Token)
if override, ok := pkg.modToPkg[mod]; ok {
@ -770,28 +777,27 @@ func (pkg *pkgContext) getTypeImports(t schema.Type, recurse bool, imports strin
for _, p := range t.Properties {
if recurse {
pkg.getTypeImports(p.Type, false, imports)
pkg.getTypeImports(p.Type, recurse, imports, seen)
}
}
case *schema.UnionType:
for _, e := range t.ElementTypes {
if recurse {
pkg.getTypeImports(e, false, imports)
}
pkg.getTypeImports(e, recurse, imports, seen)
}
}
}
func (pkg *pkgContext) getImports(member interface{}, imports stringSet) {
seen := map[schema.Type]struct{}{}
switch member := member.(type) {
case *schema.ObjectType:
pkg.getTypeImports(member, true, imports)
pkg.getTypeImports(member, true, imports, seen)
case *schema.Resource:
for _, p := range member.Properties {
pkg.getTypeImports(p.Type, false, imports)
pkg.getTypeImports(p.Type, false, imports, seen)
}
for _, p := range member.InputProperties {
pkg.getTypeImports(p.Type, false, imports)
pkg.getTypeImports(p.Type, false, imports, seen)
if p.IsRequired {
imports.add("github.com/pkg/errors")
@ -799,14 +805,14 @@ func (pkg *pkgContext) getImports(member interface{}, imports stringSet) {
}
case *schema.Function:
if member.Inputs != nil {
pkg.getTypeImports(member.Inputs, false, imports)
pkg.getTypeImports(member.Inputs, false, imports, seen)
}
if member.Outputs != nil {
pkg.getTypeImports(member.Outputs, false, imports)
pkg.getTypeImports(member.Outputs, false, imports, seen)
}
case []*schema.Property:
for _, p := range member {
pkg.getTypeImports(p.Type, false, imports)
pkg.getTypeImports(p.Type, false, imports, seen)
}
default:
return
@ -918,6 +924,17 @@ func (pkg *pkgContext) genPackageRegistration(w io.Writer) {
fmt.Fprintf(w, "}\n")
}
func (pkg *pkgContext) getPkg(mod string) *pkgContext {
if override, ok := pkg.modToPkg[mod]; ok {
mod = override
}
pack, ok := pkg.packages[mod]
if !ok {
return nil
}
return pack
}
// GoInfo holds information required to generate the Go SDK from a schema.
type GoInfo struct {
// Base path for package imports
@ -940,9 +957,10 @@ type GoInfo struct {
func GeneratePackage(tool string, pkg *schema.Package) (map[string][]byte, error) {
var goInfo GoInfo
err := json.Unmarshal(pkg.Language["go"], &goInfo)
if err != nil {
return nil, err
if golang, ok := pkg.Language["go"]; ok {
if err := json.Unmarshal(golang, &goInfo); err != nil {
return nil, errors.Wrap(err, "decoding go package info")
}
}
// group resources, types, and functions into Go packages
@ -965,6 +983,7 @@ func GeneratePackage(tool string, pkg *schema.Package) (map[string][]byte, error
tool: tool,
modToPkg: goInfo.ModuleToPackage,
pkgImportAliases: goInfo.PackageImportAliases,
packages: packages,
}
packages[mod] = pack
}
@ -1061,7 +1080,14 @@ func GeneratePackage(tool string, pkg *schema.Package) (map[string][]byte, error
if _, ok := files[relPath]; ok {
panic(errors.Errorf("duplicate file: %s", relPath))
}
files[relPath] = []byte(contents)
// Run Go formatter on the code before saving to disk
formattedSource, err := format.Source([]byte(contents))
if err != nil {
panic(errors.Errorf("invalid Go source code:\n\n%s", contents))
}
files[relPath] = formattedSource
}
name, registerPackage := pkg.Name, pkg.Provider != nil

View file

@ -77,6 +77,21 @@ func (d DocLanguageHelper) GetResourceFunctionResultName(resourceName string) st
return ""
}
// GenPropertyCaseMap generates the case maps for a property.
func (d DocLanguageHelper) GenPropertyCaseMap(pkg *schema.Package, modName, tool string, prop *schema.Property, snakeCaseToCamelCase, camelCaseToSnakeCase map[string]string) {
mod := &modContext{
pkg: pkg,
mod: modName,
tool: tool,
snakeCaseToCamelCase: snakeCaseToCamelCase,
camelCaseToSnakeCase: camelCaseToSnakeCase,
}
if err := mod.recordProperty(prop); err != nil {
fmt.Printf("error building case map for %q in module %q", prop.Name, modName)
}
}
// elementTypeToName returns the type name from an element type of the form
// package:module:_type, with its leading "_" stripped.
func elementTypeToName(el string) string {
@ -96,13 +111,13 @@ func getListWithTypeName(t string) string {
return "List[str]"
}
return fmt.Sprintf("List[%s]", PyName(t))
return fmt.Sprintf("List[%s]", strings.Title(t))
}
// getDictWithTypeName returns the Python representation of a dictionary
// where each item is of type `t`.
func getDictWithTypeName(t string) string {
return fmt.Sprintf("Dict[%s]", PyName(t))
return fmt.Sprintf("Dict[%s]", strings.Title(t))
}
// getMapWithTypeName returns the Python representation of a dictionary
@ -114,6 +129,6 @@ func getMapWithTypeName(t string) string {
case "any":
return "Dict[str, Any]"
default:
return fmt.Sprintf("Dict[str, %s]", PyName(t))
return fmt.Sprintf("Dict[str, %s]", strings.Title(t))
}
}