Skip to content

Commit

Permalink
Add Example documentation for Image (#479)
Browse files Browse the repository at this point in the history
* Add code to  generate documentation to provider package

* Add examplegen as a Makefile step

* attempt with new bridge but still fails

* explicitly set version in yaml so convert can run

* first real converted pulumi example

* Add Java

* Embed Example Usage doc into Image Resource

* Fix Java example and regenerate schema

* Generate SDKs

* Refactor convert call into helper function

* Add  make target as a dependency to tfgen

* Rename Make target to 'docs'

* handle errors
  • Loading branch information
guineveresaenger authored Feb 3, 2023
1 parent 38830b1 commit 0e19b79
Show file tree
Hide file tree
Showing 23 changed files with 567 additions and 57 deletions.
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ cleanup:
rm -r $(WORKING_DIR)/bin
rm -f provider/cmd/$(PROVIDER)/schema.go

docs:
cd provider/pkg/docs-gen/examples/ && go run generate.go ./yaml ./

help:
@grep '^[^.#]\+:\s\+.*#' Makefile | \
sed "s/\(.\+\):\s*\(.*\) #\s*\(.*\)/`printf "\033[93m"`\1`printf "\033[0m"` \3 [\2]/" | \
Expand All @@ -102,12 +105,12 @@ provider: tfgen install_plugins
test:
cd examples && go test -v -tags=all -parallel $(TESTPARALLELISM) -timeout 2h

tfgen: install_plugins
tfgen: install_plugins docs
(cd provider && go build -p 1 -o $(WORKING_DIR)/bin/$(TFGEN) -ldflags "-X $(PROJECT)/$(VERSION_PATH)=$(VERSION)" $(PROJECT)/$(PROVIDER_PATH)/cmd/$(TFGEN))
$(WORKING_DIR)/bin/$(TFGEN) schema --out provider/cmd/$(PROVIDER)
(cd provider && VERSION=$(VERSION) go generate cmd/$(PROVIDER)/main.go)

bin/pulumi-java-gen:
$(shell pulumictl download-binary -n pulumi-language-java -v $(JAVA_GEN_VERSION) -r pulumi/pulumi-java)

.PHONY: development build build_sdks install_go_sdk install_java_sdk install_python_sdk install_sdks only_build build_dotnet build_go build_java build_nodejs build_python clean cleanup help install_dotnet_sdk install_nodejs_sdk install_plugins lint_provider provider test tfgen
.PHONY: development build build_sdks docs install_go_sdk install_java_sdk install_python_sdk install_sdks only_build build_dotnet build_go build_java build_nodejs build_python clean cleanup help install_dotnet_sdk install_nodejs_sdk install_plugins lint_provider provider test tfgen
12 changes: 6 additions & 6 deletions provider/cmd/pulumi-resource-docker/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3017,7 +3017,7 @@
]
},
"docker:index/builderVersion:BuilderVersion": {
"description": "The version of the Docker builder",
"description": "The version of the Docker builder.",
"type": "string",
"enum": [
{
Expand Down Expand Up @@ -3053,11 +3053,11 @@
"additionalProperties": {
"type": "string"
},
"description": "An optional map of named build-time argument variables to set during the Docker build. This flag allows you to pass built-time variablesthat can be accessed like environment variables inside the RUN instruction."
"description": "An optional map of named build-time argument variables to set during the Docker build. This flag allows you to pass build-time variablesthat can be accessed like environment variables inside the RUN instruction."
},
"builderVersion": {
"$ref": "#/types/docker:index/builderVersion:BuilderVersion",
"description": "The version of the Docker builder. ",
"description": "The version of the Docker builder.",
"default": "BuilderBuildKit"
},
"cacheFrom": {
Expand All @@ -3076,7 +3076,7 @@
},
"platform": {
"type": "string",
"description": " Set platform if server is multi-platform capable"
"description": "The architecture of the platform you want to build this image for, e.g. `linux/arm64`."
},
"target": {
"type": "string",
Expand Down Expand Up @@ -4418,7 +4418,7 @@
}
},
"docker:index/image:Image": {
"description": "Builds a Docker Image and pushes to a Docker registry.",
"description": "{{% examples %}}\nBuilds a Docker Image and pushes to a Docker registry.\n\n{{% examples %}}\n## Example Usage\n{{% example %}}\n### A Docker image build\n\n```typescript\nimport * as pulumi from \"@pulumi/pulumi\";\nimport * as docker from \"@pulumi/docker\";\n\nconst demoImage = new docker.Image(\"demo-image\", {\n build: {\n context: \".\",\n dockerfile: \"Dockerfile\",\n },\n imageName: \"username/image:tag1\",\n skipPush: true,\n});\nexport const imageName = demoImage.imageName;\n```\n```python\nimport pulumi\nimport pulumi_docker as docker\n\ndemo_image = docker.Image(\"demo-image\",\n build={\n \"context\": \".\",\n \"dockerfile\": \"Dockerfile\",\n },\n image_name=\"username/image:tag1\",\n skip_push=True)\npulumi.export(\"imageName\", demo_image.image_name)\n```\n```csharp\nusing System.Collections.Generic;\nusing Pulumi;\nusing Docker = Pulumi.Docker;\n\nreturn await Deployment.RunAsync(() =\u003e \n{\n var demoImage = new Docker.Image(\"demo-image\", new()\n {\n Build = \n {\n { \"context\", \".\" },\n { \"dockerfile\", \"Dockerfile\" },\n },\n ImageName = \"username/image:tag1\",\n SkipPush = true,\n });\n\n return new Dictionary\u003cstring, object?\u003e\n {\n [\"imageName\"] = demoImage.ImageName,\n };\n});\n\n```\n```go\npackage main\n\nimport (\n\t\"github.com/pulumi/pulumi-docker/sdk/v4/go/docker\"\n\t\"github.com/pulumi/pulumi/sdk/v3/go/pulumi\"\n)\n\nfunc main() {\n\tpulumi.Run(func(ctx *pulumi.Context) error {\n\t\tdemoImage, err := docker.NewImage(ctx, \"demo-image\", \u0026docker.ImageArgs{\n\t\t\tBuild: pulumi.Any{\n\t\t\t\tContext: pulumi.String(\".\"),\n\t\t\t\tDockerfile: pulumi.String(\"Dockerfile\"),\n\t\t\t},\n\t\t\tImageName: pulumi.String(\"username/image:tag1\"),\n\t\t\tSkipPush: pulumi.Bool(true),\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tctx.Export(\"imageName\", demoImage.ImageName)\n\t\treturn nil\n\t})\n}\n```\n```yaml\nconfig: {}\ndescription: A Docker image build\nname: image-yaml\noutputs:\n imageName: ${demo-image.imageName}\nresources:\n demo-image:\n options:\n version: v4.0.0-alpha.1\n properties:\n build:\n context: .\n dockerfile: Dockerfile\n imageName: username/image:tag1\n skipPush: true\n type: docker:Image\nruntime: yaml\nvariables: {}\n```\n```java\npackage generated_program;\n\nimport com.pulumi.Context;\nimport com.pulumi.Pulumi;\nimport com.pulumi.core.Output;\nimport com.pulumi.docker.Image;\nimport com.pulumi.docker.ImageArgs;\nimport java.util.List;\nimport java.util.ArrayList;\nimport java.util.Map;\nimport java.io.File;\nimport java.nio.file.Files;\nimport java.nio.file.Paths;\n\npublic class App {\n public static void main(String[] args) {\n Pulumi.run(App::stack);\n }\n\n public static void stack(Context ctx) {\n var demoImage = new Image(\"demoImage\", ImageArgs.builder()\n .imageName(\"username/image:tag1\")\n .skipPush(true)\n .build());\n\n ctx.export(\"imageName\", demoImage.imageName());\n }\n}\n```\n{{% /example %}}\n{{% /examples %}}\n\n{{% //examples %}}",
"properties": {
"baseImageName": {
"type": "string",
Expand All @@ -4430,7 +4430,7 @@
},
"registryServer": {
"type": "string",
"description": "The URL of the registry server hosting the image."
"description": "The name of the registry server hosting the image."
}
},
"type": "object",
Expand Down
2 changes: 1 addition & 1 deletion provider/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
github.com/terraform-providers/terraform-provider-docker/shim v0.0.0
google.golang.org/grpc v1.51.0
google.golang.org/protobuf v1.28.1
gopkg.in/yaml.v3 v3.0.1
)

replace (
Expand Down Expand Up @@ -256,7 +257,6 @@ require (
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/frand v1.4.2 // indirect
sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 // indirect
)
6 changes: 6 additions & 0 deletions provider/nativeDocs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package provider

import _ "embed" // nolint:golint

//go:embed pkg/docs-gen/examples/image.md
var docImage string
187 changes: 187 additions & 0 deletions provider/pkg/docs-gen/examples/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
package main

import (
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strings"

"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
"gopkg.in/yaml.v3"
)

//go:generate go run generate.go yaml .

func main() {
if len(os.Args) < 3 {
fmt.Fprintf(os.Stdout, "Usage: %s <yaml source dir path> <markdown destination path>\n", os.Args[0])
os.Exit(1)
}
yamlPath := os.Args[1]
mdPath := os.Args[2]

if !filepath.IsAbs(yamlPath) {
cwd, err := os.Getwd()
contract.AssertNoError(err)
yamlPath = filepath.Join(cwd, yamlPath)
}

fileInfo, err := os.Lstat(mdPath)
if err != nil && os.IsNotExist(err) {
if err := os.MkdirAll(mdPath, 0600); err != nil {
panic(err)
}
}

if !fileInfo.IsDir() {
fmt.Fprintf(os.Stderr, "Expect markdown destination %q to be a directory\n", mdPath)
os.Exit(1)
}

yamlFiles, err := os.ReadDir(yamlPath)
if err != nil {
panic(err)
}
for _, yamlFile := range yamlFiles {
if err := processYaml(filepath.Join(yamlPath, yamlFile.Name()), mdPath); err != nil {
fmt.Fprintf(os.Stderr, "%+v", err)
os.Exit(1)
}
}
}

func markdownExamples(examples []string) string {
s := "{{% examples %}}\n## Example Usage\n"
for _, example := range examples {
s += example
}
s += "{{% /examples %}}\n"
return s
}

func markdownExample(description string,
typescript string,
python string,
csharp string,
golang string,
yaml string,
java string) string {

return fmt.Sprintf("{{%% example %%}}\n### %s\n\n"+
"```typescript\n%s```\n"+
"```python\n%s```\n"+
"```csharp\n%s```\n"+
"```go\n%s```\n"+
"```yaml\n%s```\n"+
"```java\n%s```\n"+
"{{%% /example %%}}\n",
description, typescript, python, csharp, golang, yaml, java)
}

func convert(language, tempDir, programFile string) (string, error) {
exampleDir := filepath.Join(tempDir, "example"+language)
cmd := exec.Command(
"pulumi",
"convert",
"--language",
language,
"--out",
filepath.Join(tempDir, exampleDir),
"--generate-only",
)

cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
cmd.Dir = tempDir
if err := cmd.Run(); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "convert %s failed, ignoring: %+v", language, err)
}
content, err := os.ReadFile(filepath.Join(tempDir, exampleDir, programFile))
if err != nil {
return "", err
}

return string(content), nil
}

func processYaml(path string, mdDir string) error {
yamlFile, err := os.Open(path)
if err != nil {
return err
}

base := filepath.Base(path)
md := strings.NewReplacer(".yaml", ".md", ".yml", ".md").Replace(base)

defer contract.IgnoreClose(yamlFile)
decoder := yaml.NewDecoder(yamlFile)
exampleStrings := []string{}
for {
example := map[string]interface{}{}
err := decoder.Decode(&example)
if err == io.EOF {
break
}

description := example["description"].(string)
dir, err := os.MkdirTemp("", "")
if err != nil {
return err
}

defer func() {
contract.IgnoreError(os.RemoveAll(dir))
}()

src, err := os.OpenFile(filepath.Join(dir, "Pulumi.yaml"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
if err != nil {
return err
}

if err = yaml.NewEncoder(src).Encode(example); err != nil {
return err
}
contract.AssertNoError(src.Close())

typescript, err := convert("typescript", dir, "index.ts")
if err != nil {
return err
}
python, err := convert("python", dir, "__main__.py")
if err != nil {
return err
}
csharp, err := convert("csharp", dir, "Program.cs")
if err != nil {
return err
}
golang, err := convert("go", dir, "main.go")
if err != nil {
return err
}
java, err := convert("java", dir, "src/main/java/generated_program/App.java")
if err != nil {
return err
}

yamlContent, err := os.ReadFile(filepath.Join(dir, "Pulumi.yaml"))
if err != nil {
return err
}
yaml := string(yamlContent)

exampleStrings = append(exampleStrings, markdownExample(description, typescript, python, csharp, golang, yaml, java))
}
contract.AssertNoError(err)
fmt.Fprintf(os.Stdout, "Writing %s\n", filepath.Join(mdDir, md))
f, err := os.OpenFile(filepath.Join(mdDir, md), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
if err != nil {
return err
}
defer contract.IgnoreClose(f)
_, err = f.Write([]byte(markdownExamples(exampleStrings)))
contract.AssertNoError(err)
return nil
}
Loading

0 comments on commit 0e19b79

Please sign in to comment.