diff --git a/CHANGELOG.md b/CHANGELOG.md index bbba15fe84..e37373adfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Fix Chart previews when the cluster is unreachable. (https://github.com/pulumi/pulumi-kubernetes/pull/2992) - Fix a panic that could occur when a missing field became `null`. (https://github.com/pulumi/pulumi-kubernetes/issues/1970) - Add field manager's name to server-side apply conflict errors. (https://github.com/pulumi/pulumi-kubernetes/pull/2983) +- Helm Chart V4 (https://github.com/pulumi/pulumi-kubernetes/pull/2947) ## 4.11.0 (April 17, 2024) diff --git a/provider/cmd/pulumi-gen-kubernetes/main.go b/provider/cmd/pulumi-gen-kubernetes/main.go index 77af04d3b7..afa4d0f4aa 100644 --- a/provider/cmd/pulumi-gen-kubernetes/main.go +++ b/provider/cmd/pulumi-gen-kubernetes/main.go @@ -174,6 +174,7 @@ func generateSchema(swaggerPath string) schema.PackageSpec { // This is to mostly filter resources from the spec. var resourcesToFilterFromTemplate = codegen.NewStringSet( "kubernetes:helm.sh/v3:Release", + "kubernetes:helm.sh/v4:Chart", "kubernetes:yaml/v2:ConfigFile", "kubernetes:yaml/v2:ConfigGroup", ) diff --git a/provider/cmd/pulumi-resource-kubernetes/schema.json b/provider/cmd/pulumi-resource-kubernetes/schema.json index 86769df94c..d99921d29c 100644 --- a/provider/cmd/pulumi-resource-kubernetes/schema.json +++ b/provider/cmd/pulumi-resource-kubernetes/schema.json @@ -60,6 +60,7 @@ "flowcontrol.apiserver.k8s.io/v1beta3": "FlowControl.V1Beta3", "helm.sh/v2": "Helm.V2", "helm.sh/v3": "Helm.V3", + "helm.sh/v4": "Helm.V4", "meta/v1": "Meta.V1", "networking.k8s.io/v1": "Networking.V1", "networking.k8s.io/v1alpha1": "Networking.V1Alpha1", @@ -140,6 +141,7 @@ "helm.sh": "helm", "helm.sh/v2": "helm/v2", "helm.sh/v3": "helm/v3", + "helm.sh/v4": "helm/v4", "meta/v1": "meta/v1", "networking.k8s.io/v1": "networking/v1", "networking.k8s.io/v1alpha1": "networking/v1alpha1", @@ -205,6 +207,7 @@ "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/flowcontrol/v1beta2": "flowcontrolv1beta2", "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/flowcontrol/v1beta3": "flowcontrolv1beta3", "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v3": "helmv3", + "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4": "helmv4", "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/meta/v1": "metav1", "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/networking/v1": "networkingv1", "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/networking/v1alpha1": "networkingv1alpha1", @@ -274,6 +277,7 @@ "flowcontrol.apiserver.k8s.io/v1beta3": "flowcontrol.v1beta3", "helm.sh/v2": "helm.v2", "helm.sh/v3": "helm.v3", + "helm.sh/v4": "helm.v4", "meta/v1": "meta.v1", "networking.k8s.io/v1": "networking.v1", "networking.k8s.io/v1alpha1": "networking.v1alpha1", @@ -360,6 +364,7 @@ "helm.sh": "helm", "helm.sh/v2": "helm/v2", "helm.sh/v3": "helm/v3", + "helm.sh/v4": "helm/v4", "meta/v1": "meta/v1", "networking.k8s.io/v1": "networking/v1", "networking.k8s.io/v1alpha1": "networking/v1alpha1", @@ -432,6 +437,7 @@ "helm.sh": "helm", "helm.sh/v2": "helm/v2", "helm.sh/v3": "helm/v3", + "helm.sh/v4": "helm/v4", "meta/v1": "meta/v1", "networking.k8s.io/v1": "networking/v1", "networking.k8s.io/v1alpha1": "networking/v1alpha1", @@ -53559,6 +53565,77 @@ } } }, + "kubernetes:helm.sh/v4:PostRenderer": { + "description": "Specification defining the post-renderer to use.", + "properties": { + "args": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Arguments to pass to the post-renderer command." + }, + "command": { + "type": "string", + "description": "Path to an executable to be used for post rendering." + } + }, + "type": "object", + "required": [ + "command" + ], + "language": { + "nodejs": { + "requiredOutputs": [ + "command", + "args" + ] + } + } + }, + "kubernetes:helm.sh/v4:RepositoryOpts": { + "description": "Specification defining the Helm chart repository to use.", + "properties": { + "caFile": { + "$ref": "pulumi.json#/Asset", + "description": "The Repository's CA File" + }, + "certFile": { + "$ref": "pulumi.json#/Asset", + "description": "The repository's cert file" + }, + "keyFile": { + "$ref": "pulumi.json#/Asset", + "description": "The repository's cert key file" + }, + "password": { + "type": "string", + "description": "Password for HTTP basic authentication", + "secret": true + }, + "repo": { + "type": "string", + "description": "Repository where to locate the requested chart. If is a URL the chart is installed without installing the repository." + }, + "username": { + "type": "string", + "description": "Username for HTTP basic authentication" + } + }, + "type": "object", + "language": { + "nodejs": { + "requiredOutputs": [ + "repo", + "keyFile", + "certFile", + "caFile", + "username", + "password" + ] + } + } + }, "kubernetes:index:HelmReleaseSettings": { "description": "Options to configure the Helm Release resource.", "properties": { @@ -53591,7 +53668,7 @@ }, "repositoryCache": { "type": "string", - "description": "The path to the file containing cached repository indexes.", + "description": "The path to the directory containing cached repository indexes.", "defaultInfo": { "environment": [ "PULUMI_K8S_HELM_REPOSITORY_CACHE" @@ -53615,7 +53692,7 @@ "properties": { "burst": { "type": "integer", - "description": "Maximum burst for throttle. Default value is 10.", + "description": "Maximum burst for throttle. Default value is 120.", "defaultInfo": { "environment": [ "PULUMI_K8S_CLIENT_BURST" @@ -53624,7 +53701,7 @@ }, "qps": { "type": "number", - "description": "Maximum queries per second (QPS) to the API server from this client. Default value is 5.", + "description": "Maximum queries per second (QPS) to the API server from this client. Default value is 50.", "defaultInfo": { "environment": [ "PULUMI_K8S_CLIENT_QPS" @@ -87314,6 +87391,91 @@ "chart" ] }, + "kubernetes:helm.sh/v4:Chart": { + "description": "Chart is a component representing a collection of resources described by a Helm Chart.\nHelm charts are a popular packaging format for Kubernetes applications, and published\nto registries such as [Artifact Hub](https://artifacthub.io/packages/search?kind=0&sort=relevance&page=1). \n\nChart does not use Tiller or create a Helm Release; the semantics are equivalent to\nrunning `helm template --dry-run=server` and then using Pulumi to deploy the resulting YAML manifests.\nThis allows you to apply [Pulumi Transformations](https://www.pulumi.com/docs/concepts/options/transformations/) and\n[Pulumi Policies](https://www.pulumi.com/docs/using-pulumi/crossguard/) to the Kubernetes resources.\n\nYou may also want to consider the `Release` resource as an alternative method for managing helm charts. For more\ninformation about the trade-offs between these options, see: [Choosing the right Helm resource for your use case](https://www.pulumi.com/registry/packages/kubernetes/how-to-guides/choosing-the-right-helm-resource-for-your-use-case).\n\n### Chart Resolution\n\nThe Helm Chart can be fetched from any source that is accessible to the `helm` command line.\nThe following variations are supported:\n\n1. By chart reference with repo prefix: `chart: \"example/mariadb\"`\n2. By path to a packaged chart: `chart: \"./nginx-1.2.3.tgz\"`\n3. By path to an unpacked chart directory: `chart: \"./nginx\"`\n4. By absolute URL: `chart: \"https://example.com/charts/nginx-1.2.3.tgz\"`\n5. By chart reference with repo URL: `chart: \"nginx\", repositoryOpts: { repo: \"https://example.com/charts/\" }`\n6. By OCI registry: `chart: \"oci://example.com/charts/nginx\", version: \"1.2.3\"`\n\nA chart reference is a convenient way of referencing a chart in a chart repository.\n\nWhen you use a chart reference with a repo prefix (`example/mariadb`), Pulumi will look in Helm's local configuration\nfor a chart repository named `example`, and will then look for a chart in that repository whose name is `mariadb`.\nIt will install the latest stable version of that chart, unless you specify `devel` to also include\ndevelopment versions (alpha, beta, and release candidate releases), or supply a version number with `version`.\n\nUse the `verify` and optional `keyring` inputs to enable Chart verification.\nBy default, Pulumi uses the keyring at `$HOME/.gnupg/pubring.gpg`. See: [Helm Provenance and Integrity](https://helm.sh/docs/topics/provenance/).\n\n### Chart Values\n\n[Values files](https://helm.sh/docs/chart_template_guide/values_files/#helm) (`values.yaml`) may be supplied \nwith the `valueYamlFiles` input, accepting [Pulumi Assets](https://www.pulumi.com/docs/concepts/assets-archives/#assets).\n\nA map of chart values may also be supplied with the `values` input, with highest precedence. You're able to use literals,\nnested maps, [Pulumi outputs](https://www.pulumi.com/docs/concepts/inputs-outputs/), and Pulumi assets as values.\nAssets are automatically opened and converted to a string.\n\nNote that the use of expressions (e.g. `--set service.type`) is not supported.\n\n### Chart Dependency Resolution\n\nFor unpacked chart directories, Pulumi automatically rebuilds the dependencies if dependencies are missing \nand a `Chart.lock` file is present (see: [Helm Dependency Build](https://helm.sh/docs/helm/helm_dependency_build/)).\nUse the `dependencyUpdate` input to have Pulumi update the dependencies (see: [Helm Dependency Update](https://helm.sh/docs/helm/helm_dependency_update/)).\n\n### Templating\n\nThe `Chart` resource renders the templates from your chart and then manages the resources directly with the\nPulumi Kubernetes provider. A default namespace is applied based on the `namespace` input, the provider's\nconfigured namespace, and the active Kubernetes context. Use the `skipCrds` option to skip installing the\nCustom Resource Definition (CRD) objects located in the chart's `crds/` special directory.\n\nUse the `postRenderer` input to pipe the rendered manifest through a [post-rendering command](https://helm.sh/docs/topics/advanced/#post-rendering).\n\n### Resource Ordering\n\nSometimes resources must be applied in a specific order. For example, a namespace resource must be\ncreated before any namespaced resources, or a Custom Resource Definition (CRD) must be pre-installed.\n\nPulumi uses heuristics to determine which order to apply and delete objects within the Chart. Pulumi also\nwaits for each object to be fully reconciled, unless `skipAwait` is enabled.\n\nPulumi supports the `config.kubernetes.io/depends-on` annotation to declare an explicit dependency on a given resource.\nThe annotation accepts a list of resource references, delimited by commas. \n\nNote that references to resources outside the Chart aren't supported.\n\n**Resource reference**\n\nA resource reference is a string that uniquely identifies a resource.\n\nIt consists of the group, kind, name, and optionally the namespace, delimited by forward slashes.\n\n| Resource Scope | Format |\n| :--------------- | :--------------------------------------------- |\n| namespace-scoped | `/namespaces///` |\n| cluster-scoped | `//` |\n\nFor resources in the “core” group, the empty string is used instead (for example: `/namespaces/test/Pod/pod-a`).\n\n{{% examples %}}\n## Example Usage\n{{% example %}}\n### Local Chart Directory\n\n```typescript\nimport * as k8s from \"@pulumi/kubernetes\";\n\nconst nginx = new k8s.helm.v4.Chart(\"nginx\", {\n chart: \"./nginx\",\n});\n```\n```python\nimport pulumi\nfrom pulumi_kubernetes.helm.v4 import Chart\n\nnginx = Chart(\"nginx\",\n chart=\"./nginx\"\n)\n```\n```go\npackage main\n\nimport (\n\thelmv4 \"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4\"\n\t\"github.com/pulumi/pulumi/sdk/v3/go/pulumi\"\n)\n\nfunc main() {\n\tpulumi.Run(func(ctx *pulumi.Context) error {\n\t\t_, err := helmv4.NewChart(ctx, \"nginx\", &helmv4.ChartArgs{\n\t\t\tChart: pulumi.String(\"./nginx\"),\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n}\n```\n```csharp\nusing Pulumi;\nusing Pulumi.Kubernetes.Types.Inputs.Helm.V4;\nusing System.Collections.Generic;\n\nreturn await Deployment.RunAsync(() =>\n{\n new Pulumi.Kubernetes.Helm.V4.Chart(\"nginx\", new ChartArgs\n {\n Chart = \"./nginx\"\n });\n return new Dictionary{};\n});\n```\n```java\npackage generated_program;\n\nimport com.pulumi.Pulumi;\nimport com.pulumi.kubernetes.helm.v4.Chart;\nimport com.pulumi.kubernetes.helm.v4.ChartArgs;\n\npublic class App {\n public static void main(String[] args) {\n Pulumi.run(ctx -> {\n var nginx = new Chart(\"nginx\", ChartArgs.builder()\n .chart(\"./nginx\")\n .build());\n });\n }\n}\n```\n```yaml\nname: example\nruntime: yaml\nresources:\n nginx:\n type: kubernetes:helm.sh/v4:Chart\n properties:\n chart: ./nginx\n```\n{{% /example %}}\n{{% example %}}\n### Repository Chart\n\n```typescript\nimport * as k8s from \"@pulumi/kubernetes\";\n\nconst nginx = new k8s.helm.v4.Chart(\"nginx\", {\n chart: \"nginx\",\n repositoryOpts: {\n repo: \"https://charts.bitnami.com/bitnami\",\n },\n});\n```\n```python\nimport pulumi\nfrom pulumi_kubernetes.helm.v4 import Chart,RepositoryOptsArgs\n\nnginx = Chart(\"nginx\",\n chart=\"nginx\",\n repository_opts=RepositoryOptsArgs(\n repo=\"https://charts.bitnami.com/bitnami\",\n )\n)\n```\n```go\npackage main\n\nimport (\n\thelmv4 \"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4\"\n\t\"github.com/pulumi/pulumi/sdk/v3/go/pulumi\"\n)\n\nfunc main() {\n\tpulumi.Run(func(ctx *pulumi.Context) error {\n\t\t_, err := helmv4.NewChart(ctx, \"nginx\", &helmv4.ChartArgs{\n\t\t\tChart: pulumi.String(\"nginx\"),\n\t\t\tRepositoryOpts: &helmv4.RepositoryOptsArgs{\n\t\t\t\tRepo: pulumi.String(\"https://charts.bitnami.com/bitnami\"),\n\t\t\t},\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n}\n```\n```csharp\nusing Pulumi;\nusing Pulumi.Kubernetes.Types.Inputs.Helm.V4;\nusing System.Collections.Generic;\n\nreturn await Deployment.RunAsync(() =>\n{\n new Pulumi.Kubernetes.Helm.V4.Chart(\"nginx\", new ChartArgs\n {\n Chart = \"nginx\",\n RepositoryOpts = new RepositoryOptsArgs\n {\n Repo = \"https://charts.bitnami.com/bitnami\"\n },\n });\n \n return new Dictionary{};\n});\n```\n```java\npackage generated_program;\n\nimport com.pulumi.Pulumi;\nimport com.pulumi.kubernetes.helm.v4.Chart;\nimport com.pulumi.kubernetes.helm.v4.ChartArgs;\nimport com.pulumi.kubernetes.helm.v4.inputs.RepositoryOptsArgs;\n\npublic class App {\n public static void main(String[] args) {\n Pulumi.run(ctx -> {\n var nginx = new Chart(\"nginx\", ChartArgs.builder()\n .chart(\"nginx\")\n .repositoryOpts(RepositoryOptsArgs.builder()\n .repo(\"https://charts.bitnami.com/bitnami\")\n .build())\n .build());\n });\n }\n}\n```\n```yaml\nname: example\nruntime: yaml\nresources:\n nginx:\n type: kubernetes:helm.sh/v4:Chart\n properties:\n chart: nginx\n repositoryOpts:\n repo: https://charts.bitnami.com/bitnami\n```\n{{% /example %}}\n{{% example %}}\n### OCI Chart\n\n```typescript\nimport * as k8s from \"@pulumi/kubernetes\";\n\nconst nginx = new k8s.helm.v4.Chart(\"nginx\", {\n chart: \"oci://registry-1.docker.io/bitnamicharts/nginx\",\n version: \"16.0.7\",\n});\n```\n```python\nimport pulumi\nfrom pulumi_kubernetes.helm.v4 import Chart\n\nnginx = Chart(\"nginx\",\n chart=\"oci://registry-1.docker.io/bitnamicharts/nginx\",\n version=\"16.0.7\",\n)\n```\n```go\npackage main\n\nimport (\n\thelmv4 \"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4\"\n\t\"github.com/pulumi/pulumi/sdk/v3/go/pulumi\"\n)\n\nfunc main() {\n\tpulumi.Run(func(ctx *pulumi.Context) error {\n\t\t_, err := helmv4.NewChart(ctx, \"nginx\", &helmv4.ChartArgs{\n\t\t\tChart: pulumi.String(\"oci://registry-1.docker.io/bitnamicharts/nginx\"),\n\t\t\tVersion: pulumi.String(\"16.0.7\"),\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n}\n```\n```csharp\nusing Pulumi;\nusing Pulumi.Kubernetes.Types.Inputs.Helm.V4;\nusing System.Collections.Generic;\n\nreturn await Deployment.RunAsync(() =>\n{\n new Pulumi.Kubernetes.Helm.V4.Chart(\"nginx\", new ChartArgs\n {\n Chart = \"oci://registry-1.docker.io/bitnamicharts/nginx\",\n Version = \"16.0.7\",\n });\n \n return new Dictionary{};\n});\n```\n```java\npackage generated_program;\n\nimport com.pulumi.Pulumi;\nimport com.pulumi.kubernetes.helm.v4.Chart;\nimport com.pulumi.kubernetes.helm.v4.ChartArgs;\n\npublic class App {\n public static void main(String[] args) {\n Pulumi.run(ctx -> {\n var nginx = new Chart(\"nginx\", ChartArgs.builder()\n .chart(\"oci://registry-1.docker.io/bitnamicharts/nginx\")\n .version(\"16.0.7\")\n .build());\n });\n }\n}\n```\n```yaml\nname: example\nruntime: yaml\nresources:\n nginx:\n type: kubernetes:helm.sh/v4:Chart\n properties:\n chart: oci://registry-1.docker.io/bitnamicharts/nginx\n version: \"16.0.7\"\n```\n{{% /example %}}\n{{% example %}}\n### Chart Values\n\n```typescript\nimport * as pulumi from \"@pulumi/pulumi\";\nimport * as k8s from \"@pulumi/kubernetes\";\n\nconst nginx = new k8s.helm.v4.Chart(\"nginx\", {\n chart: \"nginx\",\n repositoryOpts: {\n repo: \"https://charts.bitnami.com/bitnami\",\n },\n valueYamlFiles: [\n new pulumi.asset.FileAsset(\"./values.yaml\")\n ],\n values: {\n service: {\n type: \"ClusterIP\",\n },\n notes: new pulumi.asset.FileAsset(\"./notes.txt\"),\n },\n});\n```\n```python\n\"\"\"A Kubernetes Python Pulumi program\"\"\"\n\nimport pulumi\nfrom pulumi_kubernetes.helm.v4 import Chart,RepositoryOptsArgs\n\nnginx = Chart(\"nginx\",\n chart=\"nginx\",\n repository_opts=RepositoryOptsArgs(\n repo=\"https://charts.bitnami.com/bitnami\"\n ),\n value_yaml_files=[\n pulumi.FileAsset(\"./values.yaml\")\n ],\n values={\n \"service\": {\n \"type\": \"ClusterIP\"\n },\n \"notes\": pulumi.FileAsset(\"./notes.txt\")\n }\n)\n```\n```go\npackage main\n\nimport (\n\thelmv4 \"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4\"\n\t\"github.com/pulumi/pulumi/sdk/v3/go/pulumi\"\n)\n\nfunc main() {\n\tpulumi.Run(func(ctx *pulumi.Context) error {\n\t\t_, err := helmv4.NewChart(ctx, \"nginx\", &helmv4.ChartArgs{\n\t\t\tChart: pulumi.String(\"nginx\"),\n\t\t\tRepositoryOpts: &helmv4.RepositoryOptsArgs{\n\t\t\t\tRepo: pulumi.String(\"https://charts.bitnami.com/bitnami\"),\n\t\t\t},\n\t\t\tValueYamlFiles: pulumi.AssetOrArchiveArray{\n\t\t\t\tpulumi.NewFileAsset(\"./values.yaml\"),\n\t\t\t},\n\t\t\tValues: pulumi.Map{\n\t\t\t\t\"service\": pulumi.Map{\n\t\t\t\t\t\"type\": pulumi.String(\"ClusterIP\"),\n\t\t\t\t},\n\t\t\t\t\"notes\": pulumi.NewFileAsset(\"./notes.txt\"),\n\t\t\t},\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n}\n```\n```csharp\nusing Pulumi;\nusing Pulumi.Kubernetes.Types.Inputs.Helm.V4;\nusing System.Collections.Generic;\n\nreturn await Deployment.RunAsync(() =>\n{\n new Pulumi.Kubernetes.Helm.V4.Chart(\"nginx\", new ChartArgs\n {\n Chart = \"nginx\",\n RepositoryOpts = new RepositoryOptsArgs\n {\n Repo = \"https://charts.bitnami.com/bitnami\"\n },\n ValueYamlFiles = \n {\n new FileAsset(\"./values.yaml\") \n },\n Values = new InputMap\n {\n [\"service\"] = new InputMap\n {\n [\"type\"] = \"ClusterIP\",\n },\n [\"notes\"] = new FileAsset(\"./notes.txt\")\n },\n });\n \n return new Dictionary{};\n});\n```\n```java\npackage generated_program;\n\nimport java.util.Map;\n\nimport com.pulumi.Pulumi;\nimport com.pulumi.kubernetes.helm.v4.Chart;\nimport com.pulumi.kubernetes.helm.v4.ChartArgs;\nimport com.pulumi.kubernetes.helm.v4.inputs.RepositoryOptsArgs;\nimport com.pulumi.asset.FileAsset;\n\npublic class App {\n public static void main(String[] args) {\n Pulumi.run(ctx -> {\n var nginx = new Chart(\"nginx\", ChartArgs.builder()\n .chart(\"nginx\")\n .repositoryOpts(RepositoryOptsArgs.builder()\n .repo(\"https://charts.bitnami.com/bitnami\")\n .build())\n .valueYamlFiles(new FileAsset(\"./values.yaml\"))\n .values(Map.of(\n \"service\", Map.of(\n \"type\", \"ClusterIP\"),\n \"notes\", new FileAsset(\"./notes.txt\")))\n .build());\n });\n }\n}\n```\n```yaml\nname: example\nruntime: yaml\nresources:\n nginx:\n type: kubernetes:helm.sh/v4:Chart\n properties:\n chart: nginx\n repositoryOpts:\n repo: https://charts.bitnami.com/bitnami\n valueYamlFiles:\n - fn::fileAsset: values.yaml\n values:\n service:\n type: ClusterIP\n notes:\n fn::fileAsset: notes.txt\n```\n{{% /example %}}\n{{% example %}}\n### Chart Namespace\n\n```typescript\nimport * as pulumi from \"@pulumi/pulumi\";\nimport * as k8s from \"@pulumi/kubernetes\";\n\nconst ns = new k8s.core.v1.Namespace(\"nginx\", {\n metadata: { name: \"nginx\" },\n});\nconst nginx = new k8s.helm.v4.Chart(\"nginx\", {\n namespace: ns.metadata.name,\n chart: \"nginx\",\n repositoryOpts: {\n repo: \"https://charts.bitnami.com/bitnami\",\n }\n});\n```\n```python\nimport pulumi\nfrom pulumi_kubernetes.meta.v1 import ObjectMetaArgs\nfrom pulumi_kubernetes.core.v1 import Namespace\nfrom pulumi_kubernetes.helm.v4 import Chart,RepositoryOptsArgs\n\nns = Namespace(\"nginx\",\n metadata=ObjectMetaArgs(\n name=\"nginx\",\n )\n)\nnginx = Chart(\"nginx\",\n namespace=ns.metadata.name,\n chart=\"nginx\",\n repository_opts=RepositoryOptsArgs(\n repo=\"https://charts.bitnami.com/bitnami\",\n )\n)\n```\n```go\npackage main\n\nimport (\n\tcorev1 \"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/core/v1\"\n\thelmv4 \"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4\"\n\tmetav1 \"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/meta/v1\"\n\t\"github.com/pulumi/pulumi/sdk/v3/go/pulumi\"\n)\n\nfunc main() {\n\tpulumi.Run(func(ctx *pulumi.Context) error {\n\t\tns, err := corev1.NewNamespace(ctx, \"nginx\", &corev1.NamespaceArgs{\n\t\t\tMetadata: &metav1.ObjectMetaArgs{Name: pulumi.String(\"nginx\")},\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = helmv4.NewChart(ctx, \"nginx\", &helmv4.ChartArgs{\n Namespace: ns.Metadata.Name(),\n\t\t\tChart: pulumi.String(\"nginx\"),\n\t\t\tRepositoryOpts: &helmv4.RepositoryOptsArgs{\n\t\t\t\tRepo: pulumi.String(\"https://charts.bitnami.com/bitnami\"),\n\t\t\t},\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n}\n```\n```csharp\nusing Pulumi;\nusing Pulumi.Kubernetes.Types.Inputs.Core.V1;\nusing Pulumi.Kubernetes.Types.Inputs.Meta.V1;\nusing Pulumi.Kubernetes.Types.Inputs.Helm.V4;\nusing System.Collections.Generic;\n\nreturn await Deployment.RunAsync(() =>\n{\n var ns = new Pulumi.Kubernetes.Core.V1.Namespace(\"nginx\", new NamespaceArgs\n {\n Metadata = new ObjectMetaArgs{Name = \"nginx\"}\n });\n new Pulumi.Kubernetes.Helm.V4.Chart(\"nginx\", new ChartArgs\n {\n Namespace = ns.Metadata.Apply(m => m.Name),\n Chart = \"nginx\",\n RepositoryOpts = new RepositoryOptsArgs\n {\n Repo = \"https://charts.bitnami.com/bitnami\"\n },\n });\n \n return new Dictionary{};\n});\n```\n```java\npackage generated_program;\n\nimport com.pulumi.Pulumi;\nimport com.pulumi.kubernetes.core.v1.Namespace;\nimport com.pulumi.kubernetes.core.v1.NamespaceArgs;\nimport com.pulumi.kubernetes.helm.v4.Chart;\nimport com.pulumi.kubernetes.helm.v4.ChartArgs;\nimport com.pulumi.kubernetes.helm.v4.inputs.RepositoryOptsArgs;\nimport com.pulumi.kubernetes.meta.v1.inputs.ObjectMetaArgs;\nimport com.pulumi.core.Output;\n\npublic class App {\n public static void main(String[] args) {\n Pulumi.run(ctx -> {\n var ns = new Namespace(\"nginx\", NamespaceArgs.builder()\n .metadata(ObjectMetaArgs.builder()\n .name(\"nginx\")\n .build())\n .build());\n var nginx = new Chart(\"nginx\", ChartArgs.builder()\n .namespace(ns.metadata().apply(m -> Output.of(m.name().get())))\n .chart(\"nginx\")\n .repositoryOpts(RepositoryOptsArgs.builder()\n .repo(\"https://charts.bitnami.com/bitnami\")\n .build())\n .build());\n });\n }\n}\n```\n```yaml\nname: example\nruntime: yaml\nresources:\n ns:\n type: kubernetes:core/v1:Namespace\n properties:\n metadata:\n name: nginx\n nginx:\n type: kubernetes:helm.sh/v4:Chart\n properties:\n namespace: ${ns.metadata.name}\n chart: nginx\n repositoryOpts:\n repo: https://charts.bitnami.com/bitnami\n```\n{{% /example %}}\n{{% /examples %}}\n", + "properties": { + "resources": { + "type": "array", + "items": { + "$ref": "pulumi.json#/Any" + }, + "description": "Resources created by the Chart." + } + }, + "type": "object", + "inputProperties": { + "chart": { + "type": "string", + "description": "Chart name to be installed. A path may be used." + }, + "dependencyUpdate": { + "type": "boolean", + "description": "Run helm dependency update before installing the chart." + }, + "devel": { + "type": "boolean", + "description": "Use chart development versions, too. Equivalent to version '>0.0.0-0'. If `version` is set, this is ignored." + }, + "keyring": { + "$ref": "pulumi.json#/Asset", + "description": "Location of public keys used for verification. Used only if `verify` is true" + }, + "name": { + "type": "string", + "description": "Release name." + }, + "namespace": { + "type": "string", + "description": "Namespace for the release." + }, + "postRenderer": { + "$ref": "#/types/kubernetes:helm.sh/v4:PostRenderer", + "description": "Specification defining the post-renderer to use." + }, + "repositoryOpts": { + "$ref": "#/types/kubernetes:helm.sh/v4:RepositoryOpts", + "description": "Specification defining the Helm chart repository to use." + }, + "resourcePrefix": { + "type": "string", + "description": "An optional prefix for the auto-generated resource names. Example: A resource created with resourcePrefix=\"foo\" would produce a resource named \"foo:resourceName\"." + }, + "skipAwait": { + "type": "boolean", + "description": "By default, the provider waits until all resources are in a ready state before marking the release as successful. Setting this to true will skip such await logic." + }, + "skipCrds": { + "type": "boolean", + "description": "If set, no CRDs will be installed. By default, CRDs are installed if not already present." + }, + "valueYamlFiles": { + "type": "array", + "items": { + "$ref": "pulumi.json#/Asset" + }, + "description": "List of assets (raw yaml files). Content is read and merged with values." + }, + "values": { + "type": "object", + "additionalProperties": { + "$ref": "pulumi.json#/Any" + }, + "description": "Custom values set for the release." + }, + "verify": { + "type": "boolean", + "description": "Verify the chart's integrity." + }, + "version": { + "type": "string", + "description": "Specify the chart version to install. If this is not specified, the latest version is installed." + } + }, + "requiredInputs": [ + "chart" + ], + "isComponent": true + }, "kubernetes:kustomize:Directory": { "description": "Directory is a component representing a collection of resources described by a kustomize directory (kustomization).\n\n{{% examples %}}\n## Example Usage\n{{% example %}}\n### Local Kustomize Directory\n\n```typescript\nimport * as k8s from \"@pulumi/kubernetes\";\n\nconst helloWorld = new k8s.kustomize.Directory(\"helloWorldLocal\", {\n directory: \"./helloWorld\",\n});\n```\n```python\nfrom pulumi_kubernetes.kustomize import Directory\n\nhello_world = Directory(\n \"hello-world-local\",\n directory=\"./helloWorld\",\n)\n```\n```csharp\nusing System.Threading.Tasks;\nusing Pulumi;\nusing Pulumi.Kubernetes.Kustomize;\n\nclass KustomizeStack : Stack\n{\n public KustomizeStack()\n {\n var helloWorld = new Directory(\"helloWorldLocal\", new DirectoryArgs\n {\n Directory = \"./helloWorld\",\n });\n }\n}\n```\n```go\npackage main\n\nimport (\n\t\"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/kustomize\"\n\t\"github.com/pulumi/pulumi/sdk/v3/go/pulumi\"\n)\n\nfunc main() {\n\tpulumi.Run(func(ctx *pulumi.Context) error {\n\t\t_, err := kustomize.NewDirectory(ctx, \"helloWorldLocal\",\n\t\t\tkustomize.DirectoryArgs{\n\t\t\t\tDirectory: pulumi.String(\"./helloWorld\"),\n\t\t\t},\n\t\t)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn nil\n\t})\n}\n```\n{{% /example %}}\n{{% example %}}\n### Kustomize Directory from a Git Repo\n\n```typescript\nimport * as k8s from \"@pulumi/kubernetes\";\n\nconst helloWorld = new k8s.kustomize.Directory(\"helloWorldRemote\", {\n directory: \"https://github.com/kubernetes-sigs/kustomize/tree/v3.3.1/examples/helloWorld\",\n});\n```\n```python\nfrom pulumi_kubernetes.kustomize import Directory\n\nhello_world = Directory(\n \"hello-world-remote\",\n directory=\"https://github.com/kubernetes-sigs/kustomize/tree/v3.3.1/examples/helloWorld\",\n)\n```\n```csharp\nusing System.Threading.Tasks;\nusing Pulumi;\nusing Pulumi.Kubernetes.Kustomize;\n\nclass KustomizeStack : Stack\n{\n public KustomizeStack()\n {\n var helloWorld = new Directory(\"helloWorldRemote\", new DirectoryArgs\n {\n Directory = \"https://github.com/kubernetes-sigs/kustomize/tree/v3.3.1/examples/helloWorld\",\n });\n }\n}\n```\n```go\npackage main\n\nimport (\n\t\"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/kustomize\"\n\t\"github.com/pulumi/pulumi/sdk/v3/go/pulumi\"\n)\n\nfunc main() {\n\tpulumi.Run(func(ctx *pulumi.Context) error {\n\t\t_, err := kustomize.NewDirectory(ctx, \"helloWorldRemote\",\n\t\t\tkustomize.DirectoryArgs{\n\t\t\t\tDirectory: pulumi.String(\"https://github.com/kubernetes-sigs/kustomize/tree/v3.3.1/examples/helloWorld\"),\n\t\t\t},\n\t\t)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn nil\n\t})\n}\n```\n{{% /example %}}\n{{% example %}}\n### Kustomize Directory with Transformations\n\n```typescript\nimport * as k8s from \"@pulumi/kubernetes\";\n\nconst helloWorld = new k8s.kustomize.Directory(\"helloWorldRemote\", {\n directory: \"https://github.com/kubernetes-sigs/kustomize/tree/v3.3.1/examples/helloWorld\",\n transformations: [\n // Make every service private to the cluster, i.e., turn all services into ClusterIP instead of LoadBalancer.\n (obj: any, opts: pulumi.CustomResourceOptions) => {\n if (obj.kind === \"Service\" && obj.apiVersion === \"v1\") {\n if (obj.spec && obj.spec.type && obj.spec.type === \"LoadBalancer\") {\n obj.spec.type = \"ClusterIP\";\n }\n }\n },\n\n // Set a resource alias for a previous name.\n (obj: any, opts: pulumi.CustomResourceOptions) => {\n if (obj.kind === \"Deployment\") {\n opts.aliases = [{ name: \"oldName\" }]\n }\n },\n\n // Omit a resource from the Chart by transforming the specified resource definition to an empty List.\n (obj: any, opts: pulumi.CustomResourceOptions) => {\n if (obj.kind === \"Pod\" && obj.metadata.name === \"test\") {\n obj.apiVersion = \"v1\"\n obj.kind = \"List\"\n }\n },\n ],\n});\n```\n```python\nfrom pulumi_kubernetes.helm.v3 import Chart, ChartOpts, FetchOpts\n\n# Make every service private to the cluster, i.e., turn all services into ClusterIP instead of LoadBalancer.\ndef make_service_private(obj, opts):\n if obj[\"kind\"] == \"Service\" and obj[\"apiVersion\"] == \"v1\":\n try:\n t = obj[\"spec\"][\"type\"]\n if t == \"LoadBalancer\":\n obj[\"spec\"][\"type\"] = \"ClusterIP\"\n except KeyError:\n pass\n\n\n# Set a resource alias for a previous name.\ndef alias(obj, opts):\n if obj[\"kind\"] == \"Deployment\":\n opts.aliases = [\"oldName\"]\n\n\n# Omit a resource from the Chart by transforming the specified resource definition to an empty List.\ndef omit_resource(obj, opts):\n if obj[\"kind\"] == \"Pod\" and obj[\"metadata\"][\"name\"] == \"test\":\n obj[\"apiVersion\"] = \"v1\"\n obj[\"kind\"] = \"List\"\n\n\nhello_world = Directory(\n \"hello-world-remote\",\n directory=\"https://github.com/kubernetes-sigs/kustomize/tree/v3.3.1/examples/helloWorld\",\n transformations=[make_service_private, alias, omit_resource],\n)\n```\n```csharp\nusing System.Collections.Generic;\nusing System.Collections.Immutable;\nusing System.Threading.Tasks;\nusing Pulumi;\nusing Pulumi.Kubernetes.Kustomize;\n\nclass KustomizeStack : Stack\n{\n public KustomizeStack()\n {\n var helloWorld = new Directory(\"helloWorldRemote\", new DirectoryArgs\n {\n Directory = \"https://github.com/kubernetes-sigs/kustomize/tree/v3.3.1/examples/helloWorld\",\n Transformations =\n {\n LoadBalancerToClusterIP,\n ResourceAlias,\n OmitTestPod,\n }\n });\n\n // Make every service private to the cluster, i.e., turn all services into ClusterIP instead of LoadBalancer.\n ImmutableDictionary LoadBalancerToClusterIP(ImmutableDictionary obj, CustomResourceOptions opts)\n {\n if ((string)obj[\"kind\"] == \"Service\" && (string)obj[\"apiVersion\"] == \"v1\")\n {\n var spec = (ImmutableDictionary)obj[\"spec\"];\n if (spec != null && (string)spec[\"type\"] == \"LoadBalancer\")\n {\n return obj.SetItem(\"spec\", spec.SetItem(\"type\", \"ClusterIP\"));\n }\n }\n\n return obj;\n }\n\n // Set a resource alias for a previous name.\n ImmutableDictionary ResourceAlias(ImmutableDictionary obj, CustomResourceOptions opts)\n {\n if ((string)obj[\"kind\"] == \"Deployment\")\n {\n opts.Aliases.Add(new Alias { Name = \"oldName\" });\n }\n\n return obj;\n }\n\n // Omit a resource from the Chart by transforming the specified resource definition to an empty List.\n ImmutableDictionary OmitTestPod(ImmutableDictionary obj, CustomResourceOptions opts)\n {\n var metadata = (ImmutableDictionary)obj[\"metadata\"];\n if ((string)obj[\"kind\"] == \"Pod\" && (string)metadata[\"name\"] == \"test\")\n {\n return new Dictionary\n {\n [\"apiVersion\"] = \"v1\",\n [\"kind\"] = \"List\",\n [\"items\"] = new Dictionary(),\n }.ToImmutableDictionary();\n }\n\n return obj;\n }\n }\n}\n```\n```go\npackage main\n\nimport (\n\t\"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/kustomize\"\n\t\"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/yaml\"\n\t\"github.com/pulumi/pulumi/sdk/v3/go/pulumi\"\n)\n\nfunc main() {\n\tpulumi.Run(func(ctx *pulumi.Context) error {\n\t\t_, err := kustomize.NewDirectory(ctx, \"helloWorldRemote\",\n\t\t\tkustomize.DirectoryArgs{\n\t\t\t\tDirectory: pulumi.String(\"https://github.com/kubernetes-sigs/kustomize/tree/v3.3.1/examples/helloWorld\"),\n\t\t\t\tTransformations: []yaml.Transformation{\n\t\t\t\t\t// Make every service private to the cluster, i.e., turn all services into ClusterIP\n\t\t\t\t\t// instead of LoadBalancer.\n\t\t\t\t\tfunc(state map[string]interface{}, opts ...pulumi.ResourceOption) {\n\t\t\t\t\t\tif state[\"kind\"] == \"Service\" {\n\t\t\t\t\t\t\tspec := state[\"spec\"].(map[string]interface{})\n\t\t\t\t\t\t\tspec[\"type\"] = \"ClusterIP\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\n\t\t\t\t\t// Set a resource alias for a previous name.\n\t\t\t\t\tfunc(state map[string]interface{}, opts ...pulumi.ResourceOption) {\n\t\t\t\t\t\tif state[\"kind\"] == \"Deployment\" {\n\t\t\t\t\t\t\taliases := pulumi.Aliases([]pulumi.Alias{\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tName: pulumi.String(\"oldName\"),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\topts = append(opts, aliases)\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\n\t\t\t\t\t// Omit a resource from the Chart by transforming the specified resource definition\n\t\t\t\t\t// to an empty List.\n\t\t\t\t\tfunc(state map[string]interface{}, opts ...pulumi.ResourceOption) {\n\t\t\t\t\t\tname := state[\"metadata\"].(map[string]interface{})[\"name\"]\n\t\t\t\t\t\tif state[\"kind\"] == \"Pod\" && name == \"test\" {\n\t\t\t\t\t\t\tstate[\"apiVersion\"] = \"core/v1\"\n\t\t\t\t\t\t\tstate[\"kind\"] = \"List\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn nil\n\t})\n}\n```\n{{% /example %}}\n{{% /examples %}}\n", "properties": { diff --git a/provider/go.mod b/provider/go.mod index 9127b51961..5ef03147d6 100644 --- a/provider/go.mod +++ b/provider/go.mod @@ -19,6 +19,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 github.com/onsi/ginkgo/v2 v2.15.0 github.com/onsi/gomega v1.31.0 + github.com/pkg/errors v0.9.1 github.com/pulumi/cloud-ready-checks v1.1.0 github.com/pulumi/pulumi-kubernetes/sdk/v4 v4.0.0 github.com/pulumi/pulumi-kubernetes/tests/v4 v4.0.0-20240302002028-652829a1ed71 @@ -32,6 +33,7 @@ require ( helm.sh/helm/v3 v3.14.3 k8s.io/api v0.30.0 k8s.io/apimachinery v0.30.0 + k8s.io/cli-runtime v0.30.0 k8s.io/client-go v0.30.0 k8s.io/kube-openapi v0.0.0-20240411171206-dc4e619f62f3 k8s.io/kubectl v0.30.0 @@ -216,7 +218,7 @@ require ( k8s.io/apiserver v0.29.0 // indirect k8s.io/component-base v0.30.0 // indirect k8s.io/klog/v2 v2.120.1 // indirect - k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b oras.land/oras-go v1.2.4 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 @@ -292,7 +294,6 @@ require ( github.com/pgavlin/fx v0.1.6 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 // indirect github.com/pulumi/esc v0.6.2 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect @@ -307,7 +308,6 @@ require ( golang.org/x/tools v0.18.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 // indirect - k8s.io/cli-runtime v0.30.0 // indirect lukechampine.com/frand v1.4.2 // indirect sigs.k8s.io/controller-runtime v0.15.0 // indirect ) diff --git a/provider/pkg/gen/examples/overlays/chartV4.md b/provider/pkg/gen/examples/overlays/chartV4.md new file mode 100644 index 0000000000..7cad5a408c --- /dev/null +++ b/provider/pkg/gen/examples/overlays/chartV4.md @@ -0,0 +1,653 @@ +Chart is a component representing a collection of resources described by a Helm Chart. +Helm charts are a popular packaging format for Kubernetes applications, and published +to registries such as [Artifact Hub](https://artifacthub.io/packages/search?kind=0&sort=relevance&page=1). + +Chart does not use Tiller or create a Helm Release; the semantics are equivalent to +running `helm template --dry-run=server` and then using Pulumi to deploy the resulting YAML manifests. +This allows you to apply [Pulumi Transformations](https://www.pulumi.com/docs/concepts/options/transformations/) and +[Pulumi Policies](https://www.pulumi.com/docs/using-pulumi/crossguard/) to the Kubernetes resources. + +You may also want to consider the `Release` resource as an alternative method for managing helm charts. For more +information about the trade-offs between these options, see: [Choosing the right Helm resource for your use case](https://www.pulumi.com/registry/packages/kubernetes/how-to-guides/choosing-the-right-helm-resource-for-your-use-case). + +### Chart Resolution + +The Helm Chart can be fetched from any source that is accessible to the `helm` command line. +The following variations are supported: + +1. By chart reference with repo prefix: `chart: "example/mariadb"` +2. By path to a packaged chart: `chart: "./nginx-1.2.3.tgz"` +3. By path to an unpacked chart directory: `chart: "./nginx"` +4. By absolute URL: `chart: "https://example.com/charts/nginx-1.2.3.tgz"` +5. By chart reference with repo URL: `chart: "nginx", repositoryOpts: { repo: "https://example.com/charts/" }` +6. By OCI registry: `chart: "oci://example.com/charts/nginx", version: "1.2.3"` + +A chart reference is a convenient way of referencing a chart in a chart repository. + +When you use a chart reference with a repo prefix (`example/mariadb`), Pulumi will look in Helm's local configuration +for a chart repository named `example`, and will then look for a chart in that repository whose name is `mariadb`. +It will install the latest stable version of that chart, unless you specify `devel` to also include +development versions (alpha, beta, and release candidate releases), or supply a version number with `version`. + +Use the `verify` and optional `keyring` inputs to enable Chart verification. +By default, Pulumi uses the keyring at `$HOME/.gnupg/pubring.gpg`. See: [Helm Provenance and Integrity](https://helm.sh/docs/topics/provenance/). + +### Chart Values + +[Values files](https://helm.sh/docs/chart_template_guide/values_files/#helm) (`values.yaml`) may be supplied +with the `valueYamlFiles` input, accepting [Pulumi Assets](https://www.pulumi.com/docs/concepts/assets-archives/#assets). + +A map of chart values may also be supplied with the `values` input, with highest precedence. You're able to use literals, +nested maps, [Pulumi outputs](https://www.pulumi.com/docs/concepts/inputs-outputs/), and Pulumi assets as values. +Assets are automatically opened and converted to a string. + +Note that the use of expressions (e.g. `--set service.type`) is not supported. + +### Chart Dependency Resolution + +For unpacked chart directories, Pulumi automatically rebuilds the dependencies if dependencies are missing +and a `Chart.lock` file is present (see: [Helm Dependency Build](https://helm.sh/docs/helm/helm_dependency_build/)). +Use the `dependencyUpdate` input to have Pulumi update the dependencies (see: [Helm Dependency Update](https://helm.sh/docs/helm/helm_dependency_update/)). + +### Templating + +The `Chart` resource renders the templates from your chart and then manages the resources directly with the +Pulumi Kubernetes provider. A default namespace is applied based on the `namespace` input, the provider's +configured namespace, and the active Kubernetes context. Use the `skipCrds` option to skip installing the +Custom Resource Definition (CRD) objects located in the chart's `crds/` special directory. + +Use the `postRenderer` input to pipe the rendered manifest through a [post-rendering command](https://helm.sh/docs/topics/advanced/#post-rendering). + +### Resource Ordering + +Sometimes resources must be applied in a specific order. For example, a namespace resource must be +created before any namespaced resources, or a Custom Resource Definition (CRD) must be pre-installed. + +Pulumi uses heuristics to determine which order to apply and delete objects within the Chart. Pulumi also +waits for each object to be fully reconciled, unless `skipAwait` is enabled. + +Pulumi supports the `config.kubernetes.io/depends-on` annotation to declare an explicit dependency on a given resource. +The annotation accepts a list of resource references, delimited by commas. + +Note that references to resources outside the Chart aren't supported. + +**Resource reference** + +A resource reference is a string that uniquely identifies a resource. + +It consists of the group, kind, name, and optionally the namespace, delimited by forward slashes. + +| Resource Scope | Format | +| :--------------- | :--------------------------------------------- | +| namespace-scoped | `/namespaces///` | +| cluster-scoped | `//` | + +For resources in the “core” group, the empty string is used instead (for example: `/namespaces/test/Pod/pod-a`). + +{{% examples %}} +## Example Usage +{{% example %}} +### Local Chart Directory + +```typescript +import * as k8s from "@pulumi/kubernetes"; + +const nginx = new k8s.helm.v4.Chart("nginx", { + chart: "./nginx", +}); +``` +```python +import pulumi +from pulumi_kubernetes.helm.v4 import Chart + +nginx = Chart("nginx", + chart="./nginx" +) +``` +```go +package main + +import ( + helmv4 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +func main() { + pulumi.Run(func(ctx *pulumi.Context) error { + _, err := helmv4.NewChart(ctx, "nginx", &helmv4.ChartArgs{ + Chart: pulumi.String("./nginx"), + }) + if err != nil { + return err + } + return nil + }) +} +``` +```csharp +using Pulumi; +using Pulumi.Kubernetes.Types.Inputs.Helm.V4; +using System.Collections.Generic; + +return await Deployment.RunAsync(() => +{ + new Pulumi.Kubernetes.Helm.V4.Chart("nginx", new ChartArgs + { + Chart = "./nginx" + }); + return new Dictionary{}; +}); +``` +```java +package generated_program; + +import com.pulumi.Pulumi; +import com.pulumi.kubernetes.helm.v4.Chart; +import com.pulumi.kubernetes.helm.v4.ChartArgs; + +public class App { + public static void main(String[] args) { + Pulumi.run(ctx -> { + var nginx = new Chart("nginx", ChartArgs.builder() + .chart("./nginx") + .build()); + }); + } +} +``` +```yaml +name: example +runtime: yaml +resources: + nginx: + type: kubernetes:helm.sh/v4:Chart + properties: + chart: ./nginx +``` +{{% /example %}} +{{% example %}} +### Repository Chart + +```typescript +import * as k8s from "@pulumi/kubernetes"; + +const nginx = new k8s.helm.v4.Chart("nginx", { + chart: "nginx", + repositoryOpts: { + repo: "https://charts.bitnami.com/bitnami", + }, +}); +``` +```python +import pulumi +from pulumi_kubernetes.helm.v4 import Chart,RepositoryOptsArgs + +nginx = Chart("nginx", + chart="nginx", + repository_opts=RepositoryOptsArgs( + repo="https://charts.bitnami.com/bitnami", + ) +) +``` +```go +package main + +import ( + helmv4 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +func main() { + pulumi.Run(func(ctx *pulumi.Context) error { + _, err := helmv4.NewChart(ctx, "nginx", &helmv4.ChartArgs{ + Chart: pulumi.String("nginx"), + RepositoryOpts: &helmv4.RepositoryOptsArgs{ + Repo: pulumi.String("https://charts.bitnami.com/bitnami"), + }, + }) + if err != nil { + return err + } + return nil + }) +} +``` +```csharp +using Pulumi; +using Pulumi.Kubernetes.Types.Inputs.Helm.V4; +using System.Collections.Generic; + +return await Deployment.RunAsync(() => +{ + new Pulumi.Kubernetes.Helm.V4.Chart("nginx", new ChartArgs + { + Chart = "nginx", + RepositoryOpts = new RepositoryOptsArgs + { + Repo = "https://charts.bitnami.com/bitnami" + }, + }); + + return new Dictionary{}; +}); +``` +```java +package generated_program; + +import com.pulumi.Pulumi; +import com.pulumi.kubernetes.helm.v4.Chart; +import com.pulumi.kubernetes.helm.v4.ChartArgs; +import com.pulumi.kubernetes.helm.v4.inputs.RepositoryOptsArgs; + +public class App { + public static void main(String[] args) { + Pulumi.run(ctx -> { + var nginx = new Chart("nginx", ChartArgs.builder() + .chart("nginx") + .repositoryOpts(RepositoryOptsArgs.builder() + .repo("https://charts.bitnami.com/bitnami") + .build()) + .build()); + }); + } +} +``` +```yaml +name: example +runtime: yaml +resources: + nginx: + type: kubernetes:helm.sh/v4:Chart + properties: + chart: nginx + repositoryOpts: + repo: https://charts.bitnami.com/bitnami +``` +{{% /example %}} +{{% example %}} +### OCI Chart + +```typescript +import * as k8s from "@pulumi/kubernetes"; + +const nginx = new k8s.helm.v4.Chart("nginx", { + chart: "oci://registry-1.docker.io/bitnamicharts/nginx", + version: "16.0.7", +}); +``` +```python +import pulumi +from pulumi_kubernetes.helm.v4 import Chart + +nginx = Chart("nginx", + chart="oci://registry-1.docker.io/bitnamicharts/nginx", + version="16.0.7", +) +``` +```go +package main + +import ( + helmv4 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +func main() { + pulumi.Run(func(ctx *pulumi.Context) error { + _, err := helmv4.NewChart(ctx, "nginx", &helmv4.ChartArgs{ + Chart: pulumi.String("oci://registry-1.docker.io/bitnamicharts/nginx"), + Version: pulumi.String("16.0.7"), + }) + if err != nil { + return err + } + return nil + }) +} +``` +```csharp +using Pulumi; +using Pulumi.Kubernetes.Types.Inputs.Helm.V4; +using System.Collections.Generic; + +return await Deployment.RunAsync(() => +{ + new Pulumi.Kubernetes.Helm.V4.Chart("nginx", new ChartArgs + { + Chart = "oci://registry-1.docker.io/bitnamicharts/nginx", + Version = "16.0.7", + }); + + return new Dictionary{}; +}); +``` +```java +package generated_program; + +import com.pulumi.Pulumi; +import com.pulumi.kubernetes.helm.v4.Chart; +import com.pulumi.kubernetes.helm.v4.ChartArgs; + +public class App { + public static void main(String[] args) { + Pulumi.run(ctx -> { + var nginx = new Chart("nginx", ChartArgs.builder() + .chart("oci://registry-1.docker.io/bitnamicharts/nginx") + .version("16.0.7") + .build()); + }); + } +} +``` +```yaml +name: example +runtime: yaml +resources: + nginx: + type: kubernetes:helm.sh/v4:Chart + properties: + chart: oci://registry-1.docker.io/bitnamicharts/nginx + version: "16.0.7" +``` +{{% /example %}} +{{% example %}} +### Chart Values + +```typescript +import * as pulumi from "@pulumi/pulumi"; +import * as k8s from "@pulumi/kubernetes"; + +const nginx = new k8s.helm.v4.Chart("nginx", { + chart: "nginx", + repositoryOpts: { + repo: "https://charts.bitnami.com/bitnami", + }, + valueYamlFiles: [ + new pulumi.asset.FileAsset("./values.yaml") + ], + values: { + service: { + type: "ClusterIP", + }, + notes: new pulumi.asset.FileAsset("./notes.txt"), + }, +}); +``` +```python +"""A Kubernetes Python Pulumi program""" + +import pulumi +from pulumi_kubernetes.helm.v4 import Chart,RepositoryOptsArgs + +nginx = Chart("nginx", + chart="nginx", + repository_opts=RepositoryOptsArgs( + repo="https://charts.bitnami.com/bitnami" + ), + value_yaml_files=[ + pulumi.FileAsset("./values.yaml") + ], + values={ + "service": { + "type": "ClusterIP" + }, + "notes": pulumi.FileAsset("./notes.txt") + } +) +``` +```go +package main + +import ( + helmv4 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +func main() { + pulumi.Run(func(ctx *pulumi.Context) error { + _, err := helmv4.NewChart(ctx, "nginx", &helmv4.ChartArgs{ + Chart: pulumi.String("nginx"), + RepositoryOpts: &helmv4.RepositoryOptsArgs{ + Repo: pulumi.String("https://charts.bitnami.com/bitnami"), + }, + ValueYamlFiles: pulumi.AssetOrArchiveArray{ + pulumi.NewFileAsset("./values.yaml"), + }, + Values: pulumi.Map{ + "service": pulumi.Map{ + "type": pulumi.String("ClusterIP"), + }, + "notes": pulumi.NewFileAsset("./notes.txt"), + }, + }) + if err != nil { + return err + } + return nil + }) +} +``` +```csharp +using Pulumi; +using Pulumi.Kubernetes.Types.Inputs.Helm.V4; +using System.Collections.Generic; + +return await Deployment.RunAsync(() => +{ + new Pulumi.Kubernetes.Helm.V4.Chart("nginx", new ChartArgs + { + Chart = "nginx", + RepositoryOpts = new RepositoryOptsArgs + { + Repo = "https://charts.bitnami.com/bitnami" + }, + ValueYamlFiles = + { + new FileAsset("./values.yaml") + }, + Values = new InputMap + { + ["service"] = new InputMap + { + ["type"] = "ClusterIP", + }, + ["notes"] = new FileAsset("./notes.txt") + }, + }); + + return new Dictionary{}; +}); +``` +```java +package generated_program; + +import java.util.Map; + +import com.pulumi.Pulumi; +import com.pulumi.kubernetes.helm.v4.Chart; +import com.pulumi.kubernetes.helm.v4.ChartArgs; +import com.pulumi.kubernetes.helm.v4.inputs.RepositoryOptsArgs; +import com.pulumi.asset.FileAsset; + +public class App { + public static void main(String[] args) { + Pulumi.run(ctx -> { + var nginx = new Chart("nginx", ChartArgs.builder() + .chart("nginx") + .repositoryOpts(RepositoryOptsArgs.builder() + .repo("https://charts.bitnami.com/bitnami") + .build()) + .valueYamlFiles(new FileAsset("./values.yaml")) + .values(Map.of( + "service", Map.of( + "type", "ClusterIP"), + "notes", new FileAsset("./notes.txt"))) + .build()); + }); + } +} +``` +```yaml +name: example +runtime: yaml +resources: + nginx: + type: kubernetes:helm.sh/v4:Chart + properties: + chart: nginx + repositoryOpts: + repo: https://charts.bitnami.com/bitnami + valueYamlFiles: + - fn::fileAsset: values.yaml + values: + service: + type: ClusterIP + notes: + fn::fileAsset: notes.txt +``` +{{% /example %}} +{{% example %}} +### Chart Namespace + +```typescript +import * as pulumi from "@pulumi/pulumi"; +import * as k8s from "@pulumi/kubernetes"; + +const ns = new k8s.core.v1.Namespace("nginx", { + metadata: { name: "nginx" }, +}); +const nginx = new k8s.helm.v4.Chart("nginx", { + namespace: ns.metadata.name, + chart: "nginx", + repositoryOpts: { + repo: "https://charts.bitnami.com/bitnami", + } +}); +``` +```python +import pulumi +from pulumi_kubernetes.meta.v1 import ObjectMetaArgs +from pulumi_kubernetes.core.v1 import Namespace +from pulumi_kubernetes.helm.v4 import Chart,RepositoryOptsArgs + +ns = Namespace("nginx", + metadata=ObjectMetaArgs( + name="nginx", + ) +) +nginx = Chart("nginx", + namespace=ns.metadata.name, + chart="nginx", + repository_opts=RepositoryOptsArgs( + repo="https://charts.bitnami.com/bitnami", + ) +) +``` +```go +package main + +import ( + corev1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/core/v1" + helmv4 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4" + metav1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/meta/v1" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +func main() { + pulumi.Run(func(ctx *pulumi.Context) error { + ns, err := corev1.NewNamespace(ctx, "nginx", &corev1.NamespaceArgs{ + Metadata: &metav1.ObjectMetaArgs{Name: pulumi.String("nginx")}, + }) + if err != nil { + return err + } + _, err = helmv4.NewChart(ctx, "nginx", &helmv4.ChartArgs{ + Namespace: ns.Metadata.Name(), + Chart: pulumi.String("nginx"), + RepositoryOpts: &helmv4.RepositoryOptsArgs{ + Repo: pulumi.String("https://charts.bitnami.com/bitnami"), + }, + }) + if err != nil { + return err + } + return nil + }) +} +``` +```csharp +using Pulumi; +using Pulumi.Kubernetes.Types.Inputs.Core.V1; +using Pulumi.Kubernetes.Types.Inputs.Meta.V1; +using Pulumi.Kubernetes.Types.Inputs.Helm.V4; +using System.Collections.Generic; + +return await Deployment.RunAsync(() => +{ + var ns = new Pulumi.Kubernetes.Core.V1.Namespace("nginx", new NamespaceArgs + { + Metadata = new ObjectMetaArgs{Name = "nginx"} + }); + new Pulumi.Kubernetes.Helm.V4.Chart("nginx", new ChartArgs + { + Namespace = ns.Metadata.Apply(m => m.Name), + Chart = "nginx", + RepositoryOpts = new RepositoryOptsArgs + { + Repo = "https://charts.bitnami.com/bitnami" + }, + }); + + return new Dictionary{}; +}); +``` +```java +package generated_program; + +import com.pulumi.Pulumi; +import com.pulumi.kubernetes.core.v1.Namespace; +import com.pulumi.kubernetes.core.v1.NamespaceArgs; +import com.pulumi.kubernetes.helm.v4.Chart; +import com.pulumi.kubernetes.helm.v4.ChartArgs; +import com.pulumi.kubernetes.helm.v4.inputs.RepositoryOptsArgs; +import com.pulumi.kubernetes.meta.v1.inputs.ObjectMetaArgs; +import com.pulumi.core.Output; + +public class App { + public static void main(String[] args) { + Pulumi.run(ctx -> { + var ns = new Namespace("nginx", NamespaceArgs.builder() + .metadata(ObjectMetaArgs.builder() + .name("nginx") + .build()) + .build()); + var nginx = new Chart("nginx", ChartArgs.builder() + .namespace(ns.metadata().apply(m -> Output.of(m.name().get()))) + .chart("nginx") + .repositoryOpts(RepositoryOptsArgs.builder() + .repo("https://charts.bitnami.com/bitnami") + .build()) + .build()); + }); + } +} +``` +```yaml +name: example +runtime: yaml +resources: + ns: + type: kubernetes:core/v1:Namespace + properties: + metadata: + name: nginx + nginx: + type: kubernetes:helm.sh/v4:Chart + properties: + namespace: ${ns.metadata.name} + chart: nginx + repositoryOpts: + repo: https://charts.bitnami.com/bitnami +``` +{{% /example %}} +{{% /examples %}} diff --git a/provider/pkg/gen/overlays.go b/provider/pkg/gen/overlays.go index a55e994b74..b4a67e7500 100644 --- a/provider/pkg/gen/overlays.go +++ b/provider/pkg/gen/overlays.go @@ -130,6 +130,130 @@ var helmV3ChartResource = pschema.ResourceSpec{ }, } +//go:embed examples/overlays/chartV4.md +var helmV4ChartMD string + +var helmV4ChartResource = pschema.ResourceSpec{ + IsComponent: true, + ObjectTypeSpec: pschema.ObjectTypeSpec{ + IsOverlay: false, + Description: helmV4ChartMD, + Properties: map[string]pschema.PropertySpec{ + "resources": { + TypeSpec: pschema.TypeSpec{ + Type: "array", + Items: &pschema.TypeSpec{ + Ref: "pulumi.json#/Any", + }, + }, + Description: "Resources created by the Chart.", + }, + }, + Type: "object", + }, + InputProperties: map[string]pschema.PropertySpec{ + "name": { + TypeSpec: pschema.TypeSpec{ + Type: "string", + }, + Description: "Release name.", + }, + "namespace": { + TypeSpec: pschema.TypeSpec{ + Type: "string", + }, + Description: "Namespace for the release.", + }, + "repositoryOpts": { + TypeSpec: pschema.TypeSpec{ + Ref: "#/types/kubernetes:helm.sh/v4:RepositoryOpts", + }, + Description: "Specification defining the Helm chart repository to use.", + }, + "chart": { + TypeSpec: pschema.TypeSpec{ + Type: "string", + }, + Description: "Chart name to be installed. A path may be used.", + }, + "version": { + TypeSpec: pschema.TypeSpec{ + Type: "string", + }, + Description: "Specify the chart version to install. If this is not specified, the latest version is installed.", + }, + "devel": { + TypeSpec: pschema.TypeSpec{ + Type: "boolean", + }, + Description: "Use chart development versions, too. Equivalent to version '>0.0.0-0'. If `version` is set, this is ignored.", + }, + "dependencyUpdate": { + TypeSpec: pschema.TypeSpec{ + Type: "boolean", + }, + Description: "Run helm dependency update before installing the chart.", + }, + "verify": { + TypeSpec: pschema.TypeSpec{ + Type: "boolean", + }, + Description: "Verify the chart's integrity.", + }, + "keyring": { + TypeSpec: pschema.TypeSpec{ + Ref: "pulumi.json#/Asset", + }, + Description: "Location of public keys used for verification. Used only if `verify` is true", + }, + "valueYamlFiles": { + TypeSpec: pschema.TypeSpec{ + Type: "array", + Items: &pschema.TypeSpec{ + Ref: "pulumi.json#/Asset", + }, + }, + Description: "List of assets (raw yaml files). Content is read and merged with values.", + }, + "values": { + TypeSpec: pschema.TypeSpec{ + Type: "object", + AdditionalProperties: &pschema.TypeSpec{ + Ref: "pulumi.json#/Any", + }, + }, + Description: "Custom values set for the release.", + }, + "skipCrds": { + TypeSpec: pschema.TypeSpec{ + Type: "boolean", + }, + Description: "If set, no CRDs will be installed. By default, CRDs are installed if not already present.", + }, + "postRenderer": { + TypeSpec: pschema.TypeSpec{ + Ref: "#/types/kubernetes:helm.sh/v4:PostRenderer", + }, + Description: "Specification defining the post-renderer to use.", + }, + "skipAwait": { + TypeSpec: pschema.TypeSpec{ + Type: "boolean", + }, + Description: "By default, the provider waits until all resources are in a ready state before marking the release as successful. Setting this to true will skip such await logic.", + }, + "resourcePrefix": { + TypeSpec: pschema.TypeSpec{ + Type: "string", + }, + Description: "An optional prefix for the auto-generated resource names. Example: A resource created with resourcePrefix=\"foo\" would produce a resource named \"foo:resourceName\".", + }, + }, + RequiredInputs: []string{ + "chart", + }, +} + var helmV3FetchOpts = pschema.ComplexTypeSpec{ ObjectTypeSpec: pschema.ObjectTypeSpec{ IsOverlay: true, @@ -287,6 +411,97 @@ var helmV3RepoOpts = pschema.ComplexTypeSpec{ }, } +var helmV4RepoOpts = pschema.ComplexTypeSpec{ + ObjectTypeSpec: pschema.ObjectTypeSpec{ + Description: "Specification defining the Helm chart repository to use.", + Properties: map[string]pschema.PropertySpec{ + "repo": { + TypeSpec: pschema.TypeSpec{ + Type: "string", + }, + Description: "Repository where to locate the requested chart. If is a URL the chart is installed without installing the repository.", + }, + "keyFile": { + TypeSpec: pschema.TypeSpec{ + Ref: "pulumi.json#/Asset", + }, + Description: "The repository's cert key file", + }, + "certFile": { + TypeSpec: pschema.TypeSpec{ + Ref: "pulumi.json#/Asset", + }, + Description: "The repository's cert file", + }, + "caFile": { + TypeSpec: pschema.TypeSpec{ + Ref: "pulumi.json#/Asset", + }, + Description: "The Repository's CA File", + }, + "username": { + TypeSpec: pschema.TypeSpec{ + Type: "string", + }, + Description: "Username for HTTP basic authentication", + }, + "password": { + TypeSpec: pschema.TypeSpec{ + Type: "string", + }, + Secret: true, + Description: "Password for HTTP basic authentication", + }, + }, + Language: map[string]pschema.RawMessage{ + "nodejs": rawMessage(map[string][]string{ + "requiredOutputs": { + "repo", + "keyFile", + "certFile", + "caFile", + "username", + "password", + }}), + }, + Type: "object", + }, +} + +var helmV4PostRenderer = pschema.ComplexTypeSpec{ + ObjectTypeSpec: pschema.ObjectTypeSpec{ + Description: "Specification defining the post-renderer to use.", + Properties: map[string]pschema.PropertySpec{ + "command": { + TypeSpec: pschema.TypeSpec{ + Type: "string", + }, + Description: "Path to an executable to be used for post rendering.", + }, + "args": { + TypeSpec: pschema.TypeSpec{ + Type: "array", + Items: &pschema.TypeSpec{ + Type: "string", + }, + }, + Description: "Arguments to pass to the post-renderer command.", + }, + }, + Language: map[string]pschema.RawMessage{ + "nodejs": rawMessage(map[string][]string{ + "requiredOutputs": { + "command", + "args", + }}), + }, + Type: "object", + Required: []string{ + "command", + }, + }, +} + var helmV3ReleaseStatus = pschema.ComplexTypeSpec{ ObjectTypeSpec: pschema.ObjectTypeSpec{ Required: []string{"status"}, @@ -356,7 +571,7 @@ var kubeClientSettings = pschema.ComplexTypeSpec{ Description: "Options for tuning the Kubernetes client used by a Provider.", Properties: map[string]pschema.PropertySpec{ "burst": { - Description: "Maximum burst for throttle. Default value is 10.", + Description: "Maximum burst for throttle. Default value is 120.", TypeSpec: pschema.TypeSpec{Type: "integer"}, DefaultInfo: &pschema.DefaultSpec{ Environment: []string{ @@ -365,7 +580,7 @@ var kubeClientSettings = pschema.ComplexTypeSpec{ }, }, "qps": { - Description: "Maximum queries per second (QPS) to the API server from this client. Default value is 5.", + Description: "Maximum queries per second (QPS) to the API server from this client. Default value is 50.", TypeSpec: pschema.TypeSpec{Type: "number"}, DefaultInfo: &pschema.DefaultSpec{ Environment: []string{ @@ -433,7 +648,7 @@ var helmReleaseSettings = pschema.ComplexTypeSpec{ "PULUMI_K8S_HELM_REPOSITORY_CACHE", }, }, - Description: "The path to the file containing cached repository indexes.", + Description: "The path to the directory containing cached repository indexes.", TypeSpec: pschema.TypeSpec{Type: "string"}, }, }, @@ -1389,12 +1604,15 @@ func init() { typeOverlays["kubernetes:helm.sh/v3:FetchOpts"] = helmV3FetchOpts typeOverlays["kubernetes:helm.sh/v3:RepositoryOpts"] = helmV3RepoOpts typeOverlays["kubernetes:helm.sh/v3:ReleaseStatus"] = helmV3ReleaseStatus + typeOverlays["kubernetes:helm.sh/v4:PostRenderer"] = helmV4PostRenderer + typeOverlays["kubernetes:helm.sh/v4:RepositoryOpts"] = helmV4RepoOpts typeOverlays["kubernetes:index:KubeClientSettings"] = kubeClientSettings typeOverlays["kubernetes:index:HelmReleaseSettings"] = helmReleaseSettings resourceOverlays["kubernetes:apiextensions.k8s.io:CustomResource"] = apiextentionsCustomResource resourceOverlays["kubernetes:apiextensions.k8s.io:CustomResourcePatch"] = apiextentionsCustomResourcePatch resourceOverlays["kubernetes:helm.sh/v3:Chart"] = helmV3ChartResource + resourceOverlays["kubernetes:helm.sh/v4:Chart"] = helmV4ChartResource resourceOverlays["kubernetes:helm.sh/v3:Release"] = helmV3ReleaseResource resourceOverlays["kubernetes:kustomize:Directory"] = kustomizeDirectoryResource resourceOverlays["kubernetes:yaml:ConfigFile"] = yamlConfigFileResource diff --git a/provider/pkg/gen/schema.go b/provider/pkg/gen/schema.go index 436f732a55..dce53dfea5 100644 --- a/provider/pkg/gen/schema.go +++ b/provider/pkg/gen/schema.go @@ -222,6 +222,7 @@ func PulumiSchema(swagger map[string]any) pschema.PackageSpec { "apiextensions": "ApiExtensions", "helm.sh/v2": "Helm.V2", "helm.sh/v3": "Helm.V3", + "helm.sh/v4": "Helm.V4", "yaml": "Yaml", "yaml/v2": "Yaml.V2", "": "Provider", @@ -229,6 +230,7 @@ func PulumiSchema(swagger map[string]any) pschema.PackageSpec { javaPackages := map[string]string{ "helm.sh/v2": "helm.v2", "helm.sh/v3": "helm.v3", + "helm.sh/v4": "helm.v4", "yaml/v2": "yaml.v2", } modToPkg := map[string]string{ @@ -236,9 +238,11 @@ func PulumiSchema(swagger map[string]any) pschema.PackageSpec { "helm.sh": "helm", "helm.sh/v2": "helm/v2", "helm.sh/v3": "helm/v3", + "helm.sh/v4": "helm/v4", } pkgImportAliases := map[string]string{ "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v3": "helmv3", + "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4": "helmv4", "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/yaml/v2": "yamlv2", } diff --git a/provider/pkg/helm/fake.go b/provider/pkg/helm/fake.go new file mode 100644 index 0000000000..0766f6c8df --- /dev/null +++ b/provider/pkg/helm/fake.go @@ -0,0 +1,135 @@ +// Copyright 2016-2024, 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 helm + +import ( + "context" + + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chartutil" + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/release" +) + +// NewFakeTool creates a new Helm tool with faked execution. +func NewFakeTool(settings *cli.EnvSettings, initActionConfig InitActionConfigF, locateChart LocateChartF, execute ExecuteF) *Tool { + if initActionConfig == nil { + initActionConfig = FakeInitActionConfig(settings.Namespace(), nil) + } + if locateChart == nil { + locateChart = NewFakeLocator("./chart", nil).LocateChart + } + if execute == nil { + execute = NewFakeExecutor().Execute + } + return &Tool{ + EnvSettings: settings, + HelmDriver: "memory", + initActionConfig: initActionConfig, + locateChart: locateChart, + execute: execute, + } +} + +func FakeInitActionConfig(defaultNamespace string, caps *chartutil.Capabilities) InitActionConfigF { + return func(actionConfig *action.Configuration, namespaceOverride string) error { + if namespaceOverride == "" { + namespaceOverride = defaultNamespace + } + if caps == nil { + caps = chartutil.DefaultCapabilities + } + actionConfig.Capabilities = caps + return actionConfig.Init(nil, namespaceOverride, "memory", debug) + } +} + +type FakeLocator struct { + Path string + Err error + + action *action.Install + name string + settings *cli.EnvSettings +} + +func (f *FakeLocator) Action() *action.Install { + return f.action +} + +func (f *FakeLocator) Name() string { + return f.name +} + +func (f *FakeLocator) Settings() *cli.EnvSettings { + return f.settings +} + +func (f *FakeLocator) LocateChart(i *action.Install, name string, settings *cli.EnvSettings) (string, error) { + f.action = i + f.name = name + f.settings = settings + return f.Path, f.Err +} + +func NewFakeLocator(path string, err error) *FakeLocator { + return &FakeLocator{ + Path: path, + Err: err, + } +} + +type FakeExecutor struct { + action *action.Install + chart *chart.Chart + values map[string]interface{} +} + +func NewFakeExecutor() *FakeExecutor { + return &FakeExecutor{} +} + +func (f *FakeExecutor) Action() *action.Install { + return f.action +} + +func (f *FakeExecutor) Chart() *chart.Chart { + return f.chart +} + +func (f *FakeExecutor) Values() map[string]interface{} { + return f.values +} + +func (f *FakeExecutor) Execute(ctx context.Context, i *action.Install, chrt *chart.Chart, vals map[string]interface{}) (*release.Release, error) { + f.action = i + f.chart = chrt + f.values = vals + + // force client-only mode + oldDryRun := i.DryRun + oldDryRunOption := i.DryRunOption + oldClientOnly := i.ClientOnly + defer func() { + i.DryRun = oldDryRun + i.DryRunOption = oldDryRunOption + i.ClientOnly = oldClientOnly + }() + i.DryRun = true + i.DryRunOption = "client" + i.ClientOnly = true + return i.RunWithContext(ctx, chrt, vals) +} diff --git a/provider/pkg/helm/keyring.go b/provider/pkg/helm/keyring.go new file mode 100644 index 0000000000..8989d307e0 --- /dev/null +++ b/provider/pkg/helm/keyring.go @@ -0,0 +1,26 @@ +// 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 helm + +import ( + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" + "helm.sh/helm/v3/pkg/getter" +) + +// LocateKeyring locates a keyring file for Helm from the given asset. +func LocateKeyring(p getter.Providers, asset pulumi.Asset) (string, error) { + path, _, err := downloadAsset(p, asset) + return path, err +} diff --git a/provider/pkg/helm/tool.go b/provider/pkg/helm/tool.go new file mode 100644 index 0000000000..b1f6852010 --- /dev/null +++ b/provider/pkg/helm/tool.go @@ -0,0 +1,516 @@ +/* +Copyright The Helm Authors. + +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 helm contains code vendored from the upstream Helm project. +package helm + +import ( + "context" + "fmt" + "net/url" + "os" + "path/filepath" + "time" + + "github.com/pkg/errors" + "github.com/pulumi/pulumi-kubernetes/provider/v4/pkg/logging" + helmv4 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4" + logger "github.com/pulumi/pulumi/sdk/v3/go/common/util/logging" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/chartutil" + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/downloader" + "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/registry" + "helm.sh/helm/v3/pkg/release" + "k8s.io/client-go/util/homedir" +) + +type InitActionConfigF func(actionConfig *action.Configuration, namespaceOverride string) error + +type LocateChartF func(i *action.Install, name string, settings *cli.EnvSettings) (string, error) + +type ExecuteF func(ctx context.Context, i *action.Install, chrt *chart.Chart, vals map[string]interface{}) (*release.Release, error) + +// Tool for executing Helm commands via the Helm library. +type Tool struct { + EnvSettings *cli.EnvSettings + HelmDriver string + + initActionConfig InitActionConfigF + locateChart LocateChartF + execute ExecuteF +} + +// NewTool creates a new Helm tool with the given environment settings. +func NewTool(settings *cli.EnvSettings) *Tool { + helmDriver := os.Getenv("HELM_DRIVER") + logger.V(6).Infof("initializing Helm tool: driver=%q, settings=%+v", helmDriver, *settings) + + return &Tool{ + EnvSettings: settings, + HelmDriver: helmDriver, + initActionConfig: func(actionConfig *action.Configuration, namespaceOverride string) error { + if namespaceOverride == "" { + namespaceOverride = settings.Namespace() + } + // https://github.com/helm/helm/blob/635b8cf33d25a86131635c32f35b2a76256e40cb/cmd/helm/helm.go#L72-L81 + return actionConfig.Init(settings.RESTClientGetter(), namespaceOverride, helmDriver, debug) + }, + locateChart: func(i *action.Install, name string, settings *cli.EnvSettings) (string, error) { + return i.ChartPathOptions.LocateChart(name, settings) + }, + execute: func(ctx context.Context, i *action.Install, chrt *chart.Chart, vals map[string]interface{}) (*release.Release, error) { + return i.RunWithContext(ctx, chrt, vals) + }, + } +} + +func (t *Tool) AllGetters() getter.Providers { + return getter.All(t.EnvSettings) +} + +// TemplateOrInstallCommand for `helm template` or `helm install`. +type TemplateOrInstallCommand struct { + // Install parameters. + *action.Install + + // Chart is the chart specification, which can be: + // - a path to a local chart directory or archive file + // - a qualified chart reference (e.g., "stable/mariadb") based on the local repository configuration + // - a URL to a remote chart (https://, oci://, file://, etc.) + Chart string + + // Values to be applied to the chart. + Values ValueOpts + + tool *Tool + actionConfig *action.Configuration +} + +func (cmd *TemplateOrInstallCommand) addFlags() { + cmd.addInstallFlags() +} + +func (cmd *TemplateOrInstallCommand) addInstallFlags() { + // https://github.com/helm/helm/blob/14d0c13e9eefff5b4a1b511cf50643529692ec94/cmd/helm/install.go#L176-L203 + client := cmd.Install + client.CreateNamespace = false + client.DryRunOption = "client" + client.Force = false + client.DisableHooks = false + client.Replace = false + client.Timeout = 300 * time.Second + client.Wait = false + client.WaitForJobs = false + client.GenerateName = false + client.NameTemplate = "" + client.Description = "" + client.Devel = false + client.DependencyUpdate = false + client.DisableOpenAPIValidation = false + client.Atomic = false + client.SkipCRDs = false + client.SubNotes = false + client.Labels = nil + client.EnableDNS = false + cmd.addValueOptionsFlags() + cmd.addChartPathOptionsFlags() +} + +func (cmd *TemplateOrInstallCommand) addValueOptionsFlags() { + // https://github.com/helm/helm/blob/14d0c13e9eefff5b4a1b511cf50643529692ec94/cmd/helm/flags.go#L45-L51 + v := cmd.Values + v.Values = map[string]any{} + v.ValuesFiles = []pulumi.Asset{} +} + +func (cmd *TemplateOrInstallCommand) addChartPathOptionsFlags() { + // https://github.com/helm/helm/blob/14d0c13e9eefff5b4a1b511cf50643529692ec94/cmd/helm/flags.go#L54-L66 + c := &cmd.Install.ChartPathOptions + c.Version = "" + c.Verify = false + c.Keyring = defaultKeyring() + c.RepoURL = "" + c.Username = "" + c.Password = "" + c.CertFile = "" + c.KeyFile = "" + c.InsecureSkipTLSverify = false + c.PlainHTTP = false + c.CaFile = "" + c.PassCredentialsAll = false +} + +// TemplateCommand for `helm template`. +type TemplateCommand struct { + TemplateOrInstallCommand + + // https://github.com/helm/helm/blob/635b8cf33d25a86131635c32f35b2a76256e40cb/cmd/helm/template.go#L50-L57 + + Validate bool + IncludeCRDs bool + SkipTests bool + KubeVersion string + ExtraAPIs []string +} + +// Template returns a new `helm template` command. +func (t *Tool) Template() *TemplateCommand { + actionConfig := new(action.Configuration) + + cmd := &TemplateCommand{ + TemplateOrInstallCommand: TemplateOrInstallCommand{ + tool: t, + actionConfig: actionConfig, + Install: action.NewInstall(actionConfig), + Values: ValueOpts{}, + }, + } + + // https://github.com/helm/helm/blob/635b8cf33d25a86131635c32f35b2a76256e40cb/cmd/helm/template.go#L192-L203 + cmd.addFlags() + cmd.Install.OutputDir = "" + cmd.Validate = false + cmd.IncludeCRDs = false + cmd.SkipTests = false + cmd.Install.IsUpgrade = false + cmd.KubeVersion = "" + cmd.ExtraAPIs = []string{} + cmd.Install.UseReleaseName = false + return cmd +} + +// Execute runs the `helm template` command. +func (cmd *TemplateCommand) Execute(ctx context.Context) (*release.Release, error) { + client := cmd.Install + + err := cmd.tool.initActionConfig(cmd.actionConfig, cmd.Namespace) + if err != nil { + return nil, err + } + + // https://github.com/helm/helm/blob/635b8cf33d25a86131635c32f35b2a76256e40cb/cmd/helm/template.go#L68-L74 + if cmd.KubeVersion != "" { + parsedKubeVersion, err := chartutil.ParseKubeVersion(cmd.KubeVersion) + if err != nil { + return nil, fmt.Errorf("invalid kube version '%s': %s", cmd.KubeVersion, err) + } + client.KubeVersion = parsedKubeVersion + } + + // https://github.com/helm/helm/blob/635b8cf33d25a86131635c32f35b2a76256e40cb/cmd/helm/template.go#L76-L81 + registryClient, err := newRegistryClient(cmd.tool.EnvSettings, client.CertFile, client.KeyFile, client.CaFile, + client.InsecureSkipTLSverify, client.PlainHTTP) + if err != nil { + return nil, fmt.Errorf("missing registry client: %w", err) + } + client.SetRegistryClient(registryClient) + + // https://github.com/helm/helm/blob/635b8cf33d25a86131635c32f35b2a76256e40cb/cmd/helm/template.go#L88-L94 + if client.DryRunOption == "" { + client.DryRunOption = "true" + } + client.DryRun = true + // client.ReleaseName = "release-name" + client.Replace = true // Skip the name check + client.ClientOnly = !cmd.Validate + client.APIVersions = chartutil.VersionSet(cmd.ExtraAPIs) + client.IncludeCRDs = cmd.IncludeCRDs + + return cmd.runInstall(ctx) +} + +// runInstall runs the install action. +// https://github.com/helm/helm/blob/14d0c13e9eefff5b4a1b511cf50643529692ec94/cmd/helm/install.go#L221 +func (cmd *TemplateOrInstallCommand) runInstall(ctx context.Context) (*release.Release, error) { + settings := cmd.tool.EnvSettings + client := cmd.Install + valueOpts := cmd.Values + + if client.Version == "" && client.Devel { + debug("setting version to >0.0.0-0") + client.Version = ">0.0.0-0" + } + + releaseName, chart, err := client.NameAndChart([]string{cmd.Chart}) + if err != nil { + return nil, err + } + client.ReleaseName = releaseName + + debug("attempting to resolve the chart %q with version %q", chart, client.Version) + cp, err := cmd.tool.locateChart(client, chart, settings) + if err != nil { + return nil, errors.Wrap(err, "unable to locate the chart") + } + debug("a chart was located at %s", cp) + + p := cmd.tool.AllGetters() + // FUTURE: add a "file:" getter for parity with Pulumi resource package + vals, err := valueOpts.MergeValues(p) + if err != nil { + return nil, errors.Wrap(err, "unable to process the chart values") + } + + // Check chart dependencies to make sure all are present in /charts + chartRequested, err := loader.Load(cp) + if err != nil { + return nil, errors.Wrap(err, "unable to load the chart") + } + + if err := checkIfInstallable(chartRequested); err != nil { + return nil, err + } + + if req := chartRequested.Metadata.Dependencies; req != nil { + // If CheckDependencies returns an error, we have unfulfilled dependencies. + if err := action.CheckDependencies(chartRequested, req); err != nil { + err = errors.Wrap(err, "An error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies") + if client.DependencyUpdate || chartRequested.Lock != nil { + logStream := debugStream() + defer logStream.Close() + + man := &downloader.Manager{ + Out: logStream, + ChartPath: cp, + Keyring: client.ChartPathOptions.Keyring, + SkipUpdate: false, + Getters: p, + RepositoryConfig: settings.RepositoryConfig, + RepositoryCache: settings.RepositoryCache, + Debug: settings.Debug, + RegistryClient: client.GetRegistryClient(), + } + if client.DependencyUpdate { + if err2 := man.Update(); err2 != nil { + debug("unable to update dependencies: %s", err2.Error()) + return nil, err + } + } else if chartRequested.Lock != nil { + // Pulumi behavior: automatically build the dependencies if a lock file is present + if err2 := man.Build(); err2 != nil { + debug("unable to build dependencies: %s", err2.Error()) + return nil, err + } + } + + // Reload the chart with the updated Chart.lock file. + if chartRequested, err = loader.Load(cp); err != nil { + return nil, errors.Wrap(err, "failed reloading chart after repo update") + } + } else { + return nil, err + } + } + } + + if client.Namespace == "" { + client.Namespace = settings.Namespace() + } + + // Validate DryRunOption member is one of the allowed values + if err := validateDryRunOptionFlag(client.DryRunOption); err != nil { + return nil, err + } + + return cmd.tool.execute(ctx, client, chartRequested, vals) +} + +func debug(format string, a ...any) { + logger.V(6).Infof("[helm] %s", fmt.Sprintf(format, a...)) +} + +func debugStream() *logging.LogWriter { + // FUTURE: set log depth + return logging.NewLogWriter(logger.V(6).Infof, logging.WithPrefix("[helm] ")) +} + +// defaultKeyring returns the expanded path to the default keyring. +// https://github.com/helm/helm/blob/635b8cf33d25a86131635c32f35b2a76256e40cb/cmd/helm/root.go#L276-L293 +func defaultKeyring() string { + if v, ok := os.LookupEnv("GNUPGHOME"); ok { + return filepath.Join(v, "pubring.gpg") + } + return filepath.Join(homedir.HomeDir(), ".gnupg", "pubring.gpg") +} + +// newRegistryClient retruns a new registry client +// https://github.com/helm/helm/blob/635b8cf33d25a86131635c32f35b2a76256e40cb/cmd/helm/root.go#L261-L274 +func newRegistryClient(settings *cli.EnvSettings, certFile, keyFile, caFile string, insecureSkipTLSverify, plainHTTP bool) (*registry.Client, error) { + if certFile != "" && keyFile != "" || caFile != "" || insecureSkipTLSverify { + registryClient, err := newRegistryClientWithTLS(settings, certFile, keyFile, caFile, insecureSkipTLSverify) + if err != nil { + return nil, err + } + return registryClient, nil + } + registryClient, err := newDefaultRegistryClient(settings, plainHTTP) + if err != nil { + return nil, err + } + return registryClient, nil +} + +// newDefaultRegistryClient returns a new registry client with default options +// https://github.com/helm/helm/blob/635b8cf33d25a86131635c32f35b2a76256e40cb/cmd/helm/root.go#L276-L293 +func newDefaultRegistryClient(settings *cli.EnvSettings, plainHTTP bool) (*registry.Client, error) { + logStream := debugStream() + opts := []registry.ClientOption{ + registry.ClientOptDebug(settings.Debug), + registry.ClientOptEnableCache(true), + registry.ClientOptWriter(logStream), + registry.ClientOptCredentialsFile(settings.RegistryConfig), + } + if plainHTTP { + opts = append(opts, registry.ClientOptPlainHTTP()) + } + + // Create a new registry client + registryClient, err := registry.NewClient(opts...) + if err != nil { + return nil, err + } + return registryClient, nil +} + +// newRegistryClientWithTLS returns a new registry client with the given TLS options +// https://github.com/helm/helm/blob/635b8cf33d25a86131635c32f35b2a76256e40cb/cmd/helm/root.go#L295-L304 +func newRegistryClientWithTLS(settings *cli.EnvSettings, certFile, keyFile, caFile string, insecureSkipTLSverify bool) (*registry.Client, error) { + logStream := debugStream() + // Create a new registry client + registryClient, err := registry.NewRegistryClientWithTLS(logStream, certFile, keyFile, caFile, insecureSkipTLSverify, + settings.RegistryConfig, settings.Debug, + ) + if err != nil { + return nil, err + } + return registryClient, nil +} + +// checkIfInstallable validates if a chart can be installed +// +// Application chart type is only installable +// https://github.com/helm/helm/blob/635b8cf33d25a86131635c32f35b2a76256e40cb/cmd/helm/install.go#L317-L326 +func checkIfInstallable(ch *chart.Chart) error { + switch ch.Metadata.Type { + case "", "application": + return nil + } + return errors.Errorf("%s charts are not installable", ch.Metadata.Type) +} + +// validateDryRunOptionFlag validates the dry-run flag value +// https://github.com/helm/helm/blob/635b8cf33d25a86131635c32f35b2a76256e40cb/cmd/helm/install.go#L340-L354 +func validateDryRunOptionFlag(dryRunOptionFlagValue string) error { + // Validate dry-run flag value with a set of allowed value + allowedDryRunValues := []string{"false", "true", "none", "client", "server"} + isAllowed := false + for _, v := range allowedDryRunValues { + if dryRunOptionFlagValue == v { + isAllowed = true + break + } + } + if !isAllowed { + return errors.New("Invalid dry-run flag. Flag must one of the following: false, true, none, client, server") + } + return nil +} + +func ApplyRepositoryOpts(cpo *action.ChartPathOptions, p getter.Providers, repoOpts helmv4.RepositoryOpts) error { + if repoOpts.CaFile != nil { + file, _, err := downloadAsset(p, repoOpts.CaFile) + if err != nil { + return fmt.Errorf("cafile: %w", err) + } + cpo.CaFile = file + } + if repoOpts.CertFile != nil { + file, _, err := downloadAsset(p, repoOpts.CertFile) + if err != nil { + return fmt.Errorf("certfile: %w", err) + } + cpo.CertFile = file + } + if repoOpts.KeyFile != nil { + file, _, err := downloadAsset(p, repoOpts.KeyFile) + if err != nil { + return fmt.Errorf("keyfile: %w", err) + } + cpo.KeyFile = file + } + if repoOpts.Username != nil { + cpo.Username = *repoOpts.Username + } + if repoOpts.Password != nil { + cpo.Password = *repoOpts.Password + } + if repoOpts.Repo != nil { + cpo.RepoURL = *repoOpts.Repo + } + return nil +} + +type cleanupF func() error + +// downloadAsset downloads an asset to the local filesystem. +func downloadAsset(p getter.Providers, asset pulumi.AssetOrArchive) (string, cleanupF, error) { + + a, isAsset := asset.(pulumi.Asset) + if !isAsset { + return "", nil, errors.New("expected an asset") + } + makeTemp := func(data []byte) (string, cleanupF, error) { + file, err := os.CreateTemp("", "pulumi-") + if err != nil { + return "", nil, err + } + defer file.Close() + if _, err := file.Write(data); err != nil { + return "", nil, err + } + return file.Name(), func() error { + return os.Remove(file.Name()) + }, err + } + + switch { + case a.Text() != "": + return makeTemp([]byte(a.Text())) + case a.Path() != "": + return a.Path(), func() error { return nil }, nil + case a.URI() != "": + u, err := url.Parse(a.URI()) + if err != nil { + return "", nil, err + } + g, err := p.ByScheme(u.Scheme) + if err != nil { + return "", nil, fmt.Errorf("no protocol handler for uri %q", a.URI()) + } + data, err := g.Get(a.URI(), getter.WithURL(a.URI())) + if err != nil { + return "", nil, fmt.Errorf("failed to read uri %q: %w", a.URI(), err) + } + return makeTemp(data.Bytes()) + default: + return "", nil, errors.New("unrecognized asset type") + } +} diff --git a/provider/pkg/helm/values.go b/provider/pkg/helm/values.go new file mode 100644 index 0000000000..8e11702b65 --- /dev/null +++ b/provider/pkg/helm/values.go @@ -0,0 +1,126 @@ +// 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 helm + +import ( + "fmt" + "net/url" + "os" + + "github.com/pkg/errors" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" + "helm.sh/helm/v3/pkg/getter" + "sigs.k8s.io/yaml" +) + +// ValueOpts handles merging of chart values from various sources. +type ValueOpts struct { + // ValuesFiles is a list of Helm values files encapsulated as Pulumi assets. + ValuesFiles []pulumi.Asset + // Values is a map of Pulumi values. + Values map[string]any +} + +// MergeValues merges the values in Helm's priority order. +func (opts *ValueOpts) MergeValues(p getter.Providers) (map[string]interface{}, error) { + base := map[string]interface{}{} + + // User specified a values files via -f/--values + for _, asset := range opts.ValuesFiles { + currentMap := map[string]interface{}{} + + bytes, err := readAsset(p, asset) + if err != nil { + return nil, err + } + + if err := yaml.Unmarshal(bytes, ¤tMap); err != nil { + return nil, err + } + // Merge with the previous map + base = MergeMaps(base, currentMap) + } + + // User specified a literal value map (possibly containing assets) + values, err := marshalValues(p, opts.Values) + if err != nil { + return nil, err + } + base = MergeMaps(base, values) + + return base, nil +} + +// readAsset reads the content of a Pulumi asset. +func readAsset(p getter.Providers, asset pulumi.Asset) ([]byte, error) { + switch { + case asset.Text() != "": + return []byte(asset.Text()), nil + case asset.Path() != "": + bytes, err := os.ReadFile(asset.Path()) + if err != nil { + return nil, fmt.Errorf("failed to read file %q: %w", asset.Path(), err) + } + return bytes, nil + case asset.URI() != "": + u, err := url.Parse(asset.URI()) + if err != nil { + return nil, err + } + g, err := p.ByScheme(u.Scheme) + if err != nil { + return nil, fmt.Errorf("no protocol handler for uri %q", asset.URI()) + } + data, err := g.Get(asset.URI(), getter.WithURL(asset.URI())) + if err != nil { + return nil, fmt.Errorf("failed to read uri %q: %w", asset.URI(), err) + } + return data.Bytes(), nil + default: + return nil, errors.New("unrecognized asset type") + } +} + +// marshalValues converts Pulumi values to Helm values. +// - Expands assets to their content (to support --set-file). +func marshalValues(p getter.Providers, a map[string]interface{}) (map[string]interface{}, error) { + var err error + out := make(map[string]interface{}, len(a)) + for k, v := range a { + if v, ok := v.(map[string]interface{}); ok { + out[k], err = marshalValues(p, v) + if err != nil { + return nil, err + } + continue + } + if v, ok := v.(pulumi.Asset); ok { + bytes, err := readAsset(p, v) + if err != nil { + return nil, err + } + out[k] = string(bytes) + continue + } + if _, ok := v.(pulumi.Archive); ok { + return nil, errors.New("Archive values are not supported as a Helm value") + } + if _, ok := v.(pulumi.Resource); ok { + return nil, errors.New("Resource values are not supported as a Helm value") + } + out[k] = v + } + return out, nil +} diff --git a/provider/pkg/helm/values_test.go b/provider/pkg/helm/values_test.go new file mode 100644 index 0000000000..0206afaaad --- /dev/null +++ b/provider/pkg/helm/values_test.go @@ -0,0 +1,238 @@ +// 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 helm + +import ( + "bytes" + "os" + "testing" + + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "helm.sh/helm/v3/pkg/getter" +) + +type mockGetter struct { + data []byte + err error +} + +var _ getter.Getter = (*mockGetter)(nil) + +func (m *mockGetter) Get(url string, options ...getter.Option) (*bytes.Buffer, error) { + return bytes.NewBuffer(m.data), m.err +} + +func TestMergeValues(t *testing.T) { + + bitnamiImage := ` +image: + repository: bitnami/nginx + tag: latest +` + + veleroConfiguration := ` +configuration: + backupStorageLocation: + - name: default +` + + tests := []struct { + name string + valuesFiles []pulumi.Asset + values map[string]any + want map[string]interface{} + }{ + { + name: "valueYamlFiles layering", + valuesFiles: []pulumi.Asset{ + pulumi.NewStringAsset(` +image: + repository: bitnami/nginx +`), + pulumi.NewStringAsset(` +image: + tag: "1.25.0" +`), + }, + want: map[string]interface{}{ + "image": map[string]any{ + "tag": "1.25.0", + "repository": "bitnami/nginx", + }, + }, + }, + { + name: "valueYamlFiles with literals", + valuesFiles: []pulumi.Asset{ + pulumi.NewStringAsset(bitnamiImage), + }, + values: map[string]interface{}{ + "image": map[string]any{ + "tag": "patched", + }, + }, + want: map[string]interface{}{ + "image": map[string]any{ + "repository": "bitnami/nginx", + "tag": "patched", + }, + }, + }, + { + name: "overrides: null values", + valuesFiles: []pulumi.Asset{ + pulumi.NewStringAsset(bitnamiImage), + }, + values: map[string]interface{}{ + "image": map[string]any{ + "tag": nil, + }, + }, + want: map[string]interface{}{ + "image": map[string]any{ + "repository": "bitnami/nginx", + "tag": nil, + }, + }, + }, + { + name: "overrides: empty list (#2731)", + valuesFiles: []pulumi.Asset{ + pulumi.NewStringAsset(veleroConfiguration), + }, + values: map[string]interface{}{ + "configuration": map[string]any{ + "backupStorageLocation": []map[string]any{}, + }, + }, + want: map[string]interface{}{ + "configuration": map[string]any{ + "backupStorageLocation": []map[string]any{}, + }, + }, + }, + { + name: "asset-type value", + values: map[string]interface{}{ + "extra": map[string]any{ + "notes": pulumi.NewStringAsset("this is a note"), + }, + }, + want: map[string]interface{}{ + "extra": map[string]any{ + "notes": "this is a note", + }, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + merger := &ValueOpts{ + ValuesFiles: tt.valuesFiles, + Values: tt.values, + } + + actual, err := merger.MergeValues(getter.Providers{}) + require.NoError(t, err) + assert.Equal(t, tt.want, actual) + }) + } +} + +func TestReadAsset(t *testing.T) { + + bitnamiImage := ` +image: + repository: bitnami/nginx + tag: latest +` + bitnamiImageFile, err := os.CreateTemp("", "pulumi-TestReadAsset-*.yaml") + require.NoError(t, err) + _, _ = bitnamiImageFile.WriteString(bitnamiImage) + _ = bitnamiImageFile.Close() + defer os.Remove(bitnamiImageFile.Name()) + + tests := []struct { + name string + asset pulumi.Asset + mockGetter getter.Getter + want []byte + wantErr bool + }{ + { + name: "string asset", + asset: pulumi.NewStringAsset(bitnamiImage), + want: []byte(bitnamiImage), + }, + { + name: "file asset", + asset: pulumi.NewFileAsset(bitnamiImageFile.Name()), + want: []byte(bitnamiImage), + }, + { + name: "file asset", + asset: pulumi.NewFileAsset("nosuchfile"), + wantErr: true, + }, + { + name: "remote asset", + mockGetter: &mockGetter{ + data: []byte(bitnamiImage), + }, + asset: pulumi.NewRemoteAsset("mock://example.com/values.yaml"), + want: []byte(bitnamiImage), + }, + { + name: "remote asset (no protocol handler)", + mockGetter: &mockGetter{ + data: []byte(bitnamiImage), + }, + asset: pulumi.NewRemoteAsset("invalid://example.com/values.yaml"), + wantErr: true, + }, + { + name: "remote asset (remote error)", + mockGetter: &mockGetter{ + err: assert.AnError, + }, + asset: pulumi.NewRemoteAsset("mock://example.com/values.yaml"), + wantErr: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + p := getter.Provider{ + Schemes: []string{"mock"}, + New: func(options ...getter.Option) (getter.Getter, error) { + return tt.mockGetter, nil + }, + } + + actual, err := readAsset(getter.Providers{p}, tt.asset) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + assert.Equal(t, tt.want, actual) + }) + } +} diff --git a/provider/pkg/host/host.go b/provider/pkg/host/host.go index 23242f1c2c..a5b006d533 100644 --- a/provider/pkg/host/host.go +++ b/provider/pkg/host/host.go @@ -23,7 +23,7 @@ import ( // HostClient is the interface that the provider uses to communicate with the Pulumi engine. // -//nolint:golint +//nolint:golint // stutter type HostClient interface { // Log logs a global message, including errors and warnings. Log(context context.Context, sev diag.Severity, urn resource.URN, msg string) error diff --git a/provider/pkg/logging/log_writer.go b/provider/pkg/logging/log_writer.go new file mode 100644 index 0000000000..82777eeb0a --- /dev/null +++ b/provider/pkg/logging/log_writer.go @@ -0,0 +1,120 @@ +// Copyright 2016-2024, 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 logging + +import ( + "bytes" + "io" + "sync" +) + +// logF is an abstract logging function that accepts a format string and arguments. +// The function is expected to write a newline after each message. +type logF func(format string, args ...interface{}) + +// logWriter is an io.Writer that writes to a logging function, buffering as necessary. +type LogWriter struct { + l logF + prefix string + + // Holds buffered text for the next write or flush + // if we haven't yet seen a newline. + buff bytes.Buffer + mu sync.Mutex // guards buff +} + +type Option interface { + apply(*LogWriter) +} + +type prefixOption string + +func (p prefixOption) apply(l *LogWriter) { + l.prefix = string(p) +} + +// WithPrefix prepends the given prefix to each line. +func WithPrefix(prefix string) Option { + return prefixOption(prefix) +} + +// NewLogWriter builds and returns an io.Writer that +// writes messages to the given logging function. +// It ensures that each line is logged separately. +// +// Any trailing buffered text that does not end with a newline +// is flushed when the writer flushes. +// +// The returned writer is safe for concurrent use. +func NewLogWriter(l logF, opts ...Option) *LogWriter { + w := &LogWriter{l: l} + for _, o := range opts { + o.apply(w) + } + return w +} + +var _ io.Writer = (*LogWriter)(nil) + +func (w *LogWriter) Write(bs []byte) (int, error) { + w.mu.Lock() + defer w.mu.Unlock() + + // log adds a newline so we should not write bs as-is. + // Instead, we'll call log one line at a time. + // + // To handle the case when Write is called with a partial line, + // we use a buffer. + total := len(bs) + for len(bs) > 0 { + idx := bytes.IndexByte(bs, '\n') + if idx < 0 { + // No newline. Buffer it for later. + w.buff.Write(bs) + break + } + + var line []byte + line, bs = bs[:idx], bs[idx+1:] + + if w.buff.Len() == 0 { + // Nothing buffered from a prior partial write. + // This is the majority case. + w.l("%s%s", w.prefix, line) + continue + } + + // There's a prior partial write. Join and flush. + w.buff.Write(line) + w.l("%s%s", w.prefix, w.buff.String()) + w.buff.Reset() + } + return total, nil +} + +// flush flushes buffered text, even if it doesn't end with a newline. +func (w *LogWriter) Flush() { + w.mu.Lock() + defer w.mu.Unlock() + if w.buff.Len() > 0 { + w.l("%s%s", w.prefix, w.buff.String()) + w.buff.Reset() + } +} + +func (w *LogWriter) Close() error { + w.Flush() + return nil +} diff --git a/provider/pkg/provider/helm/v4/chart.go b/provider/pkg/provider/helm/v4/chart.go new file mode 100644 index 0000000000..c3aa3747c1 --- /dev/null +++ b/provider/pkg/provider/helm/v4/chart.go @@ -0,0 +1,281 @@ +// Copyright 2016-2024, 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 v4 + +import ( + "context" + "fmt" + + kubehelm "github.com/pulumi/pulumi-kubernetes/provider/v4/pkg/helm" + providerresource "github.com/pulumi/pulumi-kubernetes/provider/v4/pkg/provider/resource" + provideryamlv2 "github.com/pulumi/pulumi-kubernetes/provider/v4/pkg/provider/yaml/v2" + helmv4 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi/internals" + pulumiprovider "github.com/pulumi/pulumi/sdk/v3/go/pulumi/provider" + helmkube "helm.sh/helm/v3/pkg/kube" + "helm.sh/helm/v3/pkg/postrender" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +type toolF func() *kubehelm.Tool + +type ChartProvider struct { + opts *providerresource.ResourceProviderOptions + tool toolF +} + +type ChartArgs struct { + Name pulumi.StringInput `pulumi:"name,optional"` + Namespace pulumi.StringInput `pulumi:"namespace,optional"` + Chart pulumi.StringInput `pulumi:"chart"` + Version pulumi.StringInput `pulumi:"version,optional"` + Devel pulumi.BoolInput `pulumi:"devel,optional"` + RepositoryOpts helmv4.RepositoryOptsInput `pulumi:"repositoryOpts,optional"` + DependencyUpdate pulumi.BoolInput `pulumi:"dependencyUpdate,optional"` + Verify pulumi.BoolInput `pulumi:"verify,optional"` + Keyring pulumi.AssetInput `pulumi:"keyring,optional"` + + Values pulumi.MapInput `pulumi:"values,optional"` + ValuesFiles pulumi.AssetArrayInput `pulumi:"valueYamlFiles,optional"` + SkipCrds pulumi.BoolInput `pulumi:"skipCrds,optional"` + PostRenderer helmv4.PostRendererInput `pulumi:"postRenderer,optional"` + + ResourcePrefix pulumi.StringInput `pulumi:"resourcePrefix,optional"` + SkipAwait pulumi.BoolInput `pulumi:"skipAwait,optional"` +} + +type chartArgs struct { + Name string + Namespace string + Chart string + Version string + Devel bool + RepositoryOpts helmv4.RepositoryOpts + DependencyUpdate bool + Verify bool + Keyring pulumi.Asset + + Values map[string]any + ValuesFiles []pulumi.Asset + SkipCrds bool + PostRenderer *helmv4.PostRenderer + + ResourcePrefix *string + SkipAwait bool +} + +func unwrapChartArgs(ctx context.Context, args *ChartArgs) (*chartArgs, internals.UnsafeAwaitOutputResult, error) { + result, err := internals.UnsafeAwaitOutput(ctx, pulumi.All( + args.Name, args.Namespace, + args.Chart, args.Version, args.Devel, args.RepositoryOpts, args.DependencyUpdate, args.Verify, args.Keyring, + args.Values, args.ValuesFiles, args.SkipCrds, args.PostRenderer, + args.ResourcePrefix, args.SkipAwait)) + if err != nil || !result.Known { + return nil, result, err + } + resolved := result.Value.([]any) + pop := func() (r any) { + r, resolved = resolved[0], resolved[1:] + return + } + + r := &chartArgs{} + r.Name, _ = pop().(string) + r.Namespace, _ = pop().(string) + r.Chart, _ = pop().(string) + r.Version, _ = pop().(string) + r.Devel, _ = pop().(bool) + r.RepositoryOpts, _ = pop().(helmv4.RepositoryOpts) + r.DependencyUpdate, _ = pop().(bool) + r.Verify, _ = pop().(bool) + r.Keyring, _ = pop().(pulumi.Asset) + + r.Values, _ = pop().(map[string]any) + r.ValuesFiles, _ = pop().([]pulumi.Asset) + r.SkipCrds, _ = pop().(bool) + if v, ok := pop().(helmv4.PostRenderer); ok { + r.PostRenderer = &v + } + + if v, ok := pop().(string); ok { + r.ResourcePrefix = &v + } + r.SkipAwait, _ = pop().(bool) + + return r, result, nil +} + +type ChartState struct { + pulumi.ResourceState + Resources pulumi.ArrayOutput `pulumi:"resources"` +} + +var _ providerresource.ResourceProvider = &ChartProvider{} + +func NewChartProvider(opts *providerresource.ResourceProviderOptions) providerresource.ResourceProvider { + return &ChartProvider{ + opts: opts, + tool: func() *kubehelm.Tool { + return kubehelm.NewTool(opts.HelmOptions.EnvSettings) + }, + } +} + +func (r *ChartProvider) Construct(ctx *pulumi.Context, typ, name string, inputs pulumiprovider.ConstructInputs, options pulumi.ResourceOption) (*pulumiprovider.ConstructResult, error) { + comp := &ChartState{} + err := ctx.RegisterComponentResource(typ, name, comp, options) + if err != nil { + return nil, err + } + + args := &ChartArgs{} + if err := inputs.CopyTo(args); err != nil { + return nil, fmt.Errorf("setting args: %w", err) + } + + // Unpack the resolved inputs. + chartArgs, result, err := unwrapChartArgs(ctx.Context(), args) + if err != nil { + return nil, fmt.Errorf("unwrapping args: %w", err) + } + if !result.Known { + _ = ctx.Log.Warn("Input properties have unknown values. Preview is incomplete.", &pulumi.LogArgs{ + Resource: comp, + }) + r, err := pulumiprovider.NewConstructResult(comp) + return r, err + } + if chartArgs.Name == "" { + chartArgs.Name = name + } + if chartArgs.ResourcePrefix == nil { + // use the name of the Chart as the resource prefix to ensure uniqueness + // across multiple instances of the component resource. + chartArgs.ResourcePrefix = &name + } + + // Prepare the `helm template` command + tool := r.tool() + tool.HelmDriver = r.opts.HelmOptions.HelmDriver + p := tool.AllGetters() + cmd := tool.Template() + + cmd.Validate = true + cmd.DryRun = true + cmd.DryRunOption = "server" + + cmd.Chart = chartArgs.Chart + cmd.Version = chartArgs.Version + cmd.Devel = chartArgs.Devel + if err = kubehelm.ApplyRepositoryOpts(&cmd.ChartPathOptions, p, chartArgs.RepositoryOpts); err != nil { + return nil, fmt.Errorf("repositoryOpts: %w", err) + } + cmd.DependencyUpdate = chartArgs.DependencyUpdate + cmd.Verify = chartArgs.Verify + + if chartArgs.Keyring != nil { + keyring, err := kubehelm.LocateKeyring(p, chartArgs.Keyring) + if err != nil { + return nil, fmt.Errorf("keyring: %w", err) + } + cmd.Keyring = keyring + } + + cmd.Values.Values = chartArgs.Values + cmd.Values.ValuesFiles = chartArgs.ValuesFiles + cmd.IncludeCRDs = !chartArgs.SkipCrds + cmd.DisableHooks = true + cmd.ReleaseName = chartArgs.Name + cmd.Namespace = chartArgs.Namespace + + if chartArgs.PostRenderer != nil { + postrenderer, err := postrender.NewExec(chartArgs.PostRenderer.Command, chartArgs.PostRenderer.Args...) + if err != nil { + return nil, err + } + cmd.PostRenderer = postrenderer + } + + // Execute the Helm command + release, err := cmd.Execute(ctx.Context()) + if err != nil { + return nil, err + } + + if release.Chart.Metadata.Deprecated { + _ = ctx.Log.Warn(fmt.Sprintf("Using a deprecated Helm chart (%s)", release.Chart.Name()), &pulumi.LogArgs{ + Resource: comp, + }) + } + + // Parse the YAML file into an array of Kubernetes objects. + parseOpts := provideryamlv2.ParseOptions{ + YAML: release.Manifest, + } + objs, err := provideryamlv2.Parse(ctx.Context(), parseOpts) + if err != nil { + return nil, err + } + + // Normalize the objects (apply a default namespace, etc.) + ns := chartArgs.Namespace + if ns == "" { + ns = r.opts.DefaultNamespace + } + objs, err = provideryamlv2.Normalize(objs, ns, r.opts.ClientSet) + if err != nil { + return nil, err + } + + // Register the objects as Pulumi resources. + registerOpts := provideryamlv2.RegisterOptions{ + Objects: objs, + ResourcePrefix: *chartArgs.ResourcePrefix, + SkipAwait: chartArgs.SkipAwait, + ResourceOptions: []pulumi.ResourceOption{pulumi.Parent(comp)}, + PreRegisterF: func(ctx *pulumi.Context, apiVersion, kind, resourceName string, obj *unstructured.Unstructured, + resourceOpts []pulumi.ResourceOption) (*unstructured.Unstructured, []pulumi.ResourceOption) { + return preregister(ctx, comp, obj, resourceOpts) + }, + } + resources, err := provideryamlv2.Register(ctx, registerOpts) + if err != nil { + return nil, err + } + comp.Resources = resources + + return pulumiprovider.NewConstructResult(comp) +} + +func preregister(ctx *pulumi.Context, comp *ChartState, obj *unstructured.Unstructured, + resourceOpts []pulumi.ResourceOption) (*unstructured.Unstructured, []pulumi.ResourceOption) { + + // Implement support for Helm resource policies. + // https://helm.sh/docs/howto/charts_tips_and_tricks/#tell-helm-not-to-uninstall-a-resource + policy, hasPolicy, err := unstructured.NestedString(obj.Object, "metadata", "annotations", helmkube.ResourcePolicyAnno) + if err == nil && hasPolicy { + switch policy { + case helmkube.KeepPolicy: + resourceOpts = append(resourceOpts, pulumi.RetainOnDelete(true)) + default: + _ = ctx.Log.Warn(fmt.Sprintf("Unsupported Helm resource policy %q", policy), &pulumi.LogArgs{ + Resource: comp, + }) + } + } + + return obj, resourceOpts +} diff --git a/provider/pkg/provider/helm/v4/chart_test.go b/provider/pkg/provider/helm/v4/chart_test.go new file mode 100644 index 0000000000..6135bcebae --- /dev/null +++ b/provider/pkg/provider/helm/v4/chart_test.go @@ -0,0 +1,449 @@ +// Copyright 2016-2024, 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 v4 + +import ( + "context" + "os" + "os/exec" + "path/filepath" + + . "github.com/onsi/ginkgo/v2" //nolint:golint // dot-imports + . "github.com/onsi/gomega" //nolint:golint // dot-imports + . "github.com/onsi/gomega/gstruct" //nolint:golint // dot-imports + "github.com/pulumi/pulumi-kubernetes/provider/v4/pkg/clients/fake" + kubehelm "github.com/pulumi/pulumi-kubernetes/provider/v4/pkg/helm" + providerresource "github.com/pulumi/pulumi-kubernetes/provider/v4/pkg/provider/resource" + . "github.com/pulumi/pulumi-kubernetes/tests/v4/gomega" + "github.com/pulumi/pulumi/sdk/v3/go/common/resource" + "github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin" + pulumiprovider "github.com/pulumi/pulumi/sdk/v3/go/pulumi/provider" + pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" + "helm.sh/helm/v3/pkg/chartutil" + "helm.sh/helm/v3/pkg/cli" +) + +var _ = Describe("Construct", func() { + var tc *componentProviderTestContext + var opts *providerresource.ResourceProviderOptions + var req *pulumirpc.ConstructRequest + var inputs resource.PropertyMap + var initActionConfig kubehelm.InitActionConfigF + var locator *kubehelm.FakeLocator + var executor *kubehelm.FakeExecutor + var k *ChartProvider + + BeforeEach(func() { + tc = newTestContext(GinkgoTB()) + + opts = &providerresource.ResourceProviderOptions{} + opts.ClientSet, _, _, _ = fake.NewSimpleDynamicClient() + opts.DefaultNamespace = "default" + opts.HelmOptions = &providerresource.HelmOptions{ + SuppressHelmHookWarnings: false, + EnvSettings: cli.New(), + } + opts.HelmOptions.EnvSettings.SetNamespace("default") + + // initialize the ConstructRequest to be customized in nested BeforeEach blocks + req = tc.NewConstructRequest() + req.Type = "kubernetes:helm/v4:Chart" + req.Name = "test" + + // initialize the input PropertyMap to be serialized into the request in JustBeforeEach + inputs = make(resource.PropertyMap) + inputs["chart"] = resource.NewStringProperty("reference") + + // configure the fake Helm tool + initActionConfig = kubehelm.FakeInitActionConfig("default", chartutil.DefaultCapabilities) + locator = kubehelm.NewFakeLocator("./testdata/reference", nil) + executor = kubehelm.NewFakeExecutor() + }) + + JustBeforeEach(func() { + var err error + k = &ChartProvider{ + opts: opts, + tool: func() *kubehelm.Tool { + // make a fake tool for testing purposes + return kubehelm.NewFakeTool(opts.HelmOptions.EnvSettings, initActionConfig, locator.LocateChart, executor.Execute) + }, + } + req.Inputs, err = plugin.MarshalProperties(inputs, plugin.MarshalOptions{ + Label: "inputs", KeepSecrets: true, KeepResources: true, KeepUnknowns: true, KeepOutputValues: true, + }) + Expect(err).ShouldNot(HaveOccurred()) + }) + + It("should register a component resource", func() { + resp, err := pulumiprovider.Construct(context.Background(), req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + Expect(resp.Urn).Should(Equal("urn:pulumi:stack::project::kubernetes:helm/v4:Chart::test")) + + Expect(tc.monitor.Resources()).To(MatchKeys(IgnoreExtras, Keys{ + "urn:pulumi:stack::project::kubernetes:helm/v4:Chart::test": MatchProps(IgnoreExtras, Props{ + "id": MatchValue("test"), + }), + })) + }) + + Describe("Preview", func() { + Context("when the input value(s) are unknown", func() { + BeforeEach(func() { + req.DryRun = true + inputs["chart"] = resource.MakeComputed(resource.NewStringProperty("")) + }) + + It("should emit a warning", func(ctx context.Context) { + _, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + Expect(tc.engine.Logs()).To(HaveLen(1)) + }) + + It("should provide a 'resources' output property", func(ctx context.Context) { + resp, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + outputs := unmarshalProperties(GinkgoTB(), resp.State) + Expect(outputs).To(MatchProps(IgnoreExtras, Props{ + "resources": BeComputed(), + })) + }) + }) + }) + + Describe("Connectivity", func() { + It("should use server-side dry-run mode", func(ctx context.Context) { + _, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + Expect(executor.Action().DryRun).To(BeTrue()) + Expect(executor.Action().DryRunOption).To(Equal("server")) + Expect(executor.Action().ClientOnly).To(BeFalse()) + }) + }) + + Describe("Chart Resolution", func() { + Describe("Chart", func() { + BeforeEach(func() { + inputs["chart"] = resource.NewStringProperty("reference") + }) + It("should configure the name", func(ctx context.Context) { + _, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + Expect(locator.Name()).To(Equal("reference")) + }) + }) + + Describe("Version", func() { + BeforeEach(func() { + inputs["version"] = resource.NewStringProperty("1.0.0") + }) + It("should configure the version", func(ctx context.Context) { + _, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + Expect(locator.Action().Version).To(Equal("1.0.0")) + }) + }) + + Describe("Devel", func() { + BeforeEach(func() { + inputs["devel"] = resource.NewBoolProperty(true) + }) + It("should enable the devel flag", func(ctx context.Context) { + _, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + Expect(locator.Action().Devel).To(BeTrue()) + }) + }) + + Describe("Verify", func() { + BeforeEach(func() { + inputs["verify"] = resource.NewBoolProperty(true) + }) + It("should enable the verify flag", func(ctx context.Context) { + _, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + Expect(locator.Action().Verify).To(BeTrue()) + }) + }) + + Describe("Keyring", func() { + var pub *resource.Asset + BeforeEach(func() { + var err error + pub, err = resource.NewPathAsset("./testdata/pubring.gpg") + Expect(err).ShouldNot(HaveOccurred()) + + inputs["verify"] = resource.NewBoolProperty(true) + inputs["keyring"] = resource.NewAssetProperty(pub) + }) + It("should configure the keyring", func(ctx context.Context) { + _, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + Expect(locator.Action().Keyring).To(Equal(pub.Path)) + }) + }) + + Describe("DependencyUpdate", func() { + BeforeEach(func() { + inputs["dependencyUpdate"] = resource.NewBoolProperty(true) + }) + It("should enable the dependencyUpdate flag", func(ctx context.Context) { + _, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + Expect(locator.Action().DependencyUpdate).To(BeTrue()) + }) + }) + }) + + Describe("Values", func() { + Describe("Literals", func() { + BeforeEach(func() { + inputs["values"] = resource.NewObjectProperty(resource.NewPropertyMapFromMap(map[string]any{ + "fullnameOverride": "overridden", + })) + }) + It("should configure the values", func(ctx context.Context) { + _, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + Expect(executor.Values()).To(HaveKeyWithValue("fullnameOverride", "overridden")) + }) + }) + + Describe("Values Files", func() { + var valuesFile *resource.Asset + BeforeEach(func() { + var err error + valuesFile, err = resource.NewTextAsset("fullnameOverride: overridden") + Expect(err).ShouldNot(HaveOccurred()) + + inputs["valueYamlFiles"] = resource.NewArrayProperty([]resource.PropertyValue{resource.NewAssetProperty(valuesFile)}) + }) + It("should configure the values", func(ctx context.Context) { + _, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + Expect(executor.Values()).To(HaveKeyWithValue("fullnameOverride", "overridden")) + }) + }) + }) + + Describe("Templating", func() { + Describe("Namespacing", func() { + Context("by default", func() { + It("should use the context namespace", func(ctx context.Context) { + resp, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + Expect(executor.Action().Namespace).To(Equal("default")) + outputs := unmarshalProperties(GinkgoTB(), resp.State) + Expect(outputs).To(MatchProps(IgnoreExtras, Props{ + "resources": MatchArrayValue(ContainElements( + MatchResourceReferenceValue("urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:apiextensions.k8s.io/v1:CustomResourceDefinition::test:crontabs.stable.example.com", "test:crontabs.stable.example.com"), + MatchResourceReferenceValue("urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:core/v1:ServiceAccount::test:default/test-reference", "test:default/test-reference"), + MatchResourceReferenceValue("urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:core/v1:Service::test:default/test-reference", "test:default/test-reference"), + )), + })) + }) + }) + + Context("given a provider namespace", func() { + BeforeEach(func() { + opts.DefaultNamespace = "provider" + opts.HelmOptions.EnvSettings.SetNamespace("provider") + }) + It("should use the provider's namespace", func(ctx context.Context) { + resp, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + Expect(executor.Action().Namespace).To(Equal("provider")) + outputs := unmarshalProperties(GinkgoTB(), resp.State) + Expect(outputs).To(MatchProps(IgnoreExtras, Props{ + "resources": MatchArrayValue(ContainElements( + MatchResourceReferenceValue("urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:apiextensions.k8s.io/v1:CustomResourceDefinition::test:crontabs.stable.example.com", "test:crontabs.stable.example.com"), + MatchResourceReferenceValue("urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:core/v1:ServiceAccount::test:provider/test-reference", "test:provider/test-reference"), + MatchResourceReferenceValue("urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:core/v1:Service::test:provider/test-reference", "test:provider/test-reference"), + )), + })) + }) + }) + + Context("given a release namespace", func() { + BeforeEach(func() { + inputs["namespace"] = resource.NewStringProperty("release") + }) + It("should use the release namespace", func(ctx context.Context) { + resp, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + Expect(executor.Action().Namespace).To(Equal("release")) + outputs := unmarshalProperties(GinkgoTB(), resp.State) + Expect(outputs).To(MatchProps(IgnoreExtras, Props{ + "resources": MatchArrayValue(ContainElements( + MatchResourceReferenceValue("urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:apiextensions.k8s.io/v1:CustomResourceDefinition::test:crontabs.stable.example.com", "test:crontabs.stable.example.com"), + MatchResourceReferenceValue("urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:core/v1:ServiceAccount::test:release/test-reference", "test:release/test-reference"), + MatchResourceReferenceValue("urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:core/v1:Service::test:release/test-reference", "test:release/test-reference"), + )), + })) + }) + }) + }) + + Describe("Release Name", func() { + It("should use the component name by default", func(ctx context.Context) { + _, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + Expect(executor.Action().ReleaseName).To(Equal("test")) + }) + + Context("given a release name", func() { + BeforeEach(func() { + inputs["name"] = resource.NewStringProperty("release") + }) + It("should use the release name (instead of the component name)", func(ctx context.Context) { + _, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + Expect(executor.Action().ReleaseName).To(Equal("release")) + }) + }) + }) + + Describe("Skip CRDs", func() { + Context("given skipCrds", func() { + BeforeEach(func() { + inputs["skipCrds"] = resource.NewBoolProperty(true) + }) + It("should not produce CRDs from the 'crds/' directory of the chart", func(ctx context.Context) { + resp, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + outputs := unmarshalProperties(GinkgoTB(), resp.State) + Expect(outputs).To(MatchProps(IgnoreExtras, Props{ + "resources": MatchArrayValue(ContainElements( + MatchResourceReferenceValue("urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:core/v1:ServiceAccount::test:default/test-reference", "test:default/test-reference"), + MatchResourceReferenceValue("urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:core/v1:Service::test:default/test-reference", "test:default/test-reference"), + )), + })) + Expect(outputs).To(MatchProps(IgnoreExtras, Props{ + "resources": MatchArrayValue(Not(ContainElement( + MatchResourceReferenceValue("urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:apiextensions.k8s.io/v1:CustomResourceDefinition::test:crontabs.stable.example.com", "test:crontabs.stable.example.com"), + ))), + })) + }) + }) + }) + + Describe("Post Renderer", func() { + Context("given a postRenderer", func() { + var tempdir string + BeforeEach(func() { + _, err := exec.LookPath("touch") + if err != nil { + Skip("touch command is not available") + } + tempdir, err = os.MkdirTemp("", "test") + Expect(err).ShouldNot(HaveOccurred()) + DeferCleanup(func() { + os.RemoveAll(tempdir) + }) + inputs["postRenderer"] = resource.NewObjectProperty(resource.PropertyMap{ + "command": resource.NewStringProperty("touch"), + "args": resource.NewArrayProperty([]resource.PropertyValue{resource.NewStringProperty(filepath.Join(tempdir, "touched.txt"))}), + }) + }) + It("should run the postrender command", func(ctx context.Context) { + resp, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + outputs := unmarshalProperties(GinkgoTB(), resp.State) + Expect(outputs).To(MatchProps(IgnoreExtras, Props{ + "resources": MatchArrayValue(BeEmpty()), + })) + _, err = os.Stat(filepath.Join(tempdir, "touched.txt")) + Expect(err).ShouldNot(HaveOccurred()) + }) + }) + }) + }) + + Describe("Resource Registration", func() { + It("should provide a 'resources' output property", func(ctx context.Context) { + resp, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + outputs := unmarshalProperties(GinkgoTB(), resp.State) + Expect(outputs).To(MatchProps(IgnoreExtras, Props{ + "resources": MatchArrayValue(ConsistOf( + MatchResourceReferenceValue("urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:apiextensions.k8s.io/v1:CustomResourceDefinition::test:crontabs.stable.example.com", "test:crontabs.stable.example.com"), + MatchResourceReferenceValue("urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:core/v1:ServiceAccount::test:default/test-reference", "test:default/test-reference"), + MatchResourceReferenceValue("urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:core/v1:Service::test:default/test-reference", "test:default/test-reference"), + )), + })) + }) + + Describe("Resource Prefix", func() { + BeforeEach(func() { + inputs["resourcePrefix"] = resource.NewStringProperty("prefixed") + }) + It("should use the prefix (instead of the component name)", func(ctx context.Context) { + resp, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + outputs := unmarshalProperties(GinkgoTB(), resp.State) + Expect(outputs).To(MatchProps(IgnoreExtras, Props{ + "resources": MatchArrayValue(ConsistOf( + MatchResourceReferenceValue("urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:apiextensions.k8s.io/v1:CustomResourceDefinition::prefixed:crontabs.stable.example.com", "prefixed:crontabs.stable.example.com"), + MatchResourceReferenceValue("urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:core/v1:ServiceAccount::prefixed:default/test-reference", "prefixed:default/test-reference"), + MatchResourceReferenceValue("urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:core/v1:Service::prefixed:default/test-reference", "prefixed:default/test-reference"), + )), + })) + }) + }) + + Describe("Skip Await", func() { + BeforeEach(func() { + inputs["skipAwait"] = resource.NewBoolProperty(true) + }) + It("should not await the resources", func(ctx context.Context) { + _, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + Expect(tc.monitor.Registrations()).To(MatchKeys(IgnoreExtras, Keys{ + "urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:apiextensions.k8s.io/v1:CustomResourceDefinition::test:crontabs.stable.example.com": MatchFields(IgnoreExtras, Fields{ + "State": HaveSkipAwaitAnnotation(), + }), + "urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:core/v1:ServiceAccount::test:default/test-reference": MatchFields(IgnoreExtras, Fields{ + "State": HaveSkipAwaitAnnotation(), + }), + "urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:core/v1:Service::test:default/test-reference": MatchFields(IgnoreExtras, Fields{ + "State": HaveSkipAwaitAnnotation(), + }), + })) + }) + }) + + Describe("helm.sh/resource-policy: keep", func() { + BeforeEach(func() { + inputs["values"] = resource.NewObjectProperty(resource.NewPropertyMapFromMap(map[string]any{ + "serviceAccount": map[string]any{ + "annotations": map[string]any{ + "helm.sh/resource-policy": "keep", + }, + }, + })) + }) + It("should enable the RetainOnDelete option", func(ctx context.Context) { + _, err := pulumiprovider.Construct(ctx, req, tc.EngineConn(), k.Construct) + Expect(err).ShouldNot(HaveOccurred()) + Expect(tc.monitor.Registrations()).To(MatchKeys(IgnoreExtras, Keys{ + "urn:pulumi:stack::project::kubernetes:helm/v4:Chart$kubernetes:core/v1:ServiceAccount::test:default/test-reference": MatchFields(IgnoreExtras, Fields{ + "Request": MatchFields(IgnoreExtras, Fields{ + "RetainOnDelete": BeTrue(), + }), + }), + })) + }) + }) + }) +}) diff --git a/provider/pkg/provider/helm/v4/helmv4_suite_test.go b/provider/pkg/provider/helm/v4/helmv4_suite_test.go new file mode 100644 index 0000000000..268e0d5725 --- /dev/null +++ b/provider/pkg/provider/helm/v4/helmv4_suite_test.go @@ -0,0 +1,109 @@ +// Copyright 2016-2024, 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 v4 + +import ( + "context" + "testing" + + structpb "github.com/golang/protobuf/ptypes/struct" + . "github.com/onsi/ginkgo/v2" //nolint:golint // dot-imports + . "github.com/onsi/gomega" //nolint:golint // dot-imports + fakehost "github.com/pulumi/pulumi-kubernetes/provider/v4/pkg/host/fake" + "github.com/pulumi/pulumi/sdk/v3/go/common/resource" + "github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" + pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" + "google.golang.org/grpc" +) + +func TestHelmV4(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "provider/pkg/provider/helm/v4") +} + +func unmarshalProperties(t testing.TB, props *structpb.Struct) resource.PropertyMap { + pm, err := plugin.UnmarshalProperties(props, plugin.MarshalOptions{ + KeepUnknowns: true, + KeepSecrets: true, + KeepResources: true, + KeepOutputValues: true, + }) + if err != nil { + t.Fatalf("failed to unmarshal properties: %s", err) + } + return pm +} + +type componentProviderTestContext struct { + t testing.TB + engine *fakehost.EngineServer + engineAddr string + engineConn *grpc.ClientConn + monitor *fakehost.ResourceMonitorServer + monitorAddr string +} + +func newTestContext(t testing.TB) *componentProviderTestContext { + engine := fakehost.NewEngineServer(t) + engineAddr := fakehost.StartEngineServer(t, engine) + engineConn := fakehost.ConnectToEngine(t, engineAddr) + + monitor := fakehost.NewResourceMonitorServer(t, &fakehost.SimpleMonitor{}) + monitorAddr := fakehost.StartMonitorServer(t, monitor) + + return &componentProviderTestContext{ + t: t, + engine: engine, + engineAddr: engineAddr, + engineConn: engineConn, + monitor: monitor, + monitorAddr: monitorAddr, + } +} + +func (tc *componentProviderTestContext) NewConstructRequest() *pulumirpc.ConstructRequest { + return &pulumirpc.ConstructRequest{ + Project: "project", + Stack: "stack", + MonitorEndpoint: tc.monitorAddr, + } +} + +func (tc *componentProviderTestContext) Engine() *fakehost.EngineServer { + return tc.engine +} + +func (tc *componentProviderTestContext) EngineConn() *grpc.ClientConn { + return tc.engineConn +} + +func (tc *componentProviderTestContext) Monitor() *fakehost.ResourceMonitorServer { + return tc.monitor +} + +func (tc *componentProviderTestContext) NewContext(ctx context.Context) *pulumi.Context { + runInfo := pulumi.RunInfo{ + Project: "project", + Stack: "stack", + MonitorAddr: tc.monitorAddr, + EngineAddr: tc.engineAddr, + } + pulumiCtx, err := pulumi.NewContext(ctx, runInfo) + if err != nil { + tc.t.Fatalf("constructing run context: %s", err) + } + return pulumiCtx +} diff --git a/provider/pkg/provider/helm/v4/testdata/pubring.gpg b/provider/pkg/provider/helm/v4/testdata/pubring.gpg new file mode 100644 index 0000000000..b2d2efd60b Binary files /dev/null and b/provider/pkg/provider/helm/v4/testdata/pubring.gpg differ diff --git a/provider/pkg/provider/helm/v4/testdata/reference/.helmignore b/provider/pkg/provider/helm/v4/testdata/reference/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/provider/pkg/provider/helm/v4/testdata/reference/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/provider/pkg/provider/helm/v4/testdata/reference/Chart.lock b/provider/pkg/provider/helm/v4/testdata/reference/Chart.lock new file mode 100644 index 0000000000..caa87bdacf --- /dev/null +++ b/provider/pkg/provider/helm/v4/testdata/reference/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common + repository: oci://registry-1.docker.io/bitnamicharts + version: 2.19.1 +digest: sha256:c883732817d9aaa3304f7b3109262aa338959de15b432dc5a2dbde13d2e136a5 +generated: "2024-04-25T14:03:59.911434-07:00" diff --git a/provider/pkg/provider/helm/v4/testdata/reference/Chart.yaml b/provider/pkg/provider/helm/v4/testdata/reference/Chart.yaml new file mode 100644 index 0000000000..45c513b166 --- /dev/null +++ b/provider/pkg/provider/helm/v4/testdata/reference/Chart.yaml @@ -0,0 +1,15 @@ +apiVersion: v2 +name: reference +description: A Helm chart for Kubernetes +type: application +version: 0.1.0 +appVersion: "1.0.0" + +# Has dependencies on a remote chart, necessitating a build step. +dependencies: +- name: common + repository: oci://registry-1.docker.io/bitnamicharts + tags: + - bitnami-common + version: 2.x.x + diff --git a/provider/pkg/provider/helm/v4/testdata/reference/charts/common-2.19.1.tgz b/provider/pkg/provider/helm/v4/testdata/reference/charts/common-2.19.1.tgz new file mode 100644 index 0000000000..2aec1c88ed Binary files /dev/null and b/provider/pkg/provider/helm/v4/testdata/reference/charts/common-2.19.1.tgz differ diff --git a/provider/pkg/provider/helm/v4/testdata/reference/crds/crontabs.yaml b/provider/pkg/provider/helm/v4/testdata/reference/crds/crontabs.yaml new file mode 100644 index 0000000000..ef337e9bb2 --- /dev/null +++ b/provider/pkg/provider/helm/v4/testdata/reference/crds/crontabs.yaml @@ -0,0 +1,40 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + # name must match the spec fields below, and be in the form: . + name: crontabs.stable.example.com +spec: + # group name to use for REST API: /apis// + group: stable.example.com + # list of versions supported by this CustomResourceDefinition + versions: + - name: v1 + # Each version can be enabled/disabled by Served flag. + served: true + # One and only one version must be marked as the storage version. + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + cronSpec: + type: string + image: + type: string + replicas: + type: integer + # either Namespaced or Cluster + scope: Namespaced + names: + # plural name to be used in the URL: /apis/// + plural: crontabs + # singular name to be used as an alias on the CLI and for display + singular: crontab + # kind is normally the CamelCased singular type. Your resource manifests use this. + kind: CronTab + # shortNames allow shorter string to match your resource on the CLI + shortNames: + - ct diff --git a/provider/pkg/provider/helm/v4/testdata/reference/templates/NOTES.txt b/provider/pkg/provider/helm/v4/testdata/reference/templates/NOTES.txt new file mode 100644 index 0000000000..86602ea185 --- /dev/null +++ b/provider/pkg/provider/helm/v4/testdata/reference/templates/NOTES.txt @@ -0,0 +1,5 @@ +capabilities: +{{ toYaml .Capabilities | nindent 2 }} + +values: +{{ toYaml .Values | nindent 2 }} \ No newline at end of file diff --git a/provider/pkg/provider/helm/v4/testdata/reference/templates/_helpers.tpl b/provider/pkg/provider/helm/v4/testdata/reference/templates/_helpers.tpl new file mode 100644 index 0000000000..74f1a2807a --- /dev/null +++ b/provider/pkg/provider/helm/v4/testdata/reference/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "reference.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "reference.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "reference.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "reference.labels" -}} +helm.sh/chart: {{ include "reference.chart" . }} +{{ include "reference.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "reference.selectorLabels" -}} +app.kubernetes.io/name: {{ include "reference.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "reference.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "reference.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/provider/pkg/provider/helm/v4/testdata/reference/templates/service.yaml b/provider/pkg/provider/helm/v4/testdata/reference/templates/service.yaml new file mode 100644 index 0000000000..3701a63df2 --- /dev/null +++ b/provider/pkg/provider/helm/v4/testdata/reference/templates/service.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "reference.fullname" . }} + labels: + {{- include "reference.labels" . | nindent 4 }} +spec: + type: ExternalName + externalName: www.pulumi.com diff --git a/provider/pkg/provider/helm/v4/testdata/reference/templates/serviceaccount.yaml b/provider/pkg/provider/helm/v4/testdata/reference/templates/serviceaccount.yaml new file mode 100644 index 0000000000..df354525b5 --- /dev/null +++ b/provider/pkg/provider/helm/v4/testdata/reference/templates/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "reference.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "reference.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/provider/pkg/provider/helm/v4/testdata/reference/templates/tests/test-connection.yaml b/provider/pkg/provider/helm/v4/testdata/reference/templates/tests/test-connection.yaml new file mode 100644 index 0000000000..f9ecd5c62b --- /dev/null +++ b/provider/pkg/provider/helm/v4/testdata/reference/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "reference.fullname" . }}-test-connection" + labels: + {{- include "reference.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "reference.fullname" . }}'] + restartPolicy: Never diff --git a/provider/pkg/provider/helm/v4/testdata/reference/templates/versioncheck.yaml b/provider/pkg/provider/helm/v4/testdata/reference/templates/versioncheck.yaml new file mode 100644 index 0000000000..54dee322fc --- /dev/null +++ b/provider/pkg/provider/helm/v4/testdata/reference/templates/versioncheck.yaml @@ -0,0 +1,5 @@ +{{- if .Values.versionCheck -}} +{{- if semverCompare .Values.versionCheck .Capabilities.KubeVersion.GitVersion -}} +{{ fail "Version check failed" }} +{{- end }} +{{- end }} diff --git a/provider/pkg/provider/helm/v4/testdata/reference/values.yaml b/provider/pkg/provider/helm/v4/testdata/reference/values.yaml new file mode 100644 index 0000000000..7c6fd815fb --- /dev/null +++ b/provider/pkg/provider/helm/v4/testdata/reference/values.yaml @@ -0,0 +1,26 @@ +# Default values for reference. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +image: + repository: nginx + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +namespace: + create: false + name: "reference" + +serviceAccount: + create: true + name: "" + automount: true + # annotations: + # helm.sh/resource-policy: keep + +# versionCheck: "<1.21-0" diff --git a/provider/pkg/provider/provider.go b/provider/pkg/provider/provider.go index 96864f70f5..90e26d6c43 100644 --- a/provider/pkg/provider/provider.go +++ b/provider/pkg/provider/provider.go @@ -60,16 +60,19 @@ import ( pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + helmcli "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/helmpath" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" clientapi "k8s.io/client-go/tools/clientcmd/api" k8sopenapi "k8s.io/kubectl/pkg/util/openapi" + "k8s.io/utils/ptr" "sigs.k8s.io/yaml" ) @@ -139,6 +142,7 @@ type kubeProvider struct { helmRegistryConfigPath string helmRepositoryConfigPath string helmRepositoryCache string + helmSettings *helmcli.EnvSettings helmReleaseProvider customResourceProvider yamlRenderMode bool @@ -452,6 +456,12 @@ func (k *kubeProvider) DiffConfig(ctx context.Context, req *pulumirpc.DiffReques func (k *kubeProvider) Configure(_ context.Context, req *pulumirpc.ConfigureRequest) (*pulumirpc.ConfigureResponse, error) { const trueStr = "true" + // Configure Helm settings based on the ambient Helm environment, + // using the provider configuration as overrides. + helmSettings := helmcli.New() + helmFlags := helmSettings.RESTClientGetter().(*genericclioptions.ConfigFlags) + helmSettings.Debug = true // enable verbose logging (piped to glog at level 6) + vars := req.GetVariables() // @@ -468,6 +478,8 @@ func (k *kubeProvider) Configure(_ context.Context, req *pulumirpc.ConfigureRequ // if defaultNamespace := vars["kubernetes:config:namespace"]; defaultNamespace != "" { k.defaultNamespace = defaultNamespace + helmSettings.SetNamespace(defaultNamespace) + logger.V(9).Infof("namespace set to %v", defaultNamespace) } // Compute config overrides. @@ -478,6 +490,12 @@ func (k *kubeProvider) Configure(_ context.Context, req *pulumirpc.ConfigureRequ }, CurrentContext: vars["kubernetes:config:context"], } + if overrides.Context.Cluster != "" { + helmFlags.ClusterName = &overrides.Context.Cluster + } + if overrides.CurrentContext != "" { + helmSettings.KubeContext = overrides.CurrentContext + } deleteUnreachable := func() bool { // If the provider flag is set, use that value to determine behavior. This will override the ENV var. @@ -620,6 +638,9 @@ func (k *kubeProvider) Configure(_ context.Context, req *pulumirpc.ConfigureRequ return helmpath.DataPath("plugins") } k.helmPluginsPath = helmPluginsPath() + if helmReleaseSettings.PluginsPath != nil { + helmSettings.PluginsDirectory = *helmReleaseSettings.PluginsPath + } helmRegistryConfigPath := func() string { if helmReleaseSettings.RegistryConfigPath != nil { @@ -633,6 +654,9 @@ func (k *kubeProvider) Configure(_ context.Context, req *pulumirpc.ConfigureRequ return helmpath.ConfigPath("registry.json") } k.helmRegistryConfigPath = helmRegistryConfigPath() + if helmReleaseSettings.RegistryConfigPath != nil { + helmSettings.RegistryConfig = k.helmRegistryConfigPath + } helmRepositoryConfigPath := func() string { if helmReleaseSettings.RepositoryConfigPath != nil { @@ -645,6 +669,9 @@ func (k *kubeProvider) Configure(_ context.Context, req *pulumirpc.ConfigureRequ return helmpath.ConfigPath("repositories.yaml") } k.helmRepositoryConfigPath = helmRepositoryConfigPath() + if helmReleaseSettings.RepositoryConfigPath != nil { + helmSettings.RepositoryConfig = k.helmRepositoryConfigPath + } helmRepositoryCache := func() string { if helmReleaseSettings.RepositoryCache != nil { @@ -657,6 +684,9 @@ func (k *kubeProvider) Configure(_ context.Context, req *pulumirpc.ConfigureRequ return helmpath.CachePath("repository") } k.helmRepositoryCache = helmRepositoryCache() + if helmReleaseSettings.RepositoryCache != nil { + helmSettings.RepositoryCache = k.helmRepositoryCache + } // Rather than erroring out on an invalid k8s config, mark the cluster as unreachable and conditionally bail out on // operations that require a valid cluster. This will allow us to perform invoke operations using the default @@ -674,12 +704,20 @@ func (k *kubeProvider) Configure(_ context.Context, req *pulumirpc.ConfigureRequ apiConfig, err := parseKubeconfigString(pathOrContents) if err != nil { unreachableCluster(err) + // note: kubeconfig is not set when the cluster is unreachable } else { kubeconfig = clientcmd.NewDefaultClientConfig(*apiConfig, overrides) configurationNamespace, _, err := kubeconfig.Namespace() if err == nil { k.defaultNamespace = configurationNamespace } + + // initialize Helm settings to use the kubeconfig; use a generated file as necessary. + configFile, err := writeKubeconfigToFile(apiConfig) + if err != nil { + return nil, fmt.Errorf("failed to write kubeconfig file: %w", err) + } + helmSettings.KubeConfig = configFile } } else { // Use client-go to resolve the final configuration values for the client. Typically, these @@ -710,7 +748,7 @@ func (k *kubeProvider) Configure(_ context.Context, req *pulumirpc.ConfigureRequ return nil, fmt.Errorf("invalid value specified for PULUMI_K8S_CLIENT_BURST: %w", err) } kubeClientSettings.Burst = &asInt - } else { + } else if kubeClientSettings.Burst == nil { v := 120 // Increased from default value of 10 kubeClientSettings.Burst = &v } @@ -721,7 +759,7 @@ func (k *kubeProvider) Configure(_ context.Context, req *pulumirpc.ConfigureRequ return nil, fmt.Errorf("invalid value specified for PULUMI_K8S_CLIENT_QPS: %w", err) } kubeClientSettings.QPS = &asFloat - } else { + } else if kubeClientSettings.QPS == nil { v := 50.0 // Increased from default value of 5.0 kubeClientSettings.QPS = &v } @@ -748,14 +786,17 @@ func (k *kubeProvider) Configure(_ context.Context, req *pulumirpc.ConfigureRequ } else { if kubeClientSettings.Burst != nil { config.Burst = *kubeClientSettings.Burst + helmSettings.BurstLimit = *kubeClientSettings.Burst logger.V(9).Infof("kube client burst set to %v", config.Burst) } if kubeClientSettings.QPS != nil { config.QPS = float32(*kubeClientSettings.QPS) + helmSettings.QPS = float32(*kubeClientSettings.QPS) logger.V(9).Infof("kube client QPS set to %v", config.QPS) } if kubeClientSettings.Timeout != nil { config.Timeout = time.Duration(*kubeClientSettings.Timeout) * time.Second + helmFlags.Timeout = ptr.To(strconv.Itoa(*kubeClientSettings.Timeout)) logger.V(9).Infof("kube client timeout set to %v", config.Timeout) } config.WarningHandler = rest.NoWarnings{} @@ -783,6 +824,10 @@ func (k *kubeProvider) Configure(_ context.Context, req *pulumirpc.ConfigureRequ } } + if !k.clusterUnreachable { + k.helmSettings = helmSettings + } + k.helmReleaseProvider, err = newHelmReleaseProvider( k.host, k.canceler, diff --git a/provider/pkg/provider/provider_configure_test.go b/provider/pkg/provider/provider_configure_test.go index 8bdb3273f7..4d221e3583 100644 --- a/provider/pkg/provider/provider_configure_test.go +++ b/provider/pkg/provider/provider_configure_test.go @@ -17,13 +17,17 @@ package provider import ( "context" _ "embed" + "encoding/json" "os" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gstruct" pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" kubeversion "k8s.io/apimachinery/pkg/version" + "k8s.io/cli-runtime/pkg/genericclioptions" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + "k8s.io/utils/ptr" ) var _ = Describe("RPC:Configure", func() { @@ -89,6 +93,21 @@ var _ = Describe("RPC:Configure", func() { }) }) + Describe("Namespacing", func() { + Context("when configured to use a particular namespace", func() { + JustBeforeEach(func() { + req.Variables["kubernetes:config:namespace"] = "pulumi" + }) + It("should use the configured namespace as the default namespace", func() { + _, err := k.Configure(context.Background(), req) + Expect(err).ShouldNot(HaveOccurred()) + Expect(k.defaultNamespace).To(Equal("pulumi")) + helmFlags := k.helmSettings.RESTClientGetter().(*genericclioptions.ConfigFlags) + Expect(helmFlags.Namespace).To(PointTo(Equal("pulumi"))) + }) + }) + }) + Describe("Kubeconfig Parsing", func() { var other *clientcmdapi.Config @@ -100,18 +119,7 @@ var _ = Describe("RPC:Configure", func() { // Define some "shared behaviors" that will be used to test various use cases. // pattern: https://onsi.github.io/ginkgo/#shared-behaviors - commonChecks := func() { - Context("when configured to use a particular namespace", func() { - JustBeforeEach(func() { - req.Variables["kubernetes:config:namespace"] = "pulumi" - }) - It("should use the configured namespace as the default namespace", func() { - _, err := k.Configure(context.Background(), req) - Expect(err).ShouldNot(HaveOccurred()) - Expect(k.defaultNamespace).To(Equal("pulumi")) - }) - }) - } + commonChecks := func() {} connectedChecks := func(expectedNS string) { It("should have an initialized client", func() { @@ -128,6 +136,12 @@ var _ = Describe("RPC:Configure", func() { Expect(err).ShouldNot(HaveOccurred()) Expect(k.defaultNamespace).To(Equal(expectedNS)) }) + + It("should provide Helm settings", func() { + _, err := k.Configure(context.Background(), req) + Expect(err).ShouldNot(HaveOccurred()) + Expect(k.helmSettings).ToNot(BeNil()) + }) } clusterUnreachableChecks := func() { @@ -160,6 +174,12 @@ var _ = Describe("RPC:Configure", func() { }) commonChecks() connectedChecks("other") + + It("should set Helm's --kubeconfig", func() { + _, err := k.Configure(context.Background(), req) + Expect(err).ShouldNot(HaveOccurred()) + Expect(k.helmSettings.KubeConfig).ToNot(BeEmpty()) + }) }) }) @@ -195,6 +215,92 @@ var _ = Describe("RPC:Configure", func() { }) commonChecks() connectedChecks("other") + + It("should set Helm's --kubeconfig", func() { + _, err := k.Configure(context.Background(), req) + Expect(err).ShouldNot(HaveOccurred()) + Expect(k.helmSettings.KubeConfig).ToNot(BeEmpty()) + }) + }) + }) + }) + + Describe("Kube Context", func() { + Context("when configured to use a particular context", func() { + JustBeforeEach(func() { + req.Variables["kubernetes:config:context"] = "context2" + }) + It("should use the configured context", func() { + _, err := k.Configure(context.Background(), req) + Expect(err).ShouldNot(HaveOccurred()) + Expect(k.helmSettings.KubeContext).To(Equal("context2")) + }) + }) + }) + + Describe("Kube Cluster", func() { + Context("when configured to use a particular cluster", func() { + JustBeforeEach(func() { + req.Variables["kubernetes:config:cluster"] = "cluster2" + }) + It("should use the configured context", func() { + _, err := k.Configure(context.Background(), req) + Expect(err).ShouldNot(HaveOccurred()) + helmFlags := k.helmSettings.RESTClientGetter().(*genericclioptions.ConfigFlags) + Expect(helmFlags.ClusterName).To(PointTo(Equal("cluster2"))) + }) + }) + }) + + Describe("Kube Client Settings", func() { + Context("when configured with Kube client settings", func() { + var kubeClientSettings *KubeClientSettings + BeforeEach(func() { + kubeClientSettings = &KubeClientSettings{ + Burst: ptr.To(42), + QPS: ptr.To(42.), + Timeout: ptr.To(42), + } + }) + JustBeforeEach(func() { + data, _ := json.Marshal(kubeClientSettings) + req.Variables["kubernetes:config:kubeClientSettings"] = string(data) + }) + It("should use the configured settings", func() { + _, err := k.Configure(context.Background(), req) + Expect(err).ShouldNot(HaveOccurred()) + helmFlags := k.helmSettings.RESTClientGetter().(*genericclioptions.ConfigFlags) + Expect(k.helmSettings.BurstLimit).To(Equal(42)) + Expect(k.helmSettings.QPS).To(Equal(float32(42.))) + Expect(helmFlags.Timeout).To(PointTo(Equal("42"))) + }) + }) + }) + + Describe("Helm Release Settings", func() { + Context("given helmReleaseSettings", func() { + var helmReleaseSettings *HelmReleaseSettings + BeforeEach(func() { + helmReleaseSettings = &HelmReleaseSettings{ + Driver: ptr.To("configmap"), + PluginsPath: ptr.To("plugins"), + RegistryConfigPath: ptr.To("registry"), + RepositoryCache: ptr.To("cache"), + RepositoryConfigPath: ptr.To("config"), + } + }) + JustBeforeEach(func() { + data, _ := json.Marshal(helmReleaseSettings) + req.Variables["kubernetes:config:helmReleaseSettings"] = string(data) + }) + It("should use the configured settings", func() { + _, err := k.Configure(context.Background(), req) + Expect(err).ShouldNot(HaveOccurred()) + Expect(k.helmDriver).To(Equal("configmap")) + Expect(k.helmSettings.PluginsDirectory).To(Equal("plugins")) + Expect(k.helmSettings.RegistryConfig).To(Equal("registry")) + Expect(k.helmSettings.RepositoryCache).To(Equal("cache")) + Expect(k.helmSettings.RepositoryConfig).To(Equal("config")) }) }) }) diff --git a/provider/pkg/provider/provider_construct.go b/provider/pkg/provider/provider_construct.go index 53eba2e2c7..02d0d60dd3 100644 --- a/provider/pkg/provider/provider_construct.go +++ b/provider/pkg/provider/provider_construct.go @@ -18,8 +18,10 @@ import ( "context" "fmt" + providerhelmv4 "github.com/pulumi/pulumi-kubernetes/provider/v4/pkg/provider/helm/v4" providerresource "github.com/pulumi/pulumi-kubernetes/provider/v4/pkg/provider/resource" provideryamlv2 "github.com/pulumi/pulumi-kubernetes/provider/v4/pkg/provider/yaml/v2" + "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" pulumiprovider "github.com/pulumi/pulumi/sdk/v3/go/pulumi/provider" pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" ) @@ -28,6 +30,7 @@ import ( var resourceProviders = map[string]providerresource.ResourceProviderFactory{ "kubernetes:yaml/v2:ConfigFile": provideryamlv2.NewConfigFileProvider, "kubernetes:yaml/v2:ConfigGroup": provideryamlv2.NewConfigGroupProvider, + "kubernetes:helm.sh/v4:Chart": providerhelmv4.NewChartProvider, } // getResourceProvider returns the resource provider for the given type, if a factory for one is registered. @@ -36,15 +39,29 @@ func (k *kubeProvider) getResourceProvider(typ string) (providerresource.Resourc if !found { return nil, false } + options := &providerresource.ResourceProviderOptions{ ClientSet: k.clientSet, DefaultNamespace: k.defaultNamespace, + HelmOptions: &providerresource.HelmOptions{ + SuppressHelmHookWarnings: k.suppressHelmHookWarnings, + HelmDriver: k.helmDriver, + EnvSettings: k.helmSettings, + }, } return providerF(options), true } // Construct creates a new instance of the provided component resource and returns its state. func (k *kubeProvider) Construct(ctx context.Context, req *pulumirpc.ConstructRequest) (*pulumirpc.ConstructResponse, error) { + + if k.clusterUnreachable { + return nil, fmt.Errorf("configured Kubernetes cluster is unreachable: %s", k.clusterUnreachableReason) + } + contract.Assertf(k.defaultNamespace != "", "expected defaultNamespace") + contract.Assertf(k.helmDriver != "", "expected helmDriver") + contract.Assertf(k.helmSettings != nil, "expected helmSettings") + typ := req.GetType() provider, found := k.getResourceProvider(typ) if !found { diff --git a/provider/pkg/provider/provider_construct_test.go b/provider/pkg/provider/provider_construct_test.go index 56550bbbd4..1c8954b906 100644 --- a/provider/pkg/provider/provider_construct_test.go +++ b/provider/pkg/provider/provider_construct_test.go @@ -20,10 +20,12 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/pulumi/pulumi-kubernetes/provider/v4/pkg/clients" providerresource "github.com/pulumi/pulumi-kubernetes/provider/v4/pkg/provider/resource" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumi/pulumi/sdk/v3/go/pulumi/provider" pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" + helmcli "helm.sh/helm/v3/pkg/cli" ) var _ = Describe("RPC:Construct", func() { @@ -51,6 +53,10 @@ var _ = Describe("RPC:Construct", func() { JustBeforeEach(func() { k = pctx.NewProvider(opts...) + k.clientSet = &clients.DynamicClientSet{} + k.defaultNamespace = "default" + k.helmDriver = "memory" + k.helmSettings = helmcli.New() }) Context("when the requested type is unknown", func() { @@ -77,6 +83,7 @@ var _ = Describe("RPC:Construct", func() { req.Type = "kubernetes:test:TestComponent" req.Name = "testComponent" }) + It("should delegate to the provider", func() { result, err := k.Construct(context.Background(), req) Expect(err).ShouldNot(HaveOccurred()) @@ -84,6 +91,25 @@ var _ = Describe("RPC:Construct", func() { Expect(testComponent.name).Should(Equal("testComponent")) Expect(result.Urn).Should(Equal("urn:pulumi:test::test::test:TestComponent::testComponent")) }) + + It("should provide options", func() { + _, err := k.Construct(context.Background(), req) + Expect(err).ShouldNot(HaveOccurred()) + Expect(testComponent.opts.ClientSet).ShouldNot(BeNil()) + Expect(testComponent.opts.DefaultNamespace).ShouldNot(BeEmpty()) + Expect(testComponent.opts.HelmOptions).ShouldNot(BeNil()) + }) + + Context("when clusterUnreachable is true", func() { + JustBeforeEach(func() { + k.clusterUnreachable = true + k.clusterUnreachableReason = "testing" + }) + It("should return an error", func() { + _, err := k.Construct(context.Background(), req) + Expect(err).To(MatchError(ContainSubstring("configured Kubernetes cluster is unreachable"))) + }) + }) }) }) diff --git a/provider/pkg/provider/resource/provider.go b/provider/pkg/provider/resource/provider.go index dea8ffbd79..26e3520400 100644 --- a/provider/pkg/provider/resource/provider.go +++ b/provider/pkg/provider/resource/provider.go @@ -18,6 +18,7 @@ import ( "github.com/pulumi/pulumi-kubernetes/provider/v4/pkg/clients" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" pulumiprovider "github.com/pulumi/pulumi/sdk/v3/go/pulumi/provider" + "helm.sh/helm/v3/pkg/cli" ) type ResourceProvider interface { // nolint:golint // stutters @@ -28,6 +29,13 @@ type ResourceProvider interface { // nolint:golint // stutters type ResourceProviderOptions struct { // nolint:golint // stutters ClientSet *clients.DynamicClientSet DefaultNamespace string + HelmOptions *HelmOptions +} + +type HelmOptions struct { + SuppressHelmHookWarnings bool + HelmDriver string + EnvSettings *cli.EnvSettings } type ResourceProviderFactory func(*ResourceProviderOptions) ResourceProvider // nolint:golint // stutters diff --git a/provider/pkg/provider/types.go b/provider/pkg/provider/types.go index c8849ea2fe..e012026d93 100644 --- a/provider/pkg/provider/types.go +++ b/provider/pkg/provider/types.go @@ -25,7 +25,7 @@ type HelmReleaseSettings struct { PluginsPath *string `json:"pluginsPath"` // The path to the registry config file. RegistryConfigPath *string `json:"registryConfigPath"` - // The path to the file containing cached repository indexes. + // The path to the directory containing cached repository indexes. RepositoryCache *string `json:"repositoryCache"` // The path to the file containing repository names and URLs. RepositoryConfigPath *string `json:"repositoryConfigPath"` @@ -33,9 +33,9 @@ type HelmReleaseSettings struct { // Options for tuning the Kubernetes client used by a Provider. type KubeClientSettings struct { - // Maximum burst for throttle. Default value is 10. + // Maximum burst for throttle. Default value is 120. Burst *int `json:"burst"` - // Maximum queries per second (QPS) to the API server from this client. Default value is 5. + // Maximum queries per second (QPS) to the API server from this client. Default value is 50. QPS *float64 `json:"qps"` // Maximum time in seconds to wait before cancelling a HTTP request to the Kubernetes server. Default value is 32. Timeout *int `json:"timeout"` diff --git a/provider/pkg/provider/util.go b/provider/pkg/provider/util.go index e90cc343ef..d55f9b1c94 100644 --- a/provider/pkg/provider/util.go +++ b/provider/pkg/provider/util.go @@ -122,6 +122,15 @@ func fqName(namespace, name string) string { // Kubeconfig helpers. // -------------------------------------------------------------------------- +func writeKubeconfigToFile(config *clientapi.Config) (string, error) { + file, err := os.CreateTemp("", "kubeconfig") + if err != nil { + return "", err + } + err = clientcmd.WriteToFile(*config, file.Name()) + return file.Name(), err +} + // parseKubeconfigString takes a string that contains either a path to a kubeconfig file // or the contents of a kubeconfig (YAML or JSON). func parseKubeconfigString(pathOrContents string) (*clientapi.Config, error) { @@ -217,6 +226,10 @@ func getActiveClusterFromConfig(config *clientapi.Config, overrides resource.Pro return activeCluster, true } +// -------------------------------------------------------------------------- +// Unstructured helpers. +// -------------------------------------------------------------------------- + // pruneMap builds a pruned map by recursively copying elements from the source map that have a matching key in the // target map. This is useful as a preprocessing step for live resource state before comparing it to program inputs. func pruneMap(source, target map[string]any) map[string]any { diff --git a/provider/pkg/provider/yaml/v2/yaml.go b/provider/pkg/provider/yaml/v2/yaml.go index d127afad2e..1275f42b07 100644 --- a/provider/pkg/provider/yaml/v2/yaml.go +++ b/provider/pkg/provider/yaml/v2/yaml.go @@ -212,11 +212,15 @@ func expand(objs []unstructured.Unstructured) ([]unstructured.Unstructured, erro return result, nil } +type PreRegisterFunc func(ctx *pulumi.Context, apiVersion, kind, resourceName string, obj *unstructured.Unstructured, + resourceOpts []pulumi.ResourceOption) (*unstructured.Unstructured, []pulumi.ResourceOption) + type RegisterOptions struct { Objects []unstructured.Unstructured ResourcePrefix string SkipAwait bool ResourceOptions []pulumi.ResourceOption + PreRegisterF PreRegisterFunc } // Register registers the given Kubernetes objects as resources with the Pulumi engine. @@ -306,6 +310,13 @@ func register( } } + if opts.PreRegisterF != nil { + obj, resourceOpts = opts.PreRegisterF(ctx, apiVersion, kind, resourceName, obj, resourceOpts) + } + + // At this point, the object is a pure unstructured object (no inputs or outputs within it). + // Now it becomes an input to ctx.RegisterResource. + if fullKind == "v1/Secret" { // Always mark these fields as secret to avoid leaking sensitive values from raw YAML. for _, key := range []string{"data", "stringData"} { diff --git a/provider/pkg/provider/yaml/v2/yaml_test.go b/provider/pkg/provider/yaml/v2/yaml_test.go index 2e74a51cea..362352a1c4 100644 --- a/provider/pkg/provider/yaml/v2/yaml_test.go +++ b/provider/pkg/provider/yaml/v2/yaml_test.go @@ -231,40 +231,16 @@ var _ = Describe("Register", func() { Expect(tc.monitor.Resources()).To(MatchAllKeys(Keys{ "urn:pulumi:stack::project::kubernetes:apiextensions.k8s.io/v1:CustomResourceDefinition::crontabs.stable.example.com": MatchProps(IgnoreExtras, Props{ - "state": MatchObject(IgnoreExtras, Props{ - "metadata": MatchObject(IgnoreExtras, Props{ - "annotations": MatchObject(IgnoreExtras, Props{ - "pulumi.com/skipAwait": MatchValue("true"), - }), - }), - }), + "state": BeObject(HaveSkipAwaitAnnotation()), }), "urn:pulumi:stack::project::kubernetes:core/v1:Namespace::my-namespace": MatchProps(IgnoreExtras, Props{ - "state": MatchObject(IgnoreExtras, Props{ - "metadata": MatchObject(IgnoreExtras, Props{ - "annotations": MatchObject(IgnoreExtras, Props{ - "pulumi.com/skipAwait": MatchValue("true"), - }), - }), - }), + "state": BeObject(HaveSkipAwaitAnnotation()), }), "urn:pulumi:stack::project::kubernetes:core/v1:ConfigMap::my-namespace/my-map": MatchProps(IgnoreExtras, Props{ - "state": MatchObject(IgnoreExtras, Props{ - "metadata": MatchObject(IgnoreExtras, Props{ - "annotations": MatchObject(IgnoreExtras, Props{ - "pulumi.com/skipAwait": MatchValue("true"), - }), - }), - }), + "state": BeObject(HaveSkipAwaitAnnotation()), }), "urn:pulumi:stack::project::kubernetes:stable.example.com/v1:CronTab::my-namespace/my-new-cron-object": MatchProps(IgnoreExtras, Props{ - "state": MatchObject(IgnoreExtras, Props{ - "metadata": MatchObject(IgnoreExtras, Props{ - "annotations": MatchObject(IgnoreExtras, Props{ - "pulumi.com/skipAwait": MatchValue("true"), - }), - }), - }), + "state": BeObject(HaveSkipAwaitAnnotation()), }), })) }) diff --git a/provider/pkg/provider/yaml/v2/yamlv2_suite_test.go b/provider/pkg/provider/yaml/v2/yamlv2_suite_test.go index 25860a51a7..2136be6cf2 100644 --- a/provider/pkg/provider/yaml/v2/yamlv2_suite_test.go +++ b/provider/pkg/provider/yaml/v2/yamlv2_suite_test.go @@ -29,7 +29,7 @@ import ( "google.golang.org/grpc" ) -func TestV2(t *testing.T) { +func TestYamlV2(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "provider/pkg/provider/yaml/v2") } diff --git a/sdk/dotnet/Helm/V4/Chart.cs b/sdk/dotnet/Helm/V4/Chart.cs new file mode 100644 index 0000000000..4d1a005531 --- /dev/null +++ b/sdk/dotnet/Helm/V4/Chart.cs @@ -0,0 +1,359 @@ +// *** WARNING: this file was generated by pulumigen. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Pulumi.Serialization; + +namespace Pulumi.Kubernetes.Helm.V4 +{ + /// + /// Chart is a component representing a collection of resources described by a Helm Chart. + /// Helm charts are a popular packaging format for Kubernetes applications, and published + /// to registries such as [Artifact Hub](https://artifacthub.io/packages/search?kind=0&sort=relevance&page=1). + /// + /// Chart does not use Tiller or create a Helm Release; the semantics are equivalent to + /// running `helm template --dry-run=server` and then using Pulumi to deploy the resulting YAML manifests. + /// This allows you to apply [Pulumi Transformations](https://www.pulumi.com/docs/concepts/options/transformations/) and + /// [Pulumi Policies](https://www.pulumi.com/docs/using-pulumi/crossguard/) to the Kubernetes resources. + /// + /// You may also want to consider the `Release` resource as an alternative method for managing helm charts. For more + /// information about the trade-offs between these options, see: [Choosing the right Helm resource for your use case](https://www.pulumi.com/registry/packages/kubernetes/how-to-guides/choosing-the-right-helm-resource-for-your-use-case). + /// + /// ### Chart Resolution + /// + /// The Helm Chart can be fetched from any source that is accessible to the `helm` command line. + /// The following variations are supported: + /// + /// 1. By chart reference with repo prefix: `chart: "example/mariadb"` + /// 2. By path to a packaged chart: `chart: "./nginx-1.2.3.tgz"` + /// 3. By path to an unpacked chart directory: `chart: "./nginx"` + /// 4. By absolute URL: `chart: "https://example.com/charts/nginx-1.2.3.tgz"` + /// 5. By chart reference with repo URL: `chart: "nginx", repositoryOpts: { repo: "https://example.com/charts/" }` + /// 6. By OCI registry: `chart: "oci://example.com/charts/nginx", version: "1.2.3"` + /// + /// A chart reference is a convenient way of referencing a chart in a chart repository. + /// + /// When you use a chart reference with a repo prefix (`example/mariadb`), Pulumi will look in Helm's local configuration + /// for a chart repository named `example`, and will then look for a chart in that repository whose name is `mariadb`. + /// It will install the latest stable version of that chart, unless you specify `devel` to also include + /// development versions (alpha, beta, and release candidate releases), or supply a version number with `version`. + /// + /// Use the `verify` and optional `keyring` inputs to enable Chart verification. + /// By default, Pulumi uses the keyring at `$HOME/.gnupg/pubring.gpg`. See: [Helm Provenance and Integrity](https://helm.sh/docs/topics/provenance/). + /// + /// ### Chart Values + /// + /// [Values files](https://helm.sh/docs/chart_template_guide/values_files/#helm) (`values.yaml`) may be supplied + /// with the `valueYamlFiles` input, accepting [Pulumi Assets](https://www.pulumi.com/docs/concepts/assets-archives/#assets). + /// + /// A map of chart values may also be supplied with the `values` input, with highest precedence. You're able to use literals, + /// nested maps, [Pulumi outputs](https://www.pulumi.com/docs/concepts/inputs-outputs/), and Pulumi assets as values. + /// Assets are automatically opened and converted to a string. + /// + /// Note that the use of expressions (e.g. `--set service.type`) is not supported. + /// + /// ### Chart Dependency Resolution + /// + /// For unpacked chart directories, Pulumi automatically rebuilds the dependencies if dependencies are missing + /// and a `Chart.lock` file is present (see: [Helm Dependency Build](https://helm.sh/docs/helm/helm_dependency_build/)). + /// Use the `dependencyUpdate` input to have Pulumi update the dependencies (see: [Helm Dependency Update](https://helm.sh/docs/helm/helm_dependency_update/)). + /// + /// ### Templating + /// + /// The `Chart` resource renders the templates from your chart and then manages the resources directly with the + /// Pulumi Kubernetes provider. A default namespace is applied based on the `namespace` input, the provider's + /// configured namespace, and the active Kubernetes context. Use the `skipCrds` option to skip installing the + /// Custom Resource Definition (CRD) objects located in the chart's `crds/` special directory. + /// + /// Use the `postRenderer` input to pipe the rendered manifest through a [post-rendering command](https://helm.sh/docs/topics/advanced/#post-rendering). + /// + /// ### Resource Ordering + /// + /// Sometimes resources must be applied in a specific order. For example, a namespace resource must be + /// created before any namespaced resources, or a Custom Resource Definition (CRD) must be pre-installed. + /// + /// Pulumi uses heuristics to determine which order to apply and delete objects within the Chart. Pulumi also + /// waits for each object to be fully reconciled, unless `skipAwait` is enabled. + /// + /// Pulumi supports the `config.kubernetes.io/depends-on` annotation to declare an explicit dependency on a given resource. + /// The annotation accepts a list of resource references, delimited by commas. + /// + /// Note that references to resources outside the Chart aren't supported. + /// + /// **Resource reference** + /// + /// A resource reference is a string that uniquely identifies a resource. + /// + /// It consists of the group, kind, name, and optionally the namespace, delimited by forward slashes. + /// + /// | Resource Scope | Format | + /// | :--------------- | :--------------------------------------------- | + /// | namespace-scoped | `<group>/namespaces/<namespace>/<kind>/<name>` | + /// | cluster-scoped | `<group>/<kind>/<name>` | + /// + /// For resources in the “core” group, the empty string is used instead (for example: `/namespaces/test/Pod/pod-a`). + /// + /// ## Example Usage + /// ### Local Chart Directory + /// ```csharp + /// using Pulumi; + /// using Pulumi.Kubernetes.Types.Inputs.Helm.V4; + /// using System.Collections.Generic; + /// + /// return await Deployment.RunAsync(() => + /// { + /// new Pulumi.Kubernetes.Helm.V4.Chart("nginx", new ChartArgs + /// { + /// Chart = "./nginx" + /// }); + /// return new Dictionary<string, object?>{}; + /// }); + /// ``` + /// ### Repository Chart + /// ```csharp + /// using Pulumi; + /// using Pulumi.Kubernetes.Types.Inputs.Helm.V4; + /// using System.Collections.Generic; + /// + /// return await Deployment.RunAsync(() => + /// { + /// new Pulumi.Kubernetes.Helm.V4.Chart("nginx", new ChartArgs + /// { + /// Chart = "nginx", + /// RepositoryOpts = new RepositoryOptsArgs + /// { + /// Repo = "https://charts.bitnami.com/bitnami" + /// }, + /// }); + /// + /// return new Dictionary<string, object?>{}; + /// }); + /// ``` + /// ### OCI Chart + /// ```csharp + /// using Pulumi; + /// using Pulumi.Kubernetes.Types.Inputs.Helm.V4; + /// using System.Collections.Generic; + /// + /// return await Deployment.RunAsync(() => + /// { + /// new Pulumi.Kubernetes.Helm.V4.Chart("nginx", new ChartArgs + /// { + /// Chart = "oci://registry-1.docker.io/bitnamicharts/nginx", + /// Version = "16.0.7", + /// }); + /// + /// return new Dictionary<string, object?>{}; + /// }); + /// ``` + /// ### Chart Values + /// ```csharp + /// using Pulumi; + /// using Pulumi.Kubernetes.Types.Inputs.Helm.V4; + /// using System.Collections.Generic; + /// + /// return await Deployment.RunAsync(() => + /// { + /// new Pulumi.Kubernetes.Helm.V4.Chart("nginx", new ChartArgs + /// { + /// Chart = "nginx", + /// RepositoryOpts = new RepositoryOptsArgs + /// { + /// Repo = "https://charts.bitnami.com/bitnami" + /// }, + /// ValueYamlFiles = + /// { + /// new FileAsset("./values.yaml") + /// }, + /// Values = new InputMap<object> + /// { + /// ["service"] = new InputMap<object> + /// { + /// ["type"] = "ClusterIP", + /// }, + /// ["notes"] = new FileAsset("./notes.txt") + /// }, + /// }); + /// + /// return new Dictionary<string, object?>{}; + /// }); + /// ``` + /// ### Chart Namespace + /// ```csharp + /// using Pulumi; + /// using Pulumi.Kubernetes.Types.Inputs.Core.V1; + /// using Pulumi.Kubernetes.Types.Inputs.Meta.V1; + /// using Pulumi.Kubernetes.Types.Inputs.Helm.V4; + /// using System.Collections.Generic; + /// + /// return await Deployment.RunAsync(() => + /// { + /// var ns = new Pulumi.Kubernetes.Core.V1.Namespace("nginx", new NamespaceArgs + /// { + /// Metadata = new ObjectMetaArgs{Name = "nginx"} + /// }); + /// new Pulumi.Kubernetes.Helm.V4.Chart("nginx", new ChartArgs + /// { + /// Namespace = ns.Metadata.Apply(m => m.Name), + /// Chart = "nginx", + /// RepositoryOpts = new RepositoryOptsArgs + /// { + /// Repo = "https://charts.bitnami.com/bitnami" + /// }, + /// }); + /// + /// return new Dictionary<string, object?>{}; + /// }); + /// ``` + /// + [KubernetesResourceType("kubernetes:helm.sh/v4:Chart")] + public partial class Chart : global::Pulumi.ComponentResource + { + /// + /// Resources created by the Chart. + /// + [Output("resources")] + public Output> Resources { get; private set; } = null!; + + + /// + /// Create a Chart resource with the given unique name, arguments, and options. + /// + /// + /// The unique name of the resource + /// The arguments used to populate this resource's properties + /// A bag of options that control this resource's behavior + public Chart(string name, Pulumi.Kubernetes.Types.Inputs.Helm.V4.ChartArgs? args = null, ComponentResourceOptions? options = null) + : base("kubernetes:helm.sh/v4:Chart", name, args ?? new Pulumi.Kubernetes.Types.Inputs.Helm.V4.ChartArgs(), MakeResourceOptions(options, ""), remote: true) + { + } + + private static ComponentResourceOptions MakeResourceOptions(ComponentResourceOptions? options, Input? id) + { + var defaultOptions = new ComponentResourceOptions + { + Version = Utilities.Version, + }; + var merged = ComponentResourceOptions.Merge(defaultOptions, options); + // Override the ID if one was specified for consistency with other language SDKs. + merged.Id = id ?? merged.Id; + return merged; + } + } +} +namespace Pulumi.Kubernetes.Types.Inputs.Helm.V4 +{ + + public class ChartArgs : global::Pulumi.ResourceArgs + { + /// + /// Chart name to be installed. A path may be used. + /// + [Input("chart", required: true)] + public Input Chart { get; set; } = null!; + + /// + /// Run helm dependency update before installing the chart. + /// + [Input("dependencyUpdate")] + public Input? DependencyUpdate { get; set; } + + /// + /// Use chart development versions, too. Equivalent to version '>0.0.0-0'. If `version` is set, this is ignored. + /// + [Input("devel")] + public Input? Devel { get; set; } + + /// + /// Location of public keys used for verification. Used only if `verify` is true + /// + [Input("keyring")] + public Input? Keyring { get; set; } + + /// + /// Release name. + /// + [Input("name")] + public Input? Name { get; set; } + + /// + /// Namespace for the release. + /// + [Input("namespace")] + public Input? Namespace { get; set; } + + /// + /// Specification defining the post-renderer to use. + /// + [Input("postRenderer")] + public Input? PostRenderer { get; set; } + + /// + /// Specification defining the Helm chart repository to use. + /// + [Input("repositoryOpts")] + public Input? RepositoryOpts { get; set; } + + /// + /// An optional prefix for the auto-generated resource names. Example: A resource created with resourcePrefix="foo" would produce a resource named "foo:resourceName". + /// + [Input("resourcePrefix")] + public Input? ResourcePrefix { get; set; } + + /// + /// By default, the provider waits until all resources are in a ready state before marking the release as successful. Setting this to true will skip such await logic. + /// + [Input("skipAwait")] + public Input? SkipAwait { get; set; } + + /// + /// If set, no CRDs will be installed. By default, CRDs are installed if not already present. + /// + [Input("skipCrds")] + public Input? SkipCrds { get; set; } + + [Input("valueYamlFiles")] + private InputList? _valueYamlFiles; + + /// + /// List of assets (raw yaml files). Content is read and merged with values. + /// + public InputList ValueYamlFiles + { + get => _valueYamlFiles ?? (_valueYamlFiles = new InputList()); + set => _valueYamlFiles = value; + } + + [Input("values")] + private InputMap? _values; + + /// + /// Custom values set for the release. + /// + public InputMap Values + { + get => _values ?? (_values = new InputMap()); + set => _values = value; + } + + /// + /// Verify the chart's integrity. + /// + [Input("verify")] + public Input? Verify { get; set; } + + /// + /// Specify the chart version to install. If this is not specified, the latest version is installed. + /// + [Input("version")] + public Input? Version { get; set; } + + public ChartArgs() + { + } + public static new ChartArgs Empty => new ChartArgs(); + } +} diff --git a/sdk/dotnet/Helm/V4/Inputs/PostRendererArgs.cs b/sdk/dotnet/Helm/V4/Inputs/PostRendererArgs.cs new file mode 100644 index 0000000000..3082679332 --- /dev/null +++ b/sdk/dotnet/Helm/V4/Inputs/PostRendererArgs.cs @@ -0,0 +1,41 @@ +// *** WARNING: this file was generated by pulumigen. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Pulumi.Serialization; + +namespace Pulumi.Kubernetes.Types.Inputs.Helm.V4 +{ + + /// + /// Specification defining the post-renderer to use. + /// + public class PostRendererArgs : global::Pulumi.ResourceArgs + { + [Input("args")] + private InputList? _args; + + /// + /// Arguments to pass to the post-renderer command. + /// + public InputList Args + { + get => _args ?? (_args = new InputList()); + set => _args = value; + } + + /// + /// Path to an executable to be used for post rendering. + /// + [Input("command", required: true)] + public Input Command { get; set; } = null!; + + public PostRendererArgs() + { + } + public static new PostRendererArgs Empty => new PostRendererArgs(); + } +} diff --git a/sdk/dotnet/Helm/V4/Inputs/RepositoryOptsArgs.cs b/sdk/dotnet/Helm/V4/Inputs/RepositoryOptsArgs.cs new file mode 100644 index 0000000000..70cb853122 --- /dev/null +++ b/sdk/dotnet/Helm/V4/Inputs/RepositoryOptsArgs.cs @@ -0,0 +1,69 @@ +// *** WARNING: this file was generated by pulumigen. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Pulumi.Serialization; + +namespace Pulumi.Kubernetes.Types.Inputs.Helm.V4 +{ + + /// + /// Specification defining the Helm chart repository to use. + /// + public class RepositoryOptsArgs : global::Pulumi.ResourceArgs + { + /// + /// The Repository's CA File + /// + [Input("caFile")] + public Input? CaFile { get; set; } + + /// + /// The repository's cert file + /// + [Input("certFile")] + public Input? CertFile { get; set; } + + /// + /// The repository's cert key file + /// + [Input("keyFile")] + public Input? KeyFile { get; set; } + + [Input("password")] + private Input? _password; + + /// + /// Password for HTTP basic authentication + /// + public Input? Password + { + get => _password; + set + { + var emptySecret = Output.CreateSecret(0); + _password = Output.Tuple?, int>(value, emptySecret).Apply(t => t.Item1); + } + } + + /// + /// Repository where to locate the requested chart. If is a URL the chart is installed without installing the repository. + /// + [Input("repo")] + public Input? Repo { get; set; } + + /// + /// Username for HTTP basic authentication + /// + [Input("username")] + public Input? Username { get; set; } + + public RepositoryOptsArgs() + { + } + public static new RepositoryOptsArgs Empty => new RepositoryOptsArgs(); + } +} diff --git a/sdk/dotnet/Inputs/HelmReleaseSettingsArgs.cs b/sdk/dotnet/Inputs/HelmReleaseSettingsArgs.cs index f106c4ecd7..43a542a77e 100644 --- a/sdk/dotnet/Inputs/HelmReleaseSettingsArgs.cs +++ b/sdk/dotnet/Inputs/HelmReleaseSettingsArgs.cs @@ -34,7 +34,7 @@ public class HelmReleaseSettingsArgs : global::Pulumi.ResourceArgs public Input? RegistryConfigPath { get; set; } /// - /// The path to the file containing cached repository indexes. + /// The path to the directory containing cached repository indexes. /// [Input("repositoryCache")] public Input? RepositoryCache { get; set; } diff --git a/sdk/dotnet/Inputs/KubeClientSettingsArgs.cs b/sdk/dotnet/Inputs/KubeClientSettingsArgs.cs index 5f1bd7ddc3..859551b9a6 100644 --- a/sdk/dotnet/Inputs/KubeClientSettingsArgs.cs +++ b/sdk/dotnet/Inputs/KubeClientSettingsArgs.cs @@ -16,13 +16,13 @@ namespace Pulumi.Kubernetes.Types.Inputs.Provider public class KubeClientSettingsArgs : global::Pulumi.ResourceArgs { /// - /// Maximum burst for throttle. Default value is 10. + /// Maximum burst for throttle. Default value is 120. /// [Input("burst")] public Input? Burst { get; set; } /// - /// Maximum queries per second (QPS) to the API server from this client. Default value is 5. + /// Maximum queries per second (QPS) to the API server from this client. Default value is 50. /// [Input("qps")] public Input? Qps { get; set; } diff --git a/sdk/go/kubernetes/helm/v4/chart.go b/sdk/go/kubernetes/helm/v4/chart.go new file mode 100644 index 0000000000..51cfb318a2 --- /dev/null +++ b/sdk/go/kubernetes/helm/v4/chart.go @@ -0,0 +1,482 @@ +// Code generated by pulumigen DO NOT EDIT. +// *** WARNING: Do not edit by hand unless you're certain you know what you are doing! *** + +package v4 + +import ( + "context" + "reflect" + + "errors" + "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/utilities" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +// Chart is a component representing a collection of resources described by a Helm Chart. +// Helm charts are a popular packaging format for Kubernetes applications, and published +// to registries such as [Artifact Hub](https://artifacthub.io/packages/search?kind=0&sort=relevance&page=1). +// +// Chart does not use Tiller or create a Helm Release; the semantics are equivalent to +// running `helm template --dry-run=server` and then using Pulumi to deploy the resulting YAML manifests. +// This allows you to apply [Pulumi Transformations](https://www.pulumi.com/docs/concepts/options/transformations/) and +// [Pulumi Policies](https://www.pulumi.com/docs/using-pulumi/crossguard/) to the Kubernetes resources. +// +// You may also want to consider the `Release` resource as an alternative method for managing helm charts. For more +// information about the trade-offs between these options, see: [Choosing the right Helm resource for your use case](https://www.pulumi.com/registry/packages/kubernetes/how-to-guides/choosing-the-right-helm-resource-for-your-use-case). +// +// ### Chart Resolution +// +// The Helm Chart can be fetched from any source that is accessible to the `helm` command line. +// The following variations are supported: +// +// 1. By chart reference with repo prefix: `chart: "example/mariadb"` +// 2. By path to a packaged chart: `chart: "./nginx-1.2.3.tgz"` +// 3. By path to an unpacked chart directory: `chart: "./nginx"` +// 4. By absolute URL: `chart: "https://example.com/charts/nginx-1.2.3.tgz"` +// 5. By chart reference with repo URL: `chart: "nginx", repositoryOpts: { repo: "https://example.com/charts/" }` +// 6. By OCI registry: `chart: "oci://example.com/charts/nginx", version: "1.2.3"` +// +// A chart reference is a convenient way of referencing a chart in a chart repository. +// +// When you use a chart reference with a repo prefix (`example/mariadb`), Pulumi will look in Helm's local configuration +// for a chart repository named `example`, and will then look for a chart in that repository whose name is `mariadb`. +// It will install the latest stable version of that chart, unless you specify `devel` to also include +// development versions (alpha, beta, and release candidate releases), or supply a version number with `version`. +// +// Use the `verify` and optional `keyring` inputs to enable Chart verification. +// By default, Pulumi uses the keyring at `$HOME/.gnupg/pubring.gpg`. See: [Helm Provenance and Integrity](https://helm.sh/docs/topics/provenance/). +// +// ### Chart Values +// +// [Values files](https://helm.sh/docs/chart_template_guide/values_files/#helm) (`values.yaml`) may be supplied +// with the `valueYamlFiles` input, accepting [Pulumi Assets](https://www.pulumi.com/docs/concepts/assets-archives/#assets). +// +// A map of chart values may also be supplied with the `values` input, with highest precedence. You're able to use literals, +// nested maps, [Pulumi outputs](https://www.pulumi.com/docs/concepts/inputs-outputs/), and Pulumi assets as values. +// Assets are automatically opened and converted to a string. +// +// Note that the use of expressions (e.g. `--set service.type`) is not supported. +// +// ### Chart Dependency Resolution +// +// For unpacked chart directories, Pulumi automatically rebuilds the dependencies if dependencies are missing +// and a `Chart.lock` file is present (see: [Helm Dependency Build](https://helm.sh/docs/helm/helm_dependency_build/)). +// Use the `dependencyUpdate` input to have Pulumi update the dependencies (see: [Helm Dependency Update](https://helm.sh/docs/helm/helm_dependency_update/)). +// +// ### Templating +// +// The `Chart` resource renders the templates from your chart and then manages the resources directly with the +// Pulumi Kubernetes provider. A default namespace is applied based on the `namespace` input, the provider's +// configured namespace, and the active Kubernetes context. Use the `skipCrds` option to skip installing the +// Custom Resource Definition (CRD) objects located in the chart's `crds/` special directory. +// +// Use the `postRenderer` input to pipe the rendered manifest through a [post-rendering command](https://helm.sh/docs/topics/advanced/#post-rendering). +// +// ### Resource Ordering +// +// Sometimes resources must be applied in a specific order. For example, a namespace resource must be +// created before any namespaced resources, or a Custom Resource Definition (CRD) must be pre-installed. +// +// Pulumi uses heuristics to determine which order to apply and delete objects within the Chart. Pulumi also +// waits for each object to be fully reconciled, unless `skipAwait` is enabled. +// +// Pulumi supports the `config.kubernetes.io/depends-on` annotation to declare an explicit dependency on a given resource. +// The annotation accepts a list of resource references, delimited by commas. +// +// Note that references to resources outside the Chart aren't supported. +// +// **Resource reference** +// +// A resource reference is a string that uniquely identifies a resource. +// +// It consists of the group, kind, name, and optionally the namespace, delimited by forward slashes. +// +// | Resource Scope | Format | +// | :--------------- | :--------------------------------------------- | +// | namespace-scoped | `/namespaces///` | +// | cluster-scoped | `//` | +// +// For resources in the “core” group, the empty string is used instead (for example: `/namespaces/test/Pod/pod-a`). +// +// ## Example Usage +// ### Local Chart Directory +// ```go +// package main +// +// import ( +// +// helmv4 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4" +// "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +// +// ) +// +// func main() { +// pulumi.Run(func(ctx *pulumi.Context) error { +// _, err := helmv4.NewChart(ctx, "nginx", &helmv4.ChartArgs{ +// Chart: pulumi.String("./nginx"), +// }) +// if err != nil { +// return err +// } +// return nil +// }) +// } +// +// ``` +// ### Repository Chart +// ```go +// package main +// +// import ( +// +// helmv4 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4" +// "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +// +// ) +// +// func main() { +// pulumi.Run(func(ctx *pulumi.Context) error { +// _, err := helmv4.NewChart(ctx, "nginx", &helmv4.ChartArgs{ +// Chart: pulumi.String("nginx"), +// RepositoryOpts: &helmv4.RepositoryOptsArgs{ +// Repo: pulumi.String("https://charts.bitnami.com/bitnami"), +// }, +// }) +// if err != nil { +// return err +// } +// return nil +// }) +// } +// +// ``` +// ### OCI Chart +// ```go +// package main +// +// import ( +// +// helmv4 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4" +// "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +// +// ) +// +// func main() { +// pulumi.Run(func(ctx *pulumi.Context) error { +// _, err := helmv4.NewChart(ctx, "nginx", &helmv4.ChartArgs{ +// Chart: pulumi.String("oci://registry-1.docker.io/bitnamicharts/nginx"), +// Version: pulumi.String("16.0.7"), +// }) +// if err != nil { +// return err +// } +// return nil +// }) +// } +// +// ``` +// ### Chart Values +// ```go +// package main +// +// import ( +// +// helmv4 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4" +// "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +// +// ) +// +// func main() { +// pulumi.Run(func(ctx *pulumi.Context) error { +// _, err := helmv4.NewChart(ctx, "nginx", &helmv4.ChartArgs{ +// Chart: pulumi.String("nginx"), +// RepositoryOpts: &helmv4.RepositoryOptsArgs{ +// Repo: pulumi.String("https://charts.bitnami.com/bitnami"), +// }, +// ValueYamlFiles: pulumi.AssetOrArchiveArray{ +// pulumi.NewFileAsset("./values.yaml"), +// }, +// Values: pulumi.Map{ +// "service": pulumi.Map{ +// "type": pulumi.String("ClusterIP"), +// }, +// "notes": pulumi.NewFileAsset("./notes.txt"), +// }, +// }) +// if err != nil { +// return err +// } +// return nil +// }) +// } +// +// ``` +// ### Chart Namespace +// ```go +// package main +// +// import ( +// +// corev1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/core/v1" +// helmv4 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v4" +// metav1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/meta/v1" +// "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +// +// ) +// +// func main() { +// pulumi.Run(func(ctx *pulumi.Context) error { +// ns, err := corev1.NewNamespace(ctx, "nginx", &corev1.NamespaceArgs{ +// Metadata: &metav1.ObjectMetaArgs{Name: pulumi.String("nginx")}, +// }) +// if err != nil { +// return err +// } +// _, err = helmv4.NewChart(ctx, "nginx", &helmv4.ChartArgs{ +// Namespace: ns.Metadata.Name(), +// Chart: pulumi.String("nginx"), +// RepositoryOpts: &helmv4.RepositoryOptsArgs{ +// Repo: pulumi.String("https://charts.bitnami.com/bitnami"), +// }, +// }) +// if err != nil { +// return err +// } +// return nil +// }) +// } +// +// ``` +type Chart struct { + pulumi.ResourceState + + // Resources created by the Chart. + Resources pulumi.ArrayOutput `pulumi:"resources"` +} + +// NewChart registers a new resource with the given unique name, arguments, and options. +func NewChart(ctx *pulumi.Context, + name string, args *ChartArgs, opts ...pulumi.ResourceOption) (*Chart, error) { + if args == nil { + return nil, errors.New("missing one or more required arguments") + } + + if args.Chart == nil { + return nil, errors.New("invalid value for required argument 'Chart'") + } + opts = utilities.PkgResourceDefaultOpts(opts) + var resource Chart + err := ctx.RegisterRemoteComponentResource("kubernetes:helm.sh/v4:Chart", name, args, &resource, opts...) + if err != nil { + return nil, err + } + return &resource, nil +} + +type chartArgs struct { + // Chart name to be installed. A path may be used. + Chart string `pulumi:"chart"` + // Run helm dependency update before installing the chart. + DependencyUpdate *bool `pulumi:"dependencyUpdate"` + // Use chart development versions, too. Equivalent to version '>0.0.0-0'. If `version` is set, this is ignored. + Devel *bool `pulumi:"devel"` + // Location of public keys used for verification. Used only if `verify` is true + Keyring pulumi.AssetOrArchive `pulumi:"keyring"` + // Release name. + Name *string `pulumi:"name"` + // Namespace for the release. + Namespace *string `pulumi:"namespace"` + // Specification defining the post-renderer to use. + PostRenderer *PostRenderer `pulumi:"postRenderer"` + // Specification defining the Helm chart repository to use. + RepositoryOpts *RepositoryOpts `pulumi:"repositoryOpts"` + // An optional prefix for the auto-generated resource names. Example: A resource created with resourcePrefix="foo" would produce a resource named "foo:resourceName". + ResourcePrefix *string `pulumi:"resourcePrefix"` + // By default, the provider waits until all resources are in a ready state before marking the release as successful. Setting this to true will skip such await logic. + SkipAwait *bool `pulumi:"skipAwait"` + // If set, no CRDs will be installed. By default, CRDs are installed if not already present. + SkipCrds *bool `pulumi:"skipCrds"` + // List of assets (raw yaml files). Content is read and merged with values. + ValueYamlFiles []pulumi.AssetOrArchive `pulumi:"valueYamlFiles"` + // Custom values set for the release. + Values map[string]interface{} `pulumi:"values"` + // Verify the chart's integrity. + Verify *bool `pulumi:"verify"` + // Specify the chart version to install. If this is not specified, the latest version is installed. + Version *string `pulumi:"version"` +} + +// The set of arguments for constructing a Chart resource. +type ChartArgs struct { + // Chart name to be installed. A path may be used. + Chart pulumi.StringInput + // Run helm dependency update before installing the chart. + DependencyUpdate pulumi.BoolPtrInput + // Use chart development versions, too. Equivalent to version '>0.0.0-0'. If `version` is set, this is ignored. + Devel pulumi.BoolPtrInput + // Location of public keys used for verification. Used only if `verify` is true + Keyring pulumi.AssetOrArchiveInput + // Release name. + Name pulumi.StringPtrInput + // Namespace for the release. + Namespace pulumi.StringPtrInput + // Specification defining the post-renderer to use. + PostRenderer PostRendererPtrInput + // Specification defining the Helm chart repository to use. + RepositoryOpts RepositoryOptsPtrInput + // An optional prefix for the auto-generated resource names. Example: A resource created with resourcePrefix="foo" would produce a resource named "foo:resourceName". + ResourcePrefix pulumi.StringPtrInput + // By default, the provider waits until all resources are in a ready state before marking the release as successful. Setting this to true will skip such await logic. + SkipAwait pulumi.BoolPtrInput + // If set, no CRDs will be installed. By default, CRDs are installed if not already present. + SkipCrds pulumi.BoolPtrInput + // List of assets (raw yaml files). Content is read and merged with values. + ValueYamlFiles pulumi.AssetOrArchiveArrayInput + // Custom values set for the release. + Values pulumi.MapInput + // Verify the chart's integrity. + Verify pulumi.BoolPtrInput + // Specify the chart version to install. If this is not specified, the latest version is installed. + Version pulumi.StringPtrInput +} + +func (ChartArgs) ElementType() reflect.Type { + return reflect.TypeOf((*chartArgs)(nil)).Elem() +} + +type ChartInput interface { + pulumi.Input + + ToChartOutput() ChartOutput + ToChartOutputWithContext(ctx context.Context) ChartOutput +} + +func (*Chart) ElementType() reflect.Type { + return reflect.TypeOf((**Chart)(nil)).Elem() +} + +func (i *Chart) ToChartOutput() ChartOutput { + return i.ToChartOutputWithContext(context.Background()) +} + +func (i *Chart) ToChartOutputWithContext(ctx context.Context) ChartOutput { + return pulumi.ToOutputWithContext(ctx, i).(ChartOutput) +} + +// ChartArrayInput is an input type that accepts ChartArray and ChartArrayOutput values. +// You can construct a concrete instance of `ChartArrayInput` via: +// +// ChartArray{ ChartArgs{...} } +type ChartArrayInput interface { + pulumi.Input + + ToChartArrayOutput() ChartArrayOutput + ToChartArrayOutputWithContext(context.Context) ChartArrayOutput +} + +type ChartArray []ChartInput + +func (ChartArray) ElementType() reflect.Type { + return reflect.TypeOf((*[]*Chart)(nil)).Elem() +} + +func (i ChartArray) ToChartArrayOutput() ChartArrayOutput { + return i.ToChartArrayOutputWithContext(context.Background()) +} + +func (i ChartArray) ToChartArrayOutputWithContext(ctx context.Context) ChartArrayOutput { + return pulumi.ToOutputWithContext(ctx, i).(ChartArrayOutput) +} + +// ChartMapInput is an input type that accepts ChartMap and ChartMapOutput values. +// You can construct a concrete instance of `ChartMapInput` via: +// +// ChartMap{ "key": ChartArgs{...} } +type ChartMapInput interface { + pulumi.Input + + ToChartMapOutput() ChartMapOutput + ToChartMapOutputWithContext(context.Context) ChartMapOutput +} + +type ChartMap map[string]ChartInput + +func (ChartMap) ElementType() reflect.Type { + return reflect.TypeOf((*map[string]*Chart)(nil)).Elem() +} + +func (i ChartMap) ToChartMapOutput() ChartMapOutput { + return i.ToChartMapOutputWithContext(context.Background()) +} + +func (i ChartMap) ToChartMapOutputWithContext(ctx context.Context) ChartMapOutput { + return pulumi.ToOutputWithContext(ctx, i).(ChartMapOutput) +} + +type ChartOutput struct{ *pulumi.OutputState } + +func (ChartOutput) ElementType() reflect.Type { + return reflect.TypeOf((**Chart)(nil)).Elem() +} + +func (o ChartOutput) ToChartOutput() ChartOutput { + return o +} + +func (o ChartOutput) ToChartOutputWithContext(ctx context.Context) ChartOutput { + return o +} + +// Resources created by the Chart. +func (o ChartOutput) Resources() pulumi.ArrayOutput { + return o.ApplyT(func(v *Chart) pulumi.ArrayOutput { return v.Resources }).(pulumi.ArrayOutput) +} + +type ChartArrayOutput struct{ *pulumi.OutputState } + +func (ChartArrayOutput) ElementType() reflect.Type { + return reflect.TypeOf((*[]*Chart)(nil)).Elem() +} + +func (o ChartArrayOutput) ToChartArrayOutput() ChartArrayOutput { + return o +} + +func (o ChartArrayOutput) ToChartArrayOutputWithContext(ctx context.Context) ChartArrayOutput { + return o +} + +func (o ChartArrayOutput) Index(i pulumi.IntInput) ChartOutput { + return pulumi.All(o, i).ApplyT(func(vs []interface{}) *Chart { + return vs[0].([]*Chart)[vs[1].(int)] + }).(ChartOutput) +} + +type ChartMapOutput struct{ *pulumi.OutputState } + +func (ChartMapOutput) ElementType() reflect.Type { + return reflect.TypeOf((*map[string]*Chart)(nil)).Elem() +} + +func (o ChartMapOutput) ToChartMapOutput() ChartMapOutput { + return o +} + +func (o ChartMapOutput) ToChartMapOutputWithContext(ctx context.Context) ChartMapOutput { + return o +} + +func (o ChartMapOutput) MapIndex(k pulumi.StringInput) ChartOutput { + return pulumi.All(o, k).ApplyT(func(vs []interface{}) *Chart { + return vs[0].(map[string]*Chart)[vs[1].(string)] + }).(ChartOutput) +} + +func init() { + pulumi.RegisterInputType(reflect.TypeOf((*ChartInput)(nil)).Elem(), &Chart{}) + pulumi.RegisterInputType(reflect.TypeOf((*ChartArrayInput)(nil)).Elem(), ChartArray{}) + pulumi.RegisterInputType(reflect.TypeOf((*ChartMapInput)(nil)).Elem(), ChartMap{}) + pulumi.RegisterOutputType(ChartOutput{}) + pulumi.RegisterOutputType(ChartArrayOutput{}) + pulumi.RegisterOutputType(ChartMapOutput{}) +} diff --git a/sdk/go/kubernetes/helm/v4/init.go b/sdk/go/kubernetes/helm/v4/init.go new file mode 100644 index 0000000000..760816d43c --- /dev/null +++ b/sdk/go/kubernetes/helm/v4/init.go @@ -0,0 +1,44 @@ +// Code generated by pulumigen DO NOT EDIT. +// *** WARNING: Do not edit by hand unless you're certain you know what you are doing! *** + +package v4 + +import ( + "fmt" + + "github.com/blang/semver" + "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/utilities" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +type module struct { + version semver.Version +} + +func (m *module) Version() semver.Version { + return m.version +} + +func (m *module) Construct(ctx *pulumi.Context, name, typ, urn string) (r pulumi.Resource, err error) { + switch typ { + case "kubernetes:helm.sh/v4:Chart": + r = &Chart{} + default: + return nil, fmt.Errorf("unknown resource type: %s", typ) + } + + err = ctx.RegisterResource(typ, name, nil, r, pulumi.URN_(urn)) + return +} + +func init() { + version, err := utilities.PkgVersion() + if err != nil { + version = semver.Version{Major: 1} + } + pulumi.RegisterResourceModule( + "kubernetes", + "helm.sh/v4", + &module{version}, + ) +} diff --git a/sdk/go/kubernetes/helm/v4/pulumiTypes.go b/sdk/go/kubernetes/helm/v4/pulumiTypes.go new file mode 100644 index 0000000000..70f3215cd5 --- /dev/null +++ b/sdk/go/kubernetes/helm/v4/pulumiTypes.go @@ -0,0 +1,419 @@ +// Code generated by pulumigen DO NOT EDIT. +// *** WARNING: Do not edit by hand unless you're certain you know what you are doing! *** + +package v4 + +import ( + "context" + "reflect" + + "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/utilities" + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" +) + +var _ = utilities.GetEnvOrDefault + +// Specification defining the post-renderer to use. +type PostRenderer struct { + // Arguments to pass to the post-renderer command. + Args []string `pulumi:"args"` + // Path to an executable to be used for post rendering. + Command string `pulumi:"command"` +} + +// PostRendererInput is an input type that accepts PostRendererArgs and PostRendererOutput values. +// You can construct a concrete instance of `PostRendererInput` via: +// +// PostRendererArgs{...} +type PostRendererInput interface { + pulumi.Input + + ToPostRendererOutput() PostRendererOutput + ToPostRendererOutputWithContext(context.Context) PostRendererOutput +} + +// Specification defining the post-renderer to use. +type PostRendererArgs struct { + // Arguments to pass to the post-renderer command. + Args pulumi.StringArrayInput `pulumi:"args"` + // Path to an executable to be used for post rendering. + Command pulumi.StringInput `pulumi:"command"` +} + +func (PostRendererArgs) ElementType() reflect.Type { + return reflect.TypeOf((*PostRenderer)(nil)).Elem() +} + +func (i PostRendererArgs) ToPostRendererOutput() PostRendererOutput { + return i.ToPostRendererOutputWithContext(context.Background()) +} + +func (i PostRendererArgs) ToPostRendererOutputWithContext(ctx context.Context) PostRendererOutput { + return pulumi.ToOutputWithContext(ctx, i).(PostRendererOutput) +} + +func (i PostRendererArgs) ToPostRendererPtrOutput() PostRendererPtrOutput { + return i.ToPostRendererPtrOutputWithContext(context.Background()) +} + +func (i PostRendererArgs) ToPostRendererPtrOutputWithContext(ctx context.Context) PostRendererPtrOutput { + return pulumi.ToOutputWithContext(ctx, i).(PostRendererOutput).ToPostRendererPtrOutputWithContext(ctx) +} + +// PostRendererPtrInput is an input type that accepts PostRendererArgs, PostRendererPtr and PostRendererPtrOutput values. +// You can construct a concrete instance of `PostRendererPtrInput` via: +// +// PostRendererArgs{...} +// +// or: +// +// nil +type PostRendererPtrInput interface { + pulumi.Input + + ToPostRendererPtrOutput() PostRendererPtrOutput + ToPostRendererPtrOutputWithContext(context.Context) PostRendererPtrOutput +} + +type postRendererPtrType PostRendererArgs + +func PostRendererPtr(v *PostRendererArgs) PostRendererPtrInput { + return (*postRendererPtrType)(v) +} + +func (*postRendererPtrType) ElementType() reflect.Type { + return reflect.TypeOf((**PostRenderer)(nil)).Elem() +} + +func (i *postRendererPtrType) ToPostRendererPtrOutput() PostRendererPtrOutput { + return i.ToPostRendererPtrOutputWithContext(context.Background()) +} + +func (i *postRendererPtrType) ToPostRendererPtrOutputWithContext(ctx context.Context) PostRendererPtrOutput { + return pulumi.ToOutputWithContext(ctx, i).(PostRendererPtrOutput) +} + +// Specification defining the post-renderer to use. +type PostRendererOutput struct{ *pulumi.OutputState } + +func (PostRendererOutput) ElementType() reflect.Type { + return reflect.TypeOf((*PostRenderer)(nil)).Elem() +} + +func (o PostRendererOutput) ToPostRendererOutput() PostRendererOutput { + return o +} + +func (o PostRendererOutput) ToPostRendererOutputWithContext(ctx context.Context) PostRendererOutput { + return o +} + +func (o PostRendererOutput) ToPostRendererPtrOutput() PostRendererPtrOutput { + return o.ToPostRendererPtrOutputWithContext(context.Background()) +} + +func (o PostRendererOutput) ToPostRendererPtrOutputWithContext(ctx context.Context) PostRendererPtrOutput { + return o.ApplyTWithContext(ctx, func(_ context.Context, v PostRenderer) *PostRenderer { + return &v + }).(PostRendererPtrOutput) +} + +// Arguments to pass to the post-renderer command. +func (o PostRendererOutput) Args() pulumi.StringArrayOutput { + return o.ApplyT(func(v PostRenderer) []string { return v.Args }).(pulumi.StringArrayOutput) +} + +// Path to an executable to be used for post rendering. +func (o PostRendererOutput) Command() pulumi.StringOutput { + return o.ApplyT(func(v PostRenderer) string { return v.Command }).(pulumi.StringOutput) +} + +type PostRendererPtrOutput struct{ *pulumi.OutputState } + +func (PostRendererPtrOutput) ElementType() reflect.Type { + return reflect.TypeOf((**PostRenderer)(nil)).Elem() +} + +func (o PostRendererPtrOutput) ToPostRendererPtrOutput() PostRendererPtrOutput { + return o +} + +func (o PostRendererPtrOutput) ToPostRendererPtrOutputWithContext(ctx context.Context) PostRendererPtrOutput { + return o +} + +func (o PostRendererPtrOutput) Elem() PostRendererOutput { + return o.ApplyT(func(v *PostRenderer) PostRenderer { + if v != nil { + return *v + } + var ret PostRenderer + return ret + }).(PostRendererOutput) +} + +// Arguments to pass to the post-renderer command. +func (o PostRendererPtrOutput) Args() pulumi.StringArrayOutput { + return o.ApplyT(func(v *PostRenderer) []string { + if v == nil { + return nil + } + return v.Args + }).(pulumi.StringArrayOutput) +} + +// Path to an executable to be used for post rendering. +func (o PostRendererPtrOutput) Command() pulumi.StringPtrOutput { + return o.ApplyT(func(v *PostRenderer) *string { + if v == nil { + return nil + } + return &v.Command + }).(pulumi.StringPtrOutput) +} + +// Specification defining the Helm chart repository to use. +type RepositoryOpts struct { + // The Repository's CA File + CaFile pulumi.AssetOrArchive `pulumi:"caFile"` + // The repository's cert file + CertFile pulumi.AssetOrArchive `pulumi:"certFile"` + // The repository's cert key file + KeyFile pulumi.AssetOrArchive `pulumi:"keyFile"` + // Password for HTTP basic authentication + Password *string `pulumi:"password"` + // Repository where to locate the requested chart. If is a URL the chart is installed without installing the repository. + Repo *string `pulumi:"repo"` + // Username for HTTP basic authentication + Username *string `pulumi:"username"` +} + +// RepositoryOptsInput is an input type that accepts RepositoryOptsArgs and RepositoryOptsOutput values. +// You can construct a concrete instance of `RepositoryOptsInput` via: +// +// RepositoryOptsArgs{...} +type RepositoryOptsInput interface { + pulumi.Input + + ToRepositoryOptsOutput() RepositoryOptsOutput + ToRepositoryOptsOutputWithContext(context.Context) RepositoryOptsOutput +} + +// Specification defining the Helm chart repository to use. +type RepositoryOptsArgs struct { + // The Repository's CA File + CaFile pulumi.AssetOrArchiveInput `pulumi:"caFile"` + // The repository's cert file + CertFile pulumi.AssetOrArchiveInput `pulumi:"certFile"` + // The repository's cert key file + KeyFile pulumi.AssetOrArchiveInput `pulumi:"keyFile"` + // Password for HTTP basic authentication + Password pulumi.StringPtrInput `pulumi:"password"` + // Repository where to locate the requested chart. If is a URL the chart is installed without installing the repository. + Repo pulumi.StringPtrInput `pulumi:"repo"` + // Username for HTTP basic authentication + Username pulumi.StringPtrInput `pulumi:"username"` +} + +func (RepositoryOptsArgs) ElementType() reflect.Type { + return reflect.TypeOf((*RepositoryOpts)(nil)).Elem() +} + +func (i RepositoryOptsArgs) ToRepositoryOptsOutput() RepositoryOptsOutput { + return i.ToRepositoryOptsOutputWithContext(context.Background()) +} + +func (i RepositoryOptsArgs) ToRepositoryOptsOutputWithContext(ctx context.Context) RepositoryOptsOutput { + return pulumi.ToOutputWithContext(ctx, i).(RepositoryOptsOutput) +} + +func (i RepositoryOptsArgs) ToRepositoryOptsPtrOutput() RepositoryOptsPtrOutput { + return i.ToRepositoryOptsPtrOutputWithContext(context.Background()) +} + +func (i RepositoryOptsArgs) ToRepositoryOptsPtrOutputWithContext(ctx context.Context) RepositoryOptsPtrOutput { + return pulumi.ToOutputWithContext(ctx, i).(RepositoryOptsOutput).ToRepositoryOptsPtrOutputWithContext(ctx) +} + +// RepositoryOptsPtrInput is an input type that accepts RepositoryOptsArgs, RepositoryOptsPtr and RepositoryOptsPtrOutput values. +// You can construct a concrete instance of `RepositoryOptsPtrInput` via: +// +// RepositoryOptsArgs{...} +// +// or: +// +// nil +type RepositoryOptsPtrInput interface { + pulumi.Input + + ToRepositoryOptsPtrOutput() RepositoryOptsPtrOutput + ToRepositoryOptsPtrOutputWithContext(context.Context) RepositoryOptsPtrOutput +} + +type repositoryOptsPtrType RepositoryOptsArgs + +func RepositoryOptsPtr(v *RepositoryOptsArgs) RepositoryOptsPtrInput { + return (*repositoryOptsPtrType)(v) +} + +func (*repositoryOptsPtrType) ElementType() reflect.Type { + return reflect.TypeOf((**RepositoryOpts)(nil)).Elem() +} + +func (i *repositoryOptsPtrType) ToRepositoryOptsPtrOutput() RepositoryOptsPtrOutput { + return i.ToRepositoryOptsPtrOutputWithContext(context.Background()) +} + +func (i *repositoryOptsPtrType) ToRepositoryOptsPtrOutputWithContext(ctx context.Context) RepositoryOptsPtrOutput { + return pulumi.ToOutputWithContext(ctx, i).(RepositoryOptsPtrOutput) +} + +// Specification defining the Helm chart repository to use. +type RepositoryOptsOutput struct{ *pulumi.OutputState } + +func (RepositoryOptsOutput) ElementType() reflect.Type { + return reflect.TypeOf((*RepositoryOpts)(nil)).Elem() +} + +func (o RepositoryOptsOutput) ToRepositoryOptsOutput() RepositoryOptsOutput { + return o +} + +func (o RepositoryOptsOutput) ToRepositoryOptsOutputWithContext(ctx context.Context) RepositoryOptsOutput { + return o +} + +func (o RepositoryOptsOutput) ToRepositoryOptsPtrOutput() RepositoryOptsPtrOutput { + return o.ToRepositoryOptsPtrOutputWithContext(context.Background()) +} + +func (o RepositoryOptsOutput) ToRepositoryOptsPtrOutputWithContext(ctx context.Context) RepositoryOptsPtrOutput { + return o.ApplyTWithContext(ctx, func(_ context.Context, v RepositoryOpts) *RepositoryOpts { + return &v + }).(RepositoryOptsPtrOutput) +} + +// The Repository's CA File +func (o RepositoryOptsOutput) CaFile() pulumi.AssetOrArchiveOutput { + return o.ApplyT(func(v RepositoryOpts) pulumi.AssetOrArchive { return v.CaFile }).(pulumi.AssetOrArchiveOutput) +} + +// The repository's cert file +func (o RepositoryOptsOutput) CertFile() pulumi.AssetOrArchiveOutput { + return o.ApplyT(func(v RepositoryOpts) pulumi.AssetOrArchive { return v.CertFile }).(pulumi.AssetOrArchiveOutput) +} + +// The repository's cert key file +func (o RepositoryOptsOutput) KeyFile() pulumi.AssetOrArchiveOutput { + return o.ApplyT(func(v RepositoryOpts) pulumi.AssetOrArchive { return v.KeyFile }).(pulumi.AssetOrArchiveOutput) +} + +// Password for HTTP basic authentication +func (o RepositoryOptsOutput) Password() pulumi.StringPtrOutput { + return o.ApplyT(func(v RepositoryOpts) *string { return v.Password }).(pulumi.StringPtrOutput) +} + +// Repository where to locate the requested chart. If is a URL the chart is installed without installing the repository. +func (o RepositoryOptsOutput) Repo() pulumi.StringPtrOutput { + return o.ApplyT(func(v RepositoryOpts) *string { return v.Repo }).(pulumi.StringPtrOutput) +} + +// Username for HTTP basic authentication +func (o RepositoryOptsOutput) Username() pulumi.StringPtrOutput { + return o.ApplyT(func(v RepositoryOpts) *string { return v.Username }).(pulumi.StringPtrOutput) +} + +type RepositoryOptsPtrOutput struct{ *pulumi.OutputState } + +func (RepositoryOptsPtrOutput) ElementType() reflect.Type { + return reflect.TypeOf((**RepositoryOpts)(nil)).Elem() +} + +func (o RepositoryOptsPtrOutput) ToRepositoryOptsPtrOutput() RepositoryOptsPtrOutput { + return o +} + +func (o RepositoryOptsPtrOutput) ToRepositoryOptsPtrOutputWithContext(ctx context.Context) RepositoryOptsPtrOutput { + return o +} + +func (o RepositoryOptsPtrOutput) Elem() RepositoryOptsOutput { + return o.ApplyT(func(v *RepositoryOpts) RepositoryOpts { + if v != nil { + return *v + } + var ret RepositoryOpts + return ret + }).(RepositoryOptsOutput) +} + +// The Repository's CA File +func (o RepositoryOptsPtrOutput) CaFile() pulumi.AssetOrArchiveOutput { + return o.ApplyT(func(v *RepositoryOpts) pulumi.AssetOrArchive { + if v == nil { + return nil + } + return v.CaFile + }).(pulumi.AssetOrArchiveOutput) +} + +// The repository's cert file +func (o RepositoryOptsPtrOutput) CertFile() pulumi.AssetOrArchiveOutput { + return o.ApplyT(func(v *RepositoryOpts) pulumi.AssetOrArchive { + if v == nil { + return nil + } + return v.CertFile + }).(pulumi.AssetOrArchiveOutput) +} + +// The repository's cert key file +func (o RepositoryOptsPtrOutput) KeyFile() pulumi.AssetOrArchiveOutput { + return o.ApplyT(func(v *RepositoryOpts) pulumi.AssetOrArchive { + if v == nil { + return nil + } + return v.KeyFile + }).(pulumi.AssetOrArchiveOutput) +} + +// Password for HTTP basic authentication +func (o RepositoryOptsPtrOutput) Password() pulumi.StringPtrOutput { + return o.ApplyT(func(v *RepositoryOpts) *string { + if v == nil { + return nil + } + return v.Password + }).(pulumi.StringPtrOutput) +} + +// Repository where to locate the requested chart. If is a URL the chart is installed without installing the repository. +func (o RepositoryOptsPtrOutput) Repo() pulumi.StringPtrOutput { + return o.ApplyT(func(v *RepositoryOpts) *string { + if v == nil { + return nil + } + return v.Repo + }).(pulumi.StringPtrOutput) +} + +// Username for HTTP basic authentication +func (o RepositoryOptsPtrOutput) Username() pulumi.StringPtrOutput { + return o.ApplyT(func(v *RepositoryOpts) *string { + if v == nil { + return nil + } + return v.Username + }).(pulumi.StringPtrOutput) +} + +func init() { + pulumi.RegisterInputType(reflect.TypeOf((*PostRendererInput)(nil)).Elem(), PostRendererArgs{}) + pulumi.RegisterInputType(reflect.TypeOf((*PostRendererPtrInput)(nil)).Elem(), PostRendererArgs{}) + pulumi.RegisterInputType(reflect.TypeOf((*RepositoryOptsInput)(nil)).Elem(), RepositoryOptsArgs{}) + pulumi.RegisterInputType(reflect.TypeOf((*RepositoryOptsPtrInput)(nil)).Elem(), RepositoryOptsArgs{}) + pulumi.RegisterOutputType(PostRendererOutput{}) + pulumi.RegisterOutputType(PostRendererPtrOutput{}) + pulumi.RegisterOutputType(RepositoryOptsOutput{}) + pulumi.RegisterOutputType(RepositoryOptsPtrOutput{}) +} diff --git a/sdk/go/kubernetes/pulumiTypes.go b/sdk/go/kubernetes/pulumiTypes.go index 58bc248b19..0d0601fdff 100644 --- a/sdk/go/kubernetes/pulumiTypes.go +++ b/sdk/go/kubernetes/pulumiTypes.go @@ -21,7 +21,7 @@ type HelmReleaseSettings struct { PluginsPath *string `pulumi:"pluginsPath"` // The path to the registry config file. RegistryConfigPath *string `pulumi:"registryConfigPath"` - // The path to the file containing cached repository indexes. + // The path to the directory containing cached repository indexes. RepositoryCache *string `pulumi:"repositoryCache"` // The path to the file containing repository names and URLs. RepositoryConfigPath *string `pulumi:"repositoryConfigPath"` @@ -85,7 +85,7 @@ type HelmReleaseSettingsArgs struct { PluginsPath pulumi.StringPtrInput `pulumi:"pluginsPath"` // The path to the registry config file. RegistryConfigPath pulumi.StringPtrInput `pulumi:"registryConfigPath"` - // The path to the file containing cached repository indexes. + // The path to the directory containing cached repository indexes. RepositoryCache pulumi.StringPtrInput `pulumi:"repositoryCache"` // The path to the file containing repository names and URLs. RepositoryConfigPath pulumi.StringPtrInput `pulumi:"repositoryConfigPath"` @@ -217,7 +217,7 @@ func (o HelmReleaseSettingsOutput) RegistryConfigPath() pulumi.StringPtrOutput { return o.ApplyT(func(v HelmReleaseSettings) *string { return v.RegistryConfigPath }).(pulumi.StringPtrOutput) } -// The path to the file containing cached repository indexes. +// The path to the directory containing cached repository indexes. func (o HelmReleaseSettingsOutput) RepositoryCache() pulumi.StringPtrOutput { return o.ApplyT(func(v HelmReleaseSettings) *string { return v.RepositoryCache }).(pulumi.StringPtrOutput) } @@ -281,7 +281,7 @@ func (o HelmReleaseSettingsPtrOutput) RegistryConfigPath() pulumi.StringPtrOutpu }).(pulumi.StringPtrOutput) } -// The path to the file containing cached repository indexes. +// The path to the directory containing cached repository indexes. func (o HelmReleaseSettingsPtrOutput) RepositoryCache() pulumi.StringPtrOutput { return o.ApplyT(func(v *HelmReleaseSettings) *string { if v == nil { @@ -303,9 +303,9 @@ func (o HelmReleaseSettingsPtrOutput) RepositoryConfigPath() pulumi.StringPtrOut // Options for tuning the Kubernetes client used by a Provider. type KubeClientSettings struct { - // Maximum burst for throttle. Default value is 10. + // Maximum burst for throttle. Default value is 120. Burst *int `pulumi:"burst"` - // Maximum queries per second (QPS) to the API server from this client. Default value is 5. + // Maximum queries per second (QPS) to the API server from this client. Default value is 50. Qps *float64 `pulumi:"qps"` // Maximum time in seconds to wait before cancelling a HTTP request to the Kubernetes server. Default value is 32. Timeout *int `pulumi:"timeout"` @@ -351,9 +351,9 @@ type KubeClientSettingsInput interface { // Options for tuning the Kubernetes client used by a Provider. type KubeClientSettingsArgs struct { - // Maximum burst for throttle. Default value is 10. + // Maximum burst for throttle. Default value is 120. Burst pulumi.IntPtrInput `pulumi:"burst"` - // Maximum queries per second (QPS) to the API server from this client. Default value is 5. + // Maximum queries per second (QPS) to the API server from this client. Default value is 50. Qps pulumi.Float64PtrInput `pulumi:"qps"` // Maximum time in seconds to wait before cancelling a HTTP request to the Kubernetes server. Default value is 32. Timeout pulumi.IntPtrInput `pulumi:"timeout"` @@ -460,12 +460,12 @@ func (o KubeClientSettingsOutput) ToKubeClientSettingsPtrOutputWithContext(ctx c }).(KubeClientSettingsPtrOutput) } -// Maximum burst for throttle. Default value is 10. +// Maximum burst for throttle. Default value is 120. func (o KubeClientSettingsOutput) Burst() pulumi.IntPtrOutput { return o.ApplyT(func(v KubeClientSettings) *int { return v.Burst }).(pulumi.IntPtrOutput) } -// Maximum queries per second (QPS) to the API server from this client. Default value is 5. +// Maximum queries per second (QPS) to the API server from this client. Default value is 50. func (o KubeClientSettingsOutput) Qps() pulumi.Float64PtrOutput { return o.ApplyT(func(v KubeClientSettings) *float64 { return v.Qps }).(pulumi.Float64PtrOutput) } @@ -499,7 +499,7 @@ func (o KubeClientSettingsPtrOutput) Elem() KubeClientSettingsOutput { }).(KubeClientSettingsOutput) } -// Maximum burst for throttle. Default value is 10. +// Maximum burst for throttle. Default value is 120. func (o KubeClientSettingsPtrOutput) Burst() pulumi.IntPtrOutput { return o.ApplyT(func(v *KubeClientSettings) *int { if v == nil { @@ -509,7 +509,7 @@ func (o KubeClientSettingsPtrOutput) Burst() pulumi.IntPtrOutput { }).(pulumi.IntPtrOutput) } -// Maximum queries per second (QPS) to the API server from this client. Default value is 5. +// Maximum queries per second (QPS) to the API server from this client. Default value is 50. func (o KubeClientSettingsPtrOutput) Qps() pulumi.Float64PtrOutput { return o.ApplyT(func(v *KubeClientSettings) *float64 { if v == nil { diff --git a/sdk/java/src/main/java/com/pulumi/kubernetes/helm/v4/Chart.java b/sdk/java/src/main/java/com/pulumi/kubernetes/helm/v4/Chart.java new file mode 100644 index 0000000000..2dbefb8e9b --- /dev/null +++ b/sdk/java/src/main/java/com/pulumi/kubernetes/helm/v4/Chart.java @@ -0,0 +1,277 @@ +// *** WARNING: this file was generated by pulumi-java-gen. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +package com.pulumi.kubernetes.helm.v4; + +import com.pulumi.core.Output; +import com.pulumi.core.annotations.Export; +import com.pulumi.core.annotations.ResourceType; +import com.pulumi.core.internal.Codegen; +import com.pulumi.kubernetes.Utilities; +import com.pulumi.kubernetes.helm.v4.ChartArgs; +import java.lang.Object; +import java.util.List; +import java.util.Optional; +import javax.annotation.Nullable; + +/** + * Chart is a component representing a collection of resources described by a Helm Chart. + * Helm charts are a popular packaging format for Kubernetes applications, and published + * to registries such as [Artifact Hub](https://artifacthub.io/packages/search?kind=0&sort=relevance&page=1). + * + * Chart does not use Tiller or create a Helm Release; the semantics are equivalent to + * running `helm template --dry-run=server` and then using Pulumi to deploy the resulting YAML manifests. + * This allows you to apply [Pulumi Transformations](https://www.pulumi.com/docs/concepts/options/transformations/) and + * [Pulumi Policies](https://www.pulumi.com/docs/using-pulumi/crossguard/) to the Kubernetes resources. + * + * You may also want to consider the `Release` resource as an alternative method for managing helm charts. For more + * information about the trade-offs between these options, see: [Choosing the right Helm resource for your use case](https://www.pulumi.com/registry/packages/kubernetes/how-to-guides/choosing-the-right-helm-resource-for-your-use-case). + * + * ### Chart Resolution + * + * The Helm Chart can be fetched from any source that is accessible to the `helm` command line. + * The following variations are supported: + * + * 1. By chart reference with repo prefix: `chart: "example/mariadb"` + * 2. By path to a packaged chart: `chart: "./nginx-1.2.3.tgz"` + * 3. By path to an unpacked chart directory: `chart: "./nginx"` + * 4. By absolute URL: `chart: "https://example.com/charts/nginx-1.2.3.tgz"` + * 5. By chart reference with repo URL: `chart: "nginx", repositoryOpts: { repo: "https://example.com/charts/" }` + * 6. By OCI registry: `chart: "oci://example.com/charts/nginx", version: "1.2.3"` + * + * A chart reference is a convenient way of referencing a chart in a chart repository. + * + * When you use a chart reference with a repo prefix (`example/mariadb`), Pulumi will look in Helm's local configuration + * for a chart repository named `example`, and will then look for a chart in that repository whose name is `mariadb`. + * It will install the latest stable version of that chart, unless you specify `devel` to also include + * development versions (alpha, beta, and release candidate releases), or supply a version number with `version`. + * + * Use the `verify` and optional `keyring` inputs to enable Chart verification. + * By default, Pulumi uses the keyring at `$HOME/.gnupg/pubring.gpg`. See: [Helm Provenance and Integrity](https://helm.sh/docs/topics/provenance/). + * + * ### Chart Values + * + * [Values files](https://helm.sh/docs/chart_template_guide/values_files/#helm) (`values.yaml`) may be supplied + * with the `valueYamlFiles` input, accepting [Pulumi Assets](https://www.pulumi.com/docs/concepts/assets-archives/#assets). + * + * A map of chart values may also be supplied with the `values` input, with highest precedence. You're able to use literals, + * nested maps, [Pulumi outputs](https://www.pulumi.com/docs/concepts/inputs-outputs/), and Pulumi assets as values. + * Assets are automatically opened and converted to a string. + * + * Note that the use of expressions (e.g. `--set service.type`) is not supported. + * + * ### Chart Dependency Resolution + * + * For unpacked chart directories, Pulumi automatically rebuilds the dependencies if dependencies are missing + * and a `Chart.lock` file is present (see: [Helm Dependency Build](https://helm.sh/docs/helm/helm_dependency_build/)). + * Use the `dependencyUpdate` input to have Pulumi update the dependencies (see: [Helm Dependency Update](https://helm.sh/docs/helm/helm_dependency_update/)). + * + * ### Templating + * + * The `Chart` resource renders the templates from your chart and then manages the resources directly with the + * Pulumi Kubernetes provider. A default namespace is applied based on the `namespace` input, the provider's + * configured namespace, and the active Kubernetes context. Use the `skipCrds` option to skip installing the + * Custom Resource Definition (CRD) objects located in the chart's `crds/` special directory. + * + * Use the `postRenderer` input to pipe the rendered manifest through a [post-rendering command](https://helm.sh/docs/topics/advanced/#post-rendering). + * + * ### Resource Ordering + * + * Sometimes resources must be applied in a specific order. For example, a namespace resource must be + * created before any namespaced resources, or a Custom Resource Definition (CRD) must be pre-installed. + * + * Pulumi uses heuristics to determine which order to apply and delete objects within the Chart. Pulumi also + * waits for each object to be fully reconciled, unless `skipAwait` is enabled. + * + * Pulumi supports the `config.kubernetes.io/depends-on` annotation to declare an explicit dependency on a given resource. + * The annotation accepts a list of resource references, delimited by commas. + * + * Note that references to resources outside the Chart aren't supported. + * + * **Resource reference** + * + * A resource reference is a string that uniquely identifies a resource. + * + * It consists of the group, kind, name, and optionally the namespace, delimited by forward slashes. + * + * | Resource Scope | Format | + * | :--------------- | :--------------------------------------------- | + * | namespace-scoped | `<group>/namespaces/<namespace>/<kind>/<name>` | + * | cluster-scoped | `<group>/<kind>/<name>` | + * + * For resources in the “core” group, the empty string is used instead (for example: `/namespaces/test/Pod/pod-a`). + * + * ## Example Usage + * ### Local Chart Directory + * ```java + * package generated_program; + * + * import com.pulumi.Pulumi; + * import com.pulumi.kubernetes.helm.v4.Chart; + * import com.pulumi.kubernetes.helm.v4.ChartArgs; + * + * public class App { + * public static void main(String[] args) { + * Pulumi.run(ctx -> { + * var nginx = new Chart("nginx", ChartArgs.builder() + * .chart("./nginx") + * .build()); + * }); + * } + * } + * ``` + * ### Repository Chart + * ```java + * package generated_program; + * + * import com.pulumi.Pulumi; + * import com.pulumi.kubernetes.helm.v4.Chart; + * import com.pulumi.kubernetes.helm.v4.ChartArgs; + * import com.pulumi.kubernetes.helm.v4.inputs.RepositoryOptsArgs; + * + * public class App { + * public static void main(String[] args) { + * Pulumi.run(ctx -> { + * var nginx = new Chart("nginx", ChartArgs.builder() + * .chart("nginx") + * .repositoryOpts(RepositoryOptsArgs.builder() + * .repo("https://charts.bitnami.com/bitnami") + * .build()) + * .build()); + * }); + * } + * } + * ``` + * ### OCI Chart + * ```java + * package generated_program; + * + * import com.pulumi.Pulumi; + * import com.pulumi.kubernetes.helm.v4.Chart; + * import com.pulumi.kubernetes.helm.v4.ChartArgs; + * + * public class App { + * public static void main(String[] args) { + * Pulumi.run(ctx -> { + * var nginx = new Chart("nginx", ChartArgs.builder() + * .chart("oci://registry-1.docker.io/bitnamicharts/nginx") + * .version("16.0.7") + * .build()); + * }); + * } + * } + * ``` + * ### Chart Values + * ```java + * package generated_program; + * + * import java.util.Map; + * + * import com.pulumi.Pulumi; + * import com.pulumi.kubernetes.helm.v4.Chart; + * import com.pulumi.kubernetes.helm.v4.ChartArgs; + * import com.pulumi.kubernetes.helm.v4.inputs.RepositoryOptsArgs; + * import com.pulumi.asset.FileAsset; + * + * public class App { + * public static void main(String[] args) { + * Pulumi.run(ctx -> { + * var nginx = new Chart("nginx", ChartArgs.builder() + * .chart("nginx") + * .repositoryOpts(RepositoryOptsArgs.builder() + * .repo("https://charts.bitnami.com/bitnami") + * .build()) + * .valueYamlFiles(new FileAsset("./values.yaml")) + * .values(Map.of( + * "service", Map.of( + * "type", "ClusterIP"), + * "notes", new FileAsset("./notes.txt"))) + * .build()); + * }); + * } + * } + * ``` + * ### Chart Namespace + * ```java + * package generated_program; + * + * import com.pulumi.Pulumi; + * import com.pulumi.kubernetes.core.v1.Namespace; + * import com.pulumi.kubernetes.core.v1.NamespaceArgs; + * import com.pulumi.kubernetes.helm.v4.Chart; + * import com.pulumi.kubernetes.helm.v4.ChartArgs; + * import com.pulumi.kubernetes.helm.v4.inputs.RepositoryOptsArgs; + * import com.pulumi.kubernetes.meta.v1.inputs.ObjectMetaArgs; + * import com.pulumi.core.Output; + * + * public class App { + * public static void main(String[] args) { + * Pulumi.run(ctx -> { + * var ns = new Namespace("nginx", NamespaceArgs.builder() + * .metadata(ObjectMetaArgs.builder() + * .name("nginx") + * .build()) + * .build()); + * var nginx = new Chart("nginx", ChartArgs.builder() + * .namespace(ns.metadata().apply(m -> Output.of(m.name().get()))) + * .chart("nginx") + * .repositoryOpts(RepositoryOptsArgs.builder() + * .repo("https://charts.bitnami.com/bitnami") + * .build()) + * .build()); + * }); + * } + * } + * ``` + * + */ +@ResourceType(type="kubernetes:helm.sh/v4:Chart") +public class Chart extends com.pulumi.resources.ComponentResource { + /** + * Resources created by the Chart. + * + */ + @Export(name="resources", refs={List.class,Object.class}, tree="[0,1]") + private Output> resources; + + /** + * @return Resources created by the Chart. + * + */ + public Output>> resources() { + return Codegen.optional(this.resources); + } + + /** + * + * @param name The _unique_ name of the resulting resource. + */ + public Chart(String name) { + this(name, ChartArgs.Empty); + } + /** + * + * @param name The _unique_ name of the resulting resource. + * @param args The arguments to use to populate this resource's properties. + */ + public Chart(String name, ChartArgs args) { + this(name, args, null); + } + /** + * + * @param name The _unique_ name of the resulting resource. + * @param args The arguments to use to populate this resource's properties. + * @param options A bag of options that control this resource's behavior. + */ + public Chart(String name, ChartArgs args, @Nullable com.pulumi.resources.ComponentResourceOptions options) { + super("kubernetes:helm.sh/v4:Chart", name, args == null ? ChartArgs.Empty : args, makeResourceOptions(options, Codegen.empty()), true); + } + + private static com.pulumi.resources.ComponentResourceOptions makeResourceOptions(@Nullable com.pulumi.resources.ComponentResourceOptions options, @Nullable Output id) { + var defaultOptions = com.pulumi.resources.ComponentResourceOptions.builder() + .version(Utilities.getVersion()) + .build(); + return com.pulumi.resources.ComponentResourceOptions.merge(defaultOptions, options, id); + } + +} diff --git a/sdk/java/src/main/java/com/pulumi/kubernetes/helm/v4/ChartArgs.java b/sdk/java/src/main/java/com/pulumi/kubernetes/helm/v4/ChartArgs.java new file mode 100644 index 0000000000..ac960be123 --- /dev/null +++ b/sdk/java/src/main/java/com/pulumi/kubernetes/helm/v4/ChartArgs.java @@ -0,0 +1,619 @@ +// *** WARNING: this file was generated by pulumi-java-gen. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +package com.pulumi.kubernetes.helm.v4; + +import com.pulumi.asset.AssetOrArchive; +import com.pulumi.core.Output; +import com.pulumi.core.annotations.Import; +import com.pulumi.kubernetes.helm.v4.inputs.PostRendererArgs; +import com.pulumi.kubernetes.helm.v4.inputs.RepositoryOptsArgs; +import java.lang.Boolean; +import java.lang.Object; +import java.lang.String; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import javax.annotation.Nullable; + + +public final class ChartArgs extends com.pulumi.resources.ResourceArgs { + + public static final ChartArgs Empty = new ChartArgs(); + + /** + * Chart name to be installed. A path may be used. + * + */ + @Import(name="chart", required=true) + private Output chart; + + /** + * @return Chart name to be installed. A path may be used. + * + */ + public Output chart() { + return this.chart; + } + + /** + * Run helm dependency update before installing the chart. + * + */ + @Import(name="dependencyUpdate") + private @Nullable Output dependencyUpdate; + + /** + * @return Run helm dependency update before installing the chart. + * + */ + public Optional> dependencyUpdate() { + return Optional.ofNullable(this.dependencyUpdate); + } + + /** + * Use chart development versions, too. Equivalent to version '>0.0.0-0'. If `version` is set, this is ignored. + * + */ + @Import(name="devel") + private @Nullable Output devel; + + /** + * @return Use chart development versions, too. Equivalent to version '>0.0.0-0'. If `version` is set, this is ignored. + * + */ + public Optional> devel() { + return Optional.ofNullable(this.devel); + } + + /** + * Location of public keys used for verification. Used only if `verify` is true + * + */ + @Import(name="keyring") + private @Nullable Output keyring; + + /** + * @return Location of public keys used for verification. Used only if `verify` is true + * + */ + public Optional> keyring() { + return Optional.ofNullable(this.keyring); + } + + /** + * Release name. + * + */ + @Import(name="name") + private @Nullable Output name; + + /** + * @return Release name. + * + */ + public Optional> name() { + return Optional.ofNullable(this.name); + } + + /** + * Namespace for the release. + * + */ + @Import(name="namespace") + private @Nullable Output namespace; + + /** + * @return Namespace for the release. + * + */ + public Optional> namespace() { + return Optional.ofNullable(this.namespace); + } + + /** + * Specification defining the post-renderer to use. + * + */ + @Import(name="postRenderer") + private @Nullable Output postRenderer; + + /** + * @return Specification defining the post-renderer to use. + * + */ + public Optional> postRenderer() { + return Optional.ofNullable(this.postRenderer); + } + + /** + * Specification defining the Helm chart repository to use. + * + */ + @Import(name="repositoryOpts") + private @Nullable Output repositoryOpts; + + /** + * @return Specification defining the Helm chart repository to use. + * + */ + public Optional> repositoryOpts() { + return Optional.ofNullable(this.repositoryOpts); + } + + /** + * An optional prefix for the auto-generated resource names. Example: A resource created with resourcePrefix="foo" would produce a resource named "foo:resourceName". + * + */ + @Import(name="resourcePrefix") + private @Nullable Output resourcePrefix; + + /** + * @return An optional prefix for the auto-generated resource names. Example: A resource created with resourcePrefix="foo" would produce a resource named "foo:resourceName". + * + */ + public Optional> resourcePrefix() { + return Optional.ofNullable(this.resourcePrefix); + } + + /** + * By default, the provider waits until all resources are in a ready state before marking the release as successful. Setting this to true will skip such await logic. + * + */ + @Import(name="skipAwait") + private @Nullable Output skipAwait; + + /** + * @return By default, the provider waits until all resources are in a ready state before marking the release as successful. Setting this to true will skip such await logic. + * + */ + public Optional> skipAwait() { + return Optional.ofNullable(this.skipAwait); + } + + /** + * If set, no CRDs will be installed. By default, CRDs are installed if not already present. + * + */ + @Import(name="skipCrds") + private @Nullable Output skipCrds; + + /** + * @return If set, no CRDs will be installed. By default, CRDs are installed if not already present. + * + */ + public Optional> skipCrds() { + return Optional.ofNullable(this.skipCrds); + } + + /** + * List of assets (raw yaml files). Content is read and merged with values. + * + */ + @Import(name="valueYamlFiles") + private @Nullable Output> valueYamlFiles; + + /** + * @return List of assets (raw yaml files). Content is read and merged with values. + * + */ + public Optional>> valueYamlFiles() { + return Optional.ofNullable(this.valueYamlFiles); + } + + /** + * Custom values set for the release. + * + */ + @Import(name="values") + private @Nullable Output> values; + + /** + * @return Custom values set for the release. + * + */ + public Optional>> values() { + return Optional.ofNullable(this.values); + } + + /** + * Verify the chart's integrity. + * + */ + @Import(name="verify") + private @Nullable Output verify; + + /** + * @return Verify the chart's integrity. + * + */ + public Optional> verify() { + return Optional.ofNullable(this.verify); + } + + /** + * Specify the chart version to install. If this is not specified, the latest version is installed. + * + */ + @Import(name="version") + private @Nullable Output version; + + /** + * @return Specify the chart version to install. If this is not specified, the latest version is installed. + * + */ + public Optional> version() { + return Optional.ofNullable(this.version); + } + + private ChartArgs() {} + + private ChartArgs(ChartArgs $) { + this.chart = $.chart; + this.dependencyUpdate = $.dependencyUpdate; + this.devel = $.devel; + this.keyring = $.keyring; + this.name = $.name; + this.namespace = $.namespace; + this.postRenderer = $.postRenderer; + this.repositoryOpts = $.repositoryOpts; + this.resourcePrefix = $.resourcePrefix; + this.skipAwait = $.skipAwait; + this.skipCrds = $.skipCrds; + this.valueYamlFiles = $.valueYamlFiles; + this.values = $.values; + this.verify = $.verify; + this.version = $.version; + } + + public static Builder builder() { + return new Builder(); + } + public static Builder builder(ChartArgs defaults) { + return new Builder(defaults); + } + + public static final class Builder { + private ChartArgs $; + + public Builder() { + $ = new ChartArgs(); + } + + public Builder(ChartArgs defaults) { + $ = new ChartArgs(Objects.requireNonNull(defaults)); + } + + /** + * @param chart Chart name to be installed. A path may be used. + * + * @return builder + * + */ + public Builder chart(Output chart) { + $.chart = chart; + return this; + } + + /** + * @param chart Chart name to be installed. A path may be used. + * + * @return builder + * + */ + public Builder chart(String chart) { + return chart(Output.of(chart)); + } + + /** + * @param dependencyUpdate Run helm dependency update before installing the chart. + * + * @return builder + * + */ + public Builder dependencyUpdate(@Nullable Output dependencyUpdate) { + $.dependencyUpdate = dependencyUpdate; + return this; + } + + /** + * @param dependencyUpdate Run helm dependency update before installing the chart. + * + * @return builder + * + */ + public Builder dependencyUpdate(Boolean dependencyUpdate) { + return dependencyUpdate(Output.of(dependencyUpdate)); + } + + /** + * @param devel Use chart development versions, too. Equivalent to version '>0.0.0-0'. If `version` is set, this is ignored. + * + * @return builder + * + */ + public Builder devel(@Nullable Output devel) { + $.devel = devel; + return this; + } + + /** + * @param devel Use chart development versions, too. Equivalent to version '>0.0.0-0'. If `version` is set, this is ignored. + * + * @return builder + * + */ + public Builder devel(Boolean devel) { + return devel(Output.of(devel)); + } + + /** + * @param keyring Location of public keys used for verification. Used only if `verify` is true + * + * @return builder + * + */ + public Builder keyring(@Nullable Output keyring) { + $.keyring = keyring; + return this; + } + + /** + * @param keyring Location of public keys used for verification. Used only if `verify` is true + * + * @return builder + * + */ + public Builder keyring(AssetOrArchive keyring) { + return keyring(Output.of(keyring)); + } + + /** + * @param name Release name. + * + * @return builder + * + */ + public Builder name(@Nullable Output name) { + $.name = name; + return this; + } + + /** + * @param name Release name. + * + * @return builder + * + */ + public Builder name(String name) { + return name(Output.of(name)); + } + + /** + * @param namespace Namespace for the release. + * + * @return builder + * + */ + public Builder namespace(@Nullable Output namespace) { + $.namespace = namespace; + return this; + } + + /** + * @param namespace Namespace for the release. + * + * @return builder + * + */ + public Builder namespace(String namespace) { + return namespace(Output.of(namespace)); + } + + /** + * @param postRenderer Specification defining the post-renderer to use. + * + * @return builder + * + */ + public Builder postRenderer(@Nullable Output postRenderer) { + $.postRenderer = postRenderer; + return this; + } + + /** + * @param postRenderer Specification defining the post-renderer to use. + * + * @return builder + * + */ + public Builder postRenderer(PostRendererArgs postRenderer) { + return postRenderer(Output.of(postRenderer)); + } + + /** + * @param repositoryOpts Specification defining the Helm chart repository to use. + * + * @return builder + * + */ + public Builder repositoryOpts(@Nullable Output repositoryOpts) { + $.repositoryOpts = repositoryOpts; + return this; + } + + /** + * @param repositoryOpts Specification defining the Helm chart repository to use. + * + * @return builder + * + */ + public Builder repositoryOpts(RepositoryOptsArgs repositoryOpts) { + return repositoryOpts(Output.of(repositoryOpts)); + } + + /** + * @param resourcePrefix An optional prefix for the auto-generated resource names. Example: A resource created with resourcePrefix="foo" would produce a resource named "foo:resourceName". + * + * @return builder + * + */ + public Builder resourcePrefix(@Nullable Output resourcePrefix) { + $.resourcePrefix = resourcePrefix; + return this; + } + + /** + * @param resourcePrefix An optional prefix for the auto-generated resource names. Example: A resource created with resourcePrefix="foo" would produce a resource named "foo:resourceName". + * + * @return builder + * + */ + public Builder resourcePrefix(String resourcePrefix) { + return resourcePrefix(Output.of(resourcePrefix)); + } + + /** + * @param skipAwait By default, the provider waits until all resources are in a ready state before marking the release as successful. Setting this to true will skip such await logic. + * + * @return builder + * + */ + public Builder skipAwait(@Nullable Output skipAwait) { + $.skipAwait = skipAwait; + return this; + } + + /** + * @param skipAwait By default, the provider waits until all resources are in a ready state before marking the release as successful. Setting this to true will skip such await logic. + * + * @return builder + * + */ + public Builder skipAwait(Boolean skipAwait) { + return skipAwait(Output.of(skipAwait)); + } + + /** + * @param skipCrds If set, no CRDs will be installed. By default, CRDs are installed if not already present. + * + * @return builder + * + */ + public Builder skipCrds(@Nullable Output skipCrds) { + $.skipCrds = skipCrds; + return this; + } + + /** + * @param skipCrds If set, no CRDs will be installed. By default, CRDs are installed if not already present. + * + * @return builder + * + */ + public Builder skipCrds(Boolean skipCrds) { + return skipCrds(Output.of(skipCrds)); + } + + /** + * @param valueYamlFiles List of assets (raw yaml files). Content is read and merged with values. + * + * @return builder + * + */ + public Builder valueYamlFiles(@Nullable Output> valueYamlFiles) { + $.valueYamlFiles = valueYamlFiles; + return this; + } + + /** + * @param valueYamlFiles List of assets (raw yaml files). Content is read and merged with values. + * + * @return builder + * + */ + public Builder valueYamlFiles(List valueYamlFiles) { + return valueYamlFiles(Output.of(valueYamlFiles)); + } + + /** + * @param valueYamlFiles List of assets (raw yaml files). Content is read and merged with values. + * + * @return builder + * + */ + public Builder valueYamlFiles(AssetOrArchive... valueYamlFiles) { + return valueYamlFiles(List.of(valueYamlFiles)); + } + + /** + * @param values Custom values set for the release. + * + * @return builder + * + */ + public Builder values(@Nullable Output> values) { + $.values = values; + return this; + } + + /** + * @param values Custom values set for the release. + * + * @return builder + * + */ + public Builder values(Map values) { + return values(Output.of(values)); + } + + /** + * @param verify Verify the chart's integrity. + * + * @return builder + * + */ + public Builder verify(@Nullable Output verify) { + $.verify = verify; + return this; + } + + /** + * @param verify Verify the chart's integrity. + * + * @return builder + * + */ + public Builder verify(Boolean verify) { + return verify(Output.of(verify)); + } + + /** + * @param version Specify the chart version to install. If this is not specified, the latest version is installed. + * + * @return builder + * + */ + public Builder version(@Nullable Output version) { + $.version = version; + return this; + } + + /** + * @param version Specify the chart version to install. If this is not specified, the latest version is installed. + * + * @return builder + * + */ + public Builder version(String version) { + return version(Output.of(version)); + } + + public ChartArgs build() { + $.chart = Objects.requireNonNull($.chart, "expected parameter 'chart' to be non-null"); + return $; + } + } + +} diff --git a/sdk/java/src/main/java/com/pulumi/kubernetes/helm/v4/inputs/PostRendererArgs.java b/sdk/java/src/main/java/com/pulumi/kubernetes/helm/v4/inputs/PostRendererArgs.java new file mode 100644 index 0000000000..47e0529415 --- /dev/null +++ b/sdk/java/src/main/java/com/pulumi/kubernetes/helm/v4/inputs/PostRendererArgs.java @@ -0,0 +1,136 @@ +// *** WARNING: this file was generated by pulumi-java-gen. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +package com.pulumi.kubernetes.helm.v4.inputs; + +import com.pulumi.core.Output; +import com.pulumi.core.annotations.Import; +import java.lang.String; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import javax.annotation.Nullable; + + +/** + * Specification defining the post-renderer to use. + * + */ +public final class PostRendererArgs extends com.pulumi.resources.ResourceArgs { + + public static final PostRendererArgs Empty = new PostRendererArgs(); + + /** + * Arguments to pass to the post-renderer command. + * + */ + @Import(name="args") + private @Nullable Output> args; + + /** + * @return Arguments to pass to the post-renderer command. + * + */ + public Optional>> args() { + return Optional.ofNullable(this.args); + } + + /** + * Path to an executable to be used for post rendering. + * + */ + @Import(name="command", required=true) + private Output command; + + /** + * @return Path to an executable to be used for post rendering. + * + */ + public Output command() { + return this.command; + } + + private PostRendererArgs() {} + + private PostRendererArgs(PostRendererArgs $) { + this.args = $.args; + this.command = $.command; + } + + public static Builder builder() { + return new Builder(); + } + public static Builder builder(PostRendererArgs defaults) { + return new Builder(defaults); + } + + public static final class Builder { + private PostRendererArgs $; + + public Builder() { + $ = new PostRendererArgs(); + } + + public Builder(PostRendererArgs defaults) { + $ = new PostRendererArgs(Objects.requireNonNull(defaults)); + } + + /** + * @param args Arguments to pass to the post-renderer command. + * + * @return builder + * + */ + public Builder args(@Nullable Output> args) { + $.args = args; + return this; + } + + /** + * @param args Arguments to pass to the post-renderer command. + * + * @return builder + * + */ + public Builder args(List args) { + return args(Output.of(args)); + } + + /** + * @param args Arguments to pass to the post-renderer command. + * + * @return builder + * + */ + public Builder args(String... args) { + return args(List.of(args)); + } + + /** + * @param command Path to an executable to be used for post rendering. + * + * @return builder + * + */ + public Builder command(Output command) { + $.command = command; + return this; + } + + /** + * @param command Path to an executable to be used for post rendering. + * + * @return builder + * + */ + public Builder command(String command) { + return command(Output.of(command)); + } + + public PostRendererArgs build() { + $.command = Objects.requireNonNull($.command, "expected parameter 'command' to be non-null"); + return $; + } + } + +} diff --git a/sdk/java/src/main/java/com/pulumi/kubernetes/helm/v4/inputs/RepositoryOptsArgs.java b/sdk/java/src/main/java/com/pulumi/kubernetes/helm/v4/inputs/RepositoryOptsArgs.java new file mode 100644 index 0000000000..c004c4cf0f --- /dev/null +++ b/sdk/java/src/main/java/com/pulumi/kubernetes/helm/v4/inputs/RepositoryOptsArgs.java @@ -0,0 +1,273 @@ +// *** WARNING: this file was generated by pulumi-java-gen. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +package com.pulumi.kubernetes.helm.v4.inputs; + +import com.pulumi.asset.AssetOrArchive; +import com.pulumi.core.Output; +import com.pulumi.core.annotations.Import; +import java.lang.String; +import java.util.Objects; +import java.util.Optional; +import javax.annotation.Nullable; + + +/** + * Specification defining the Helm chart repository to use. + * + */ +public final class RepositoryOptsArgs extends com.pulumi.resources.ResourceArgs { + + public static final RepositoryOptsArgs Empty = new RepositoryOptsArgs(); + + /** + * The Repository's CA File + * + */ + @Import(name="caFile") + private @Nullable Output caFile; + + /** + * @return The Repository's CA File + * + */ + public Optional> caFile() { + return Optional.ofNullable(this.caFile); + } + + /** + * The repository's cert file + * + */ + @Import(name="certFile") + private @Nullable Output certFile; + + /** + * @return The repository's cert file + * + */ + public Optional> certFile() { + return Optional.ofNullable(this.certFile); + } + + /** + * The repository's cert key file + * + */ + @Import(name="keyFile") + private @Nullable Output keyFile; + + /** + * @return The repository's cert key file + * + */ + public Optional> keyFile() { + return Optional.ofNullable(this.keyFile); + } + + /** + * Password for HTTP basic authentication + * + */ + @Import(name="password") + private @Nullable Output password; + + /** + * @return Password for HTTP basic authentication + * + */ + public Optional> password() { + return Optional.ofNullable(this.password); + } + + /** + * Repository where to locate the requested chart. If is a URL the chart is installed without installing the repository. + * + */ + @Import(name="repo") + private @Nullable Output repo; + + /** + * @return Repository where to locate the requested chart. If is a URL the chart is installed without installing the repository. + * + */ + public Optional> repo() { + return Optional.ofNullable(this.repo); + } + + /** + * Username for HTTP basic authentication + * + */ + @Import(name="username") + private @Nullable Output username; + + /** + * @return Username for HTTP basic authentication + * + */ + public Optional> username() { + return Optional.ofNullable(this.username); + } + + private RepositoryOptsArgs() {} + + private RepositoryOptsArgs(RepositoryOptsArgs $) { + this.caFile = $.caFile; + this.certFile = $.certFile; + this.keyFile = $.keyFile; + this.password = $.password; + this.repo = $.repo; + this.username = $.username; + } + + public static Builder builder() { + return new Builder(); + } + public static Builder builder(RepositoryOptsArgs defaults) { + return new Builder(defaults); + } + + public static final class Builder { + private RepositoryOptsArgs $; + + public Builder() { + $ = new RepositoryOptsArgs(); + } + + public Builder(RepositoryOptsArgs defaults) { + $ = new RepositoryOptsArgs(Objects.requireNonNull(defaults)); + } + + /** + * @param caFile The Repository's CA File + * + * @return builder + * + */ + public Builder caFile(@Nullable Output caFile) { + $.caFile = caFile; + return this; + } + + /** + * @param caFile The Repository's CA File + * + * @return builder + * + */ + public Builder caFile(AssetOrArchive caFile) { + return caFile(Output.of(caFile)); + } + + /** + * @param certFile The repository's cert file + * + * @return builder + * + */ + public Builder certFile(@Nullable Output certFile) { + $.certFile = certFile; + return this; + } + + /** + * @param certFile The repository's cert file + * + * @return builder + * + */ + public Builder certFile(AssetOrArchive certFile) { + return certFile(Output.of(certFile)); + } + + /** + * @param keyFile The repository's cert key file + * + * @return builder + * + */ + public Builder keyFile(@Nullable Output keyFile) { + $.keyFile = keyFile; + return this; + } + + /** + * @param keyFile The repository's cert key file + * + * @return builder + * + */ + public Builder keyFile(AssetOrArchive keyFile) { + return keyFile(Output.of(keyFile)); + } + + /** + * @param password Password for HTTP basic authentication + * + * @return builder + * + */ + public Builder password(@Nullable Output password) { + $.password = password; + return this; + } + + /** + * @param password Password for HTTP basic authentication + * + * @return builder + * + */ + public Builder password(String password) { + return password(Output.of(password)); + } + + /** + * @param repo Repository where to locate the requested chart. If is a URL the chart is installed without installing the repository. + * + * @return builder + * + */ + public Builder repo(@Nullable Output repo) { + $.repo = repo; + return this; + } + + /** + * @param repo Repository where to locate the requested chart. If is a URL the chart is installed without installing the repository. + * + * @return builder + * + */ + public Builder repo(String repo) { + return repo(Output.of(repo)); + } + + /** + * @param username Username for HTTP basic authentication + * + * @return builder + * + */ + public Builder username(@Nullable Output username) { + $.username = username; + return this; + } + + /** + * @param username Username for HTTP basic authentication + * + * @return builder + * + */ + public Builder username(String username) { + return username(Output.of(username)); + } + + public RepositoryOptsArgs build() { + return $; + } + } + +} diff --git a/sdk/java/src/main/java/com/pulumi/kubernetes/inputs/HelmReleaseSettingsArgs.java b/sdk/java/src/main/java/com/pulumi/kubernetes/inputs/HelmReleaseSettingsArgs.java index fc74c44bf5..70f6f3bf81 100644 --- a/sdk/java/src/main/java/com/pulumi/kubernetes/inputs/HelmReleaseSettingsArgs.java +++ b/sdk/java/src/main/java/com/pulumi/kubernetes/inputs/HelmReleaseSettingsArgs.java @@ -66,14 +66,14 @@ public Optional> registryConfigPath() { } /** - * The path to the file containing cached repository indexes. + * The path to the directory containing cached repository indexes. * */ @Import(name="repositoryCache") private @Nullable Output repositoryCache; /** - * @return The path to the file containing cached repository indexes. + * @return The path to the directory containing cached repository indexes. * */ public Optional> repositoryCache() { @@ -187,7 +187,7 @@ public Builder registryConfigPath(String registryConfigPath) { } /** - * @param repositoryCache The path to the file containing cached repository indexes. + * @param repositoryCache The path to the directory containing cached repository indexes. * * @return builder * @@ -198,7 +198,7 @@ public Builder repositoryCache(@Nullable Output repositoryCache) { } /** - * @param repositoryCache The path to the file containing cached repository indexes. + * @param repositoryCache The path to the directory containing cached repository indexes. * * @return builder * diff --git a/sdk/java/src/main/java/com/pulumi/kubernetes/inputs/KubeClientSettingsArgs.java b/sdk/java/src/main/java/com/pulumi/kubernetes/inputs/KubeClientSettingsArgs.java index 0c41b025ea..436b348317 100644 --- a/sdk/java/src/main/java/com/pulumi/kubernetes/inputs/KubeClientSettingsArgs.java +++ b/sdk/java/src/main/java/com/pulumi/kubernetes/inputs/KubeClientSettingsArgs.java @@ -22,14 +22,14 @@ public final class KubeClientSettingsArgs extends com.pulumi.resources.ResourceA public static final KubeClientSettingsArgs Empty = new KubeClientSettingsArgs(); /** - * Maximum burst for throttle. Default value is 10. + * Maximum burst for throttle. Default value is 120. * */ @Import(name="burst") private @Nullable Output burst; /** - * @return Maximum burst for throttle. Default value is 10. + * @return Maximum burst for throttle. Default value is 120. * */ public Optional> burst() { @@ -37,14 +37,14 @@ public Optional> burst() { } /** - * Maximum queries per second (QPS) to the API server from this client. Default value is 5. + * Maximum queries per second (QPS) to the API server from this client. Default value is 50. * */ @Import(name="qps") private @Nullable Output qps; /** - * @return Maximum queries per second (QPS) to the API server from this client. Default value is 5. + * @return Maximum queries per second (QPS) to the API server from this client. Default value is 50. * */ public Optional> qps() { @@ -93,7 +93,7 @@ public Builder(KubeClientSettingsArgs defaults) { } /** - * @param burst Maximum burst for throttle. Default value is 10. + * @param burst Maximum burst for throttle. Default value is 120. * * @return builder * @@ -104,7 +104,7 @@ public Builder burst(@Nullable Output burst) { } /** - * @param burst Maximum burst for throttle. Default value is 10. + * @param burst Maximum burst for throttle. Default value is 120. * * @return builder * @@ -114,7 +114,7 @@ public Builder burst(Integer burst) { } /** - * @param qps Maximum queries per second (QPS) to the API server from this client. Default value is 5. + * @param qps Maximum queries per second (QPS) to the API server from this client. Default value is 50. * * @return builder * @@ -125,7 +125,7 @@ public Builder qps(@Nullable Output qps) { } /** - * @param qps Maximum queries per second (QPS) to the API server from this client. Default value is 5. + * @param qps Maximum queries per second (QPS) to the API server from this client. Default value is 50. * * @return builder * diff --git a/sdk/nodejs/helm/index.ts b/sdk/nodejs/helm/index.ts index 9fa93a9b6f..2a664ae35f 100644 --- a/sdk/nodejs/helm/index.ts +++ b/sdk/nodejs/helm/index.ts @@ -5,7 +5,9 @@ import * as utilities from "../utilities"; // Export sub-modules: import * as v3 from "./v3"; +import * as v4 from "./v4"; export { v3, + v4, }; diff --git a/sdk/nodejs/helm/v4/chart.ts b/sdk/nodejs/helm/v4/chart.ts new file mode 100644 index 0000000000..6c2d4d84ee --- /dev/null +++ b/sdk/nodejs/helm/v4/chart.ts @@ -0,0 +1,291 @@ +// *** WARNING: this file was generated by pulumigen. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +import * as pulumi from "@pulumi/pulumi"; +import * as inputs from "../../types/input"; +import * as outputs from "../../types/output"; +import * as enums from "../../types/enums"; +import * as utilities from "../../utilities"; + +/** + * Chart is a component representing a collection of resources described by a Helm Chart. + * Helm charts are a popular packaging format for Kubernetes applications, and published + * to registries such as [Artifact Hub](https://artifacthub.io/packages/search?kind=0&sort=relevance&page=1). + * + * Chart does not use Tiller or create a Helm Release; the semantics are equivalent to + * running `helm template --dry-run=server` and then using Pulumi to deploy the resulting YAML manifests. + * This allows you to apply [Pulumi Transformations](https://www.pulumi.com/docs/concepts/options/transformations/) and + * [Pulumi Policies](https://www.pulumi.com/docs/using-pulumi/crossguard/) to the Kubernetes resources. + * + * You may also want to consider the `Release` resource as an alternative method for managing helm charts. For more + * information about the trade-offs between these options, see: [Choosing the right Helm resource for your use case](https://www.pulumi.com/registry/packages/kubernetes/how-to-guides/choosing-the-right-helm-resource-for-your-use-case). + * + * ### Chart Resolution + * + * The Helm Chart can be fetched from any source that is accessible to the `helm` command line. + * The following variations are supported: + * + * 1. By chart reference with repo prefix: `chart: "example/mariadb"` + * 2. By path to a packaged chart: `chart: "./nginx-1.2.3.tgz"` + * 3. By path to an unpacked chart directory: `chart: "./nginx"` + * 4. By absolute URL: `chart: "https://example.com/charts/nginx-1.2.3.tgz"` + * 5. By chart reference with repo URL: `chart: "nginx", repositoryOpts: { repo: "https://example.com/charts/" }` + * 6. By OCI registry: `chart: "oci://example.com/charts/nginx", version: "1.2.3"` + * + * A chart reference is a convenient way of referencing a chart in a chart repository. + * + * When you use a chart reference with a repo prefix (`example/mariadb`), Pulumi will look in Helm's local configuration + * for a chart repository named `example`, and will then look for a chart in that repository whose name is `mariadb`. + * It will install the latest stable version of that chart, unless you specify `devel` to also include + * development versions (alpha, beta, and release candidate releases), or supply a version number with `version`. + * + * Use the `verify` and optional `keyring` inputs to enable Chart verification. + * By default, Pulumi uses the keyring at `$HOME/.gnupg/pubring.gpg`. See: [Helm Provenance and Integrity](https://helm.sh/docs/topics/provenance/). + * + * ### Chart Values + * + * [Values files](https://helm.sh/docs/chart_template_guide/values_files/#helm) (`values.yaml`) may be supplied + * with the `valueYamlFiles` input, accepting [Pulumi Assets](https://www.pulumi.com/docs/concepts/assets-archives/#assets). + * + * A map of chart values may also be supplied with the `values` input, with highest precedence. You're able to use literals, + * nested maps, [Pulumi outputs](https://www.pulumi.com/docs/concepts/inputs-outputs/), and Pulumi assets as values. + * Assets are automatically opened and converted to a string. + * + * Note that the use of expressions (e.g. `--set service.type`) is not supported. + * + * ### Chart Dependency Resolution + * + * For unpacked chart directories, Pulumi automatically rebuilds the dependencies if dependencies are missing + * and a `Chart.lock` file is present (see: [Helm Dependency Build](https://helm.sh/docs/helm/helm_dependency_build/)). + * Use the `dependencyUpdate` input to have Pulumi update the dependencies (see: [Helm Dependency Update](https://helm.sh/docs/helm/helm_dependency_update/)). + * + * ### Templating + * + * The `Chart` resource renders the templates from your chart and then manages the resources directly with the + * Pulumi Kubernetes provider. A default namespace is applied based on the `namespace` input, the provider's + * configured namespace, and the active Kubernetes context. Use the `skipCrds` option to skip installing the + * Custom Resource Definition (CRD) objects located in the chart's `crds/` special directory. + * + * Use the `postRenderer` input to pipe the rendered manifest through a [post-rendering command](https://helm.sh/docs/topics/advanced/#post-rendering). + * + * ### Resource Ordering + * + * Sometimes resources must be applied in a specific order. For example, a namespace resource must be + * created before any namespaced resources, or a Custom Resource Definition (CRD) must be pre-installed. + * + * Pulumi uses heuristics to determine which order to apply and delete objects within the Chart. Pulumi also + * waits for each object to be fully reconciled, unless `skipAwait` is enabled. + * + * Pulumi supports the `config.kubernetes.io/depends-on` annotation to declare an explicit dependency on a given resource. + * The annotation accepts a list of resource references, delimited by commas. + * + * Note that references to resources outside the Chart aren't supported. + * + * **Resource reference** + * + * A resource reference is a string that uniquely identifies a resource. + * + * It consists of the group, kind, name, and optionally the namespace, delimited by forward slashes. + * + * | Resource Scope | Format | + * | :--------------- | :--------------------------------------------- | + * | namespace-scoped | `/namespaces///` | + * | cluster-scoped | `//` | + * + * For resources in the “core” group, the empty string is used instead (for example: `/namespaces/test/Pod/pod-a`). + * + * ## Example Usage + * ### Local Chart Directory + * + * ```typescript + * import * as k8s from "@pulumi/kubernetes"; + * + * const nginx = new k8s.helm.v4.Chart("nginx", { + * chart: "./nginx", + * }); + * ``` + * ### Repository Chart + * + * ```typescript + * import * as k8s from "@pulumi/kubernetes"; + * + * const nginx = new k8s.helm.v4.Chart("nginx", { + * chart: "nginx", + * repositoryOpts: { + * repo: "https://charts.bitnami.com/bitnami", + * }, + * }); + * ``` + * ### OCI Chart + * + * ```typescript + * import * as k8s from "@pulumi/kubernetes"; + * + * const nginx = new k8s.helm.v4.Chart("nginx", { + * chart: "oci://registry-1.docker.io/bitnamicharts/nginx", + * version: "16.0.7", + * }); + * ``` + * ### Chart Values + * + * ```typescript + * import * as pulumi from "@pulumi/pulumi"; + * import * as k8s from "@pulumi/kubernetes"; + * + * const nginx = new k8s.helm.v4.Chart("nginx", { + * chart: "nginx", + * repositoryOpts: { + * repo: "https://charts.bitnami.com/bitnami", + * }, + * valueYamlFiles: [ + * new pulumi.asset.FileAsset("./values.yaml") + * ], + * values: { + * service: { + * type: "ClusterIP", + * }, + * notes: new pulumi.asset.FileAsset("./notes.txt"), + * }, + * }); + * ``` + * ### Chart Namespace + * + * ```typescript + * import * as pulumi from "@pulumi/pulumi"; + * import * as k8s from "@pulumi/kubernetes"; + * + * const ns = new k8s.core.v1.Namespace("nginx", { + * metadata: { name: "nginx" }, + * }); + * const nginx = new k8s.helm.v4.Chart("nginx", { + * namespace: ns.metadata.name, + * chart: "nginx", + * repositoryOpts: { + * repo: "https://charts.bitnami.com/bitnami", + * } + * }); + * ``` + */ +export class Chart extends pulumi.ComponentResource { + /** @internal */ + public static readonly __pulumiType = 'kubernetes:helm.sh/v4:Chart'; + + /** + * Returns true if the given object is an instance of Chart. This is designed to work even + * when multiple copies of the Pulumi SDK have been loaded into the same process. + */ + public static isInstance(obj: any): obj is Chart { + if (obj === undefined || obj === null) { + return false; + } + return obj['__pulumiType'] === Chart.__pulumiType; + } + + /** + * Resources created by the Chart. + */ + public /*out*/ readonly resources!: pulumi.Output; + + /** + * Create a Chart resource with the given unique name, arguments, and options. + * + * @param name The _unique_ name of the resource. + * @param args The arguments to use to populate this resource's properties. + * @param opts A bag of options that control this resource's behavior. + */ + constructor(name: string, args?: ChartArgs, opts?: pulumi.ComponentResourceOptions) { + let resourceInputs: pulumi.Inputs = {}; + opts = opts || {}; + if (!opts.id) { + if ((!args || args.chart === undefined) && !opts.urn) { + throw new Error("Missing required property 'chart'"); + } + resourceInputs["chart"] = args ? args.chart : undefined; + resourceInputs["dependencyUpdate"] = args ? args.dependencyUpdate : undefined; + resourceInputs["devel"] = args ? args.devel : undefined; + resourceInputs["keyring"] = args ? args.keyring : undefined; + resourceInputs["name"] = args ? args.name : undefined; + resourceInputs["namespace"] = args ? args.namespace : undefined; + resourceInputs["postRenderer"] = args ? args.postRenderer : undefined; + resourceInputs["repositoryOpts"] = args ? args.repositoryOpts : undefined; + resourceInputs["resourcePrefix"] = args ? args.resourcePrefix : undefined; + resourceInputs["skipAwait"] = args ? args.skipAwait : undefined; + resourceInputs["skipCrds"] = args ? args.skipCrds : undefined; + resourceInputs["valueYamlFiles"] = args ? args.valueYamlFiles : undefined; + resourceInputs["values"] = args ? args.values : undefined; + resourceInputs["verify"] = args ? args.verify : undefined; + resourceInputs["version"] = args ? args.version : undefined; + resourceInputs["resources"] = undefined /*out*/; + } else { + resourceInputs["resources"] = undefined /*out*/; + } + opts = pulumi.mergeOptions(utilities.resourceOptsDefaults(), opts); + super(Chart.__pulumiType, name, resourceInputs, opts, true /*remote*/); + } +} + +/** + * The set of arguments for constructing a Chart resource. + */ +export interface ChartArgs { + /** + * Chart name to be installed. A path may be used. + */ + chart: pulumi.Input; + /** + * Run helm dependency update before installing the chart. + */ + dependencyUpdate?: pulumi.Input; + /** + * Use chart development versions, too. Equivalent to version '>0.0.0-0'. If `version` is set, this is ignored. + */ + devel?: pulumi.Input; + /** + * Location of public keys used for verification. Used only if `verify` is true + */ + keyring?: pulumi.Input; + /** + * Release name. + */ + name?: pulumi.Input; + /** + * Namespace for the release. + */ + namespace?: pulumi.Input; + /** + * Specification defining the post-renderer to use. + */ + postRenderer?: pulumi.Input; + /** + * Specification defining the Helm chart repository to use. + */ + repositoryOpts?: pulumi.Input; + /** + * An optional prefix for the auto-generated resource names. Example: A resource created with resourcePrefix="foo" would produce a resource named "foo:resourceName". + */ + resourcePrefix?: pulumi.Input; + /** + * By default, the provider waits until all resources are in a ready state before marking the release as successful. Setting this to true will skip such await logic. + */ + skipAwait?: pulumi.Input; + /** + * If set, no CRDs will be installed. By default, CRDs are installed if not already present. + */ + skipCrds?: pulumi.Input; + /** + * List of assets (raw yaml files). Content is read and merged with values. + */ + valueYamlFiles?: pulumi.Input[]>; + /** + * Custom values set for the release. + */ + values?: pulumi.Input<{[key: string]: any}>; + /** + * Verify the chart's integrity. + */ + verify?: pulumi.Input; + /** + * Specify the chart version to install. If this is not specified, the latest version is installed. + */ + version?: pulumi.Input; +} diff --git a/sdk/nodejs/helm/v4/index.ts b/sdk/nodejs/helm/v4/index.ts new file mode 100644 index 0000000000..0ed29789c3 --- /dev/null +++ b/sdk/nodejs/helm/v4/index.ts @@ -0,0 +1,25 @@ +// *** WARNING: this file was generated by pulumigen. *** +// *** Do not edit by hand unless you're certain you know what you are doing! *** + +import * as pulumi from "@pulumi/pulumi"; +import * as utilities from "../../utilities"; + +// Export members: +export { ChartArgs } from "./chart"; +export type Chart = import("./chart").Chart; +export const Chart: typeof import("./chart").Chart = null as any; +utilities.lazyLoad(exports, ["Chart"], () => require("./chart")); + + +const _module = { + version: utilities.getVersion(), + construct: (name: string, type: string, urn: string): pulumi.Resource => { + switch (type) { + case "kubernetes:helm.sh/v4:Chart": + return new Chart(name, undefined, { urn }) + default: + throw new Error(`unknown resource type ${type}`); + } + }, +}; +pulumi.runtime.registerResourceModule("kubernetes", "helm.sh/v4", _module) diff --git a/sdk/nodejs/tsconfig.json b/sdk/nodejs/tsconfig.json index 54e7ef2610..bf4355e9e5 100644 --- a/sdk/nodejs/tsconfig.json +++ b/sdk/nodejs/tsconfig.json @@ -297,6 +297,8 @@ "helm/v3/helm.ts", "helm/v3/index.ts", "helm/v3/release.ts", + "helm/v4/chart.ts", + "helm/v4/index.ts", "index.ts", "kustomize/index.ts", "kustomize/kustomize.ts", diff --git a/sdk/nodejs/types/input.ts b/sdk/nodejs/types/input.ts index 169135c713..cd35376afb 100644 --- a/sdk/nodejs/types/input.ts +++ b/sdk/nodejs/types/input.ts @@ -25,7 +25,7 @@ export interface HelmReleaseSettings { */ registryConfigPath?: pulumi.Input; /** - * The path to the file containing cached repository indexes. + * The path to the directory containing cached repository indexes. */ repositoryCache?: pulumi.Input; /** @@ -52,11 +52,11 @@ export function helmReleaseSettingsProvideDefaults(val: HelmReleaseSettings): He */ export interface KubeClientSettings { /** - * Maximum burst for throttle. Default value is 10. + * Maximum burst for throttle. Default value is 120. */ burst?: pulumi.Input; /** - * Maximum queries per second (QPS) to the API server from this client. Default value is 5. + * Maximum queries per second (QPS) to the API server from this client. Default value is 50. */ qps?: pulumi.Input; /** @@ -26057,6 +26057,52 @@ export namespace helm { username?: pulumi.Input; } } + + export namespace v4 { + /** + * Specification defining the post-renderer to use. + */ + export interface PostRenderer { + /** + * Arguments to pass to the post-renderer command. + */ + args?: pulumi.Input[]>; + /** + * Path to an executable to be used for post rendering. + */ + command: pulumi.Input; + } + + /** + * Specification defining the Helm chart repository to use. + */ + export interface RepositoryOpts { + /** + * The Repository's CA File + */ + caFile?: pulumi.Input; + /** + * The repository's cert file + */ + certFile?: pulumi.Input; + /** + * The repository's cert key file + */ + keyFile?: pulumi.Input; + /** + * Password for HTTP basic authentication + */ + password?: pulumi.Input; + /** + * Repository where to locate the requested chart. If is a URL the chart is installed without installing the repository. + */ + repo?: pulumi.Input; + /** + * Username for HTTP basic authentication + */ + username?: pulumi.Input; + } + } } export namespace meta { diff --git a/sdk/nodejs/types/output.ts b/sdk/nodejs/types/output.ts index 1d678acf71..244e9ee8b1 100644 --- a/sdk/nodejs/types/output.ts +++ b/sdk/nodejs/types/output.ts @@ -29134,6 +29134,9 @@ export namespace helm { } } + + export namespace v4 { + } } export namespace meta { diff --git a/sdk/python/pulumi_kubernetes/__init__.py b/sdk/python/pulumi_kubernetes/__init__.py index 6395120ff8..7d5f060530 100644 --- a/sdk/python/pulumi_kubernetes/__init__.py +++ b/sdk/python/pulumi_kubernetes/__init__.py @@ -572,6 +572,14 @@ "kubernetes:helm.sh/v3:Release": "Release" } }, + { + "pkg": "kubernetes", + "mod": "helm.sh/v4", + "fqn": "pulumi_kubernetes.helm.v4", + "classes": { + "kubernetes:helm.sh/v4:Chart": "Chart" + } + }, { "pkg": "kubernetes", "mod": "meta/v1", diff --git a/sdk/python/pulumi_kubernetes/_inputs.py b/sdk/python/pulumi_kubernetes/_inputs.py index caa1854966..b480530b3b 100644 --- a/sdk/python/pulumi_kubernetes/_inputs.py +++ b/sdk/python/pulumi_kubernetes/_inputs.py @@ -27,7 +27,7 @@ def __init__(__self__, *, :param pulumi.Input[str] driver: The backend storage driver for Helm. Values are: configmap, secret, memory, sql. :param pulumi.Input[str] plugins_path: The path to the helm plugins directory. :param pulumi.Input[str] registry_config_path: The path to the registry config file. - :param pulumi.Input[str] repository_cache: The path to the file containing cached repository indexes. + :param pulumi.Input[str] repository_cache: The path to the directory containing cached repository indexes. :param pulumi.Input[str] repository_config_path: The path to the file containing repository names and URLs. """ if driver is None: @@ -91,7 +91,7 @@ def registry_config_path(self, value: Optional[pulumi.Input[str]]): @pulumi.getter(name="repositoryCache") def repository_cache(self) -> Optional[pulumi.Input[str]]: """ - The path to the file containing cached repository indexes. + The path to the directory containing cached repository indexes. """ return pulumi.get(self, "repository_cache") @@ -120,8 +120,8 @@ def __init__(__self__, *, timeout: Optional[pulumi.Input[int]] = None): """ Options for tuning the Kubernetes client used by a Provider. - :param pulumi.Input[int] burst: Maximum burst for throttle. Default value is 10. - :param pulumi.Input[float] qps: Maximum queries per second (QPS) to the API server from this client. Default value is 5. + :param pulumi.Input[int] burst: Maximum burst for throttle. Default value is 120. + :param pulumi.Input[float] qps: Maximum queries per second (QPS) to the API server from this client. Default value is 50. :param pulumi.Input[int] timeout: Maximum time in seconds to wait before cancelling a HTTP request to the Kubernetes server. Default value is 32. """ if burst is None: @@ -141,7 +141,7 @@ def __init__(__self__, *, @pulumi.getter def burst(self) -> Optional[pulumi.Input[int]]: """ - Maximum burst for throttle. Default value is 10. + Maximum burst for throttle. Default value is 120. """ return pulumi.get(self, "burst") @@ -153,7 +153,7 @@ def burst(self, value: Optional[pulumi.Input[int]]): @pulumi.getter def qps(self) -> Optional[pulumi.Input[float]]: """ - Maximum queries per second (QPS) to the API server from this client. Default value is 5. + Maximum queries per second (QPS) to the API server from this client. Default value is 50. """ return pulumi.get(self, "qps") diff --git a/sdk/python/pulumi_kubernetes/helm/__init__.py b/sdk/python/pulumi_kubernetes/helm/__init__.py index 6c932218a1..f9418d3f72 100644 --- a/sdk/python/pulumi_kubernetes/helm/__init__.py +++ b/sdk/python/pulumi_kubernetes/helm/__init__.py @@ -9,6 +9,9 @@ if typing.TYPE_CHECKING: import pulumi_kubernetes.helm.v3 as __v3 v3 = __v3 + import pulumi_kubernetes.helm.v4 as __v4 + v4 = __v4 else: v3 = _utilities.lazy_import('pulumi_kubernetes.helm.v3') + v4 = _utilities.lazy_import('pulumi_kubernetes.helm.v4') diff --git a/sdk/python/pulumi_kubernetes/helm/v4/Chart.py b/sdk/python/pulumi_kubernetes/helm/v4/Chart.py new file mode 100644 index 0000000000..84efebb99b --- /dev/null +++ b/sdk/python/pulumi_kubernetes/helm/v4/Chart.py @@ -0,0 +1,706 @@ +# coding=utf-8 +# *** WARNING: this file was generated by pulumigen. *** +# *** Do not edit by hand unless you're certain you know what you are doing! *** + +import copy +import warnings +import pulumi +import pulumi.runtime +from typing import Any, Mapping, Optional, Sequence, Union, overload +from ... import _utilities +from ._inputs import * + +__all__ = ['ChartArgs', 'Chart'] + +@pulumi.input_type +class ChartArgs: + def __init__(__self__, *, + chart: pulumi.Input[str], + dependency_update: Optional[pulumi.Input[bool]] = None, + devel: Optional[pulumi.Input[bool]] = None, + keyring: Optional[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]] = None, + name: Optional[pulumi.Input[str]] = None, + namespace: Optional[pulumi.Input[str]] = None, + post_renderer: Optional[pulumi.Input['PostRendererArgs']] = None, + repository_opts: Optional[pulumi.Input['RepositoryOptsArgs']] = None, + resource_prefix: Optional[pulumi.Input[str]] = None, + skip_await: Optional[pulumi.Input[bool]] = None, + skip_crds: Optional[pulumi.Input[bool]] = None, + value_yaml_files: Optional[pulumi.Input[Sequence[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]]]] = None, + values: Optional[pulumi.Input[Mapping[str, Any]]] = None, + verify: Optional[pulumi.Input[bool]] = None, + version: Optional[pulumi.Input[str]] = None): + """ + The set of arguments for constructing a Chart resource. + :param pulumi.Input[str] chart: Chart name to be installed. A path may be used. + :param pulumi.Input[bool] dependency_update: Run helm dependency update before installing the chart. + :param pulumi.Input[bool] devel: Use chart development versions, too. Equivalent to version '>0.0.0-0'. If `version` is set, this is ignored. + :param pulumi.Input[Union[pulumi.Asset, pulumi.Archive]] keyring: Location of public keys used for verification. Used only if `verify` is true + :param pulumi.Input[str] name: Release name. + :param pulumi.Input[str] namespace: Namespace for the release. + :param pulumi.Input['PostRendererArgs'] post_renderer: Specification defining the post-renderer to use. + :param pulumi.Input['RepositoryOptsArgs'] repository_opts: Specification defining the Helm chart repository to use. + :param pulumi.Input[str] resource_prefix: An optional prefix for the auto-generated resource names. Example: A resource created with resourcePrefix="foo" would produce a resource named "foo:resourceName". + :param pulumi.Input[bool] skip_await: By default, the provider waits until all resources are in a ready state before marking the release as successful. Setting this to true will skip such await logic. + :param pulumi.Input[bool] skip_crds: If set, no CRDs will be installed. By default, CRDs are installed if not already present. + :param pulumi.Input[Sequence[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]]] value_yaml_files: List of assets (raw yaml files). Content is read and merged with values. + :param pulumi.Input[Mapping[str, Any]] values: Custom values set for the release. + :param pulumi.Input[bool] verify: Verify the chart's integrity. + :param pulumi.Input[str] version: Specify the chart version to install. If this is not specified, the latest version is installed. + """ + pulumi.set(__self__, "chart", chart) + if dependency_update is not None: + pulumi.set(__self__, "dependency_update", dependency_update) + if devel is not None: + pulumi.set(__self__, "devel", devel) + if keyring is not None: + pulumi.set(__self__, "keyring", keyring) + if name is not None: + pulumi.set(__self__, "name", name) + if namespace is not None: + pulumi.set(__self__, "namespace", namespace) + if post_renderer is not None: + pulumi.set(__self__, "post_renderer", post_renderer) + if repository_opts is not None: + pulumi.set(__self__, "repository_opts", repository_opts) + if resource_prefix is not None: + pulumi.set(__self__, "resource_prefix", resource_prefix) + if skip_await is not None: + pulumi.set(__self__, "skip_await", skip_await) + if skip_crds is not None: + pulumi.set(__self__, "skip_crds", skip_crds) + if value_yaml_files is not None: + pulumi.set(__self__, "value_yaml_files", value_yaml_files) + if values is not None: + pulumi.set(__self__, "values", values) + if verify is not None: + pulumi.set(__self__, "verify", verify) + if version is not None: + pulumi.set(__self__, "version", version) + + @property + @pulumi.getter + def chart(self) -> pulumi.Input[str]: + """ + Chart name to be installed. A path may be used. + """ + return pulumi.get(self, "chart") + + @chart.setter + def chart(self, value: pulumi.Input[str]): + pulumi.set(self, "chart", value) + + @property + @pulumi.getter(name="dependencyUpdate") + def dependency_update(self) -> Optional[pulumi.Input[bool]]: + """ + Run helm dependency update before installing the chart. + """ + return pulumi.get(self, "dependency_update") + + @dependency_update.setter + def dependency_update(self, value: Optional[pulumi.Input[bool]]): + pulumi.set(self, "dependency_update", value) + + @property + @pulumi.getter + def devel(self) -> Optional[pulumi.Input[bool]]: + """ + Use chart development versions, too. Equivalent to version '>0.0.0-0'. If `version` is set, this is ignored. + """ + return pulumi.get(self, "devel") + + @devel.setter + def devel(self, value: Optional[pulumi.Input[bool]]): + pulumi.set(self, "devel", value) + + @property + @pulumi.getter + def keyring(self) -> Optional[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]]: + """ + Location of public keys used for verification. Used only if `verify` is true + """ + return pulumi.get(self, "keyring") + + @keyring.setter + def keyring(self, value: Optional[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]]): + pulumi.set(self, "keyring", value) + + @property + @pulumi.getter + def name(self) -> Optional[pulumi.Input[str]]: + """ + Release name. + """ + return pulumi.get(self, "name") + + @name.setter + def name(self, value: Optional[pulumi.Input[str]]): + pulumi.set(self, "name", value) + + @property + @pulumi.getter + def namespace(self) -> Optional[pulumi.Input[str]]: + """ + Namespace for the release. + """ + return pulumi.get(self, "namespace") + + @namespace.setter + def namespace(self, value: Optional[pulumi.Input[str]]): + pulumi.set(self, "namespace", value) + + @property + @pulumi.getter(name="postRenderer") + def post_renderer(self) -> Optional[pulumi.Input['PostRendererArgs']]: + """ + Specification defining the post-renderer to use. + """ + return pulumi.get(self, "post_renderer") + + @post_renderer.setter + def post_renderer(self, value: Optional[pulumi.Input['PostRendererArgs']]): + pulumi.set(self, "post_renderer", value) + + @property + @pulumi.getter(name="repositoryOpts") + def repository_opts(self) -> Optional[pulumi.Input['RepositoryOptsArgs']]: + """ + Specification defining the Helm chart repository to use. + """ + return pulumi.get(self, "repository_opts") + + @repository_opts.setter + def repository_opts(self, value: Optional[pulumi.Input['RepositoryOptsArgs']]): + pulumi.set(self, "repository_opts", value) + + @property + @pulumi.getter(name="resourcePrefix") + def resource_prefix(self) -> Optional[pulumi.Input[str]]: + """ + An optional prefix for the auto-generated resource names. Example: A resource created with resourcePrefix="foo" would produce a resource named "foo:resourceName". + """ + return pulumi.get(self, "resource_prefix") + + @resource_prefix.setter + def resource_prefix(self, value: Optional[pulumi.Input[str]]): + pulumi.set(self, "resource_prefix", value) + + @property + @pulumi.getter(name="skipAwait") + def skip_await(self) -> Optional[pulumi.Input[bool]]: + """ + By default, the provider waits until all resources are in a ready state before marking the release as successful. Setting this to true will skip such await logic. + """ + return pulumi.get(self, "skip_await") + + @skip_await.setter + def skip_await(self, value: Optional[pulumi.Input[bool]]): + pulumi.set(self, "skip_await", value) + + @property + @pulumi.getter(name="skipCrds") + def skip_crds(self) -> Optional[pulumi.Input[bool]]: + """ + If set, no CRDs will be installed. By default, CRDs are installed if not already present. + """ + return pulumi.get(self, "skip_crds") + + @skip_crds.setter + def skip_crds(self, value: Optional[pulumi.Input[bool]]): + pulumi.set(self, "skip_crds", value) + + @property + @pulumi.getter(name="valueYamlFiles") + def value_yaml_files(self) -> Optional[pulumi.Input[Sequence[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]]]]: + """ + List of assets (raw yaml files). Content is read and merged with values. + """ + return pulumi.get(self, "value_yaml_files") + + @value_yaml_files.setter + def value_yaml_files(self, value: Optional[pulumi.Input[Sequence[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]]]]): + pulumi.set(self, "value_yaml_files", value) + + @property + @pulumi.getter + def values(self) -> Optional[pulumi.Input[Mapping[str, Any]]]: + """ + Custom values set for the release. + """ + return pulumi.get(self, "values") + + @values.setter + def values(self, value: Optional[pulumi.Input[Mapping[str, Any]]]): + pulumi.set(self, "values", value) + + @property + @pulumi.getter + def verify(self) -> Optional[pulumi.Input[bool]]: + """ + Verify the chart's integrity. + """ + return pulumi.get(self, "verify") + + @verify.setter + def verify(self, value: Optional[pulumi.Input[bool]]): + pulumi.set(self, "verify", value) + + @property + @pulumi.getter + def version(self) -> Optional[pulumi.Input[str]]: + """ + Specify the chart version to install. If this is not specified, the latest version is installed. + """ + return pulumi.get(self, "version") + + @version.setter + def version(self, value: Optional[pulumi.Input[str]]): + pulumi.set(self, "version", value) + + +class Chart(pulumi.ComponentResource): + @overload + def __init__(__self__, + resource_name: str, + opts: Optional[pulumi.ResourceOptions] = None, + chart: Optional[pulumi.Input[str]] = None, + dependency_update: Optional[pulumi.Input[bool]] = None, + devel: Optional[pulumi.Input[bool]] = None, + keyring: Optional[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]] = None, + name: Optional[pulumi.Input[str]] = None, + namespace: Optional[pulumi.Input[str]] = None, + post_renderer: Optional[pulumi.Input[pulumi.InputType['PostRendererArgs']]] = None, + repository_opts: Optional[pulumi.Input[pulumi.InputType['RepositoryOptsArgs']]] = None, + resource_prefix: Optional[pulumi.Input[str]] = None, + skip_await: Optional[pulumi.Input[bool]] = None, + skip_crds: Optional[pulumi.Input[bool]] = None, + value_yaml_files: Optional[pulumi.Input[Sequence[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]]]] = None, + values: Optional[pulumi.Input[Mapping[str, Any]]] = None, + verify: Optional[pulumi.Input[bool]] = None, + version: Optional[pulumi.Input[str]] = None, + __props__=None): + """ + Chart is a component representing a collection of resources described by a Helm Chart. + Helm charts are a popular packaging format for Kubernetes applications, and published + to registries such as [Artifact Hub](https://artifacthub.io/packages/search?kind=0&sort=relevance&page=1). + + Chart does not use Tiller or create a Helm Release; the semantics are equivalent to + running `helm template --dry-run=server` and then using Pulumi to deploy the resulting YAML manifests. + This allows you to apply [Pulumi Transformations](https://www.pulumi.com/docs/concepts/options/transformations/) and + [Pulumi Policies](https://www.pulumi.com/docs/using-pulumi/crossguard/) to the Kubernetes resources. + + You may also want to consider the `Release` resource as an alternative method for managing helm charts. For more + information about the trade-offs between these options, see: [Choosing the right Helm resource for your use case](https://www.pulumi.com/registry/packages/kubernetes/how-to-guides/choosing-the-right-helm-resource-for-your-use-case). + + ### Chart Resolution + + The Helm Chart can be fetched from any source that is accessible to the `helm` command line. + The following variations are supported: + + 1. By chart reference with repo prefix: `chart: "example/mariadb"` + 2. By path to a packaged chart: `chart: "./nginx-1.2.3.tgz"` + 3. By path to an unpacked chart directory: `chart: "./nginx"` + 4. By absolute URL: `chart: "https://example.com/charts/nginx-1.2.3.tgz"` + 5. By chart reference with repo URL: `chart: "nginx", repositoryOpts: { repo: "https://example.com/charts/" }` + 6. By OCI registry: `chart: "oci://example.com/charts/nginx", version: "1.2.3"` + + A chart reference is a convenient way of referencing a chart in a chart repository. + + When you use a chart reference with a repo prefix (`example/mariadb`), Pulumi will look in Helm's local configuration + for a chart repository named `example`, and will then look for a chart in that repository whose name is `mariadb`. + It will install the latest stable version of that chart, unless you specify `devel` to also include + development versions (alpha, beta, and release candidate releases), or supply a version number with `version`. + + Use the `verify` and optional `keyring` inputs to enable Chart verification. + By default, Pulumi uses the keyring at `$HOME/.gnupg/pubring.gpg`. See: [Helm Provenance and Integrity](https://helm.sh/docs/topics/provenance/). + + ### Chart Values + + [Values files](https://helm.sh/docs/chart_template_guide/values_files/#helm) (`values.yaml`) may be supplied + with the `valueYamlFiles` input, accepting [Pulumi Assets](https://www.pulumi.com/docs/concepts/assets-archives/#assets). + + A map of chart values may also be supplied with the `values` input, with highest precedence. You're able to use literals, + nested maps, [Pulumi outputs](https://www.pulumi.com/docs/concepts/inputs-outputs/), and Pulumi assets as values. + Assets are automatically opened and converted to a string. + + Note that the use of expressions (e.g. `--set service.type`) is not supported. + + ### Chart Dependency Resolution + + For unpacked chart directories, Pulumi automatically rebuilds the dependencies if dependencies are missing + and a `Chart.lock` file is present (see: [Helm Dependency Build](https://helm.sh/docs/helm/helm_dependency_build/)). + Use the `dependencyUpdate` input to have Pulumi update the dependencies (see: [Helm Dependency Update](https://helm.sh/docs/helm/helm_dependency_update/)). + + ### Templating + + The `Chart` resource renders the templates from your chart and then manages the resources directly with the + Pulumi Kubernetes provider. A default namespace is applied based on the `namespace` input, the provider's + configured namespace, and the active Kubernetes context. Use the `skipCrds` option to skip installing the + Custom Resource Definition (CRD) objects located in the chart's `crds/` special directory. + + Use the `postRenderer` input to pipe the rendered manifest through a [post-rendering command](https://helm.sh/docs/topics/advanced/#post-rendering). + + ### Resource Ordering + + Sometimes resources must be applied in a specific order. For example, a namespace resource must be + created before any namespaced resources, or a Custom Resource Definition (CRD) must be pre-installed. + + Pulumi uses heuristics to determine which order to apply and delete objects within the Chart. Pulumi also + waits for each object to be fully reconciled, unless `skipAwait` is enabled. + + Pulumi supports the `config.kubernetes.io/depends-on` annotation to declare an explicit dependency on a given resource. + The annotation accepts a list of resource references, delimited by commas. + + Note that references to resources outside the Chart aren't supported. + + **Resource reference** + + A resource reference is a string that uniquely identifies a resource. + + It consists of the group, kind, name, and optionally the namespace, delimited by forward slashes. + + | Resource Scope | Format | + | :--------------- | :--------------------------------------------- | + | namespace-scoped | `/namespaces///` | + | cluster-scoped | `//` | + + For resources in the “core” group, the empty string is used instead (for example: `/namespaces/test/Pod/pod-a`). + + ## Example Usage + ### Local Chart Directory + ```python + import pulumi + from pulumi_kubernetes.helm.v4 import Chart + + nginx = Chart("nginx", + chart="./nginx" + ) + ``` + ### Repository Chart + ```python + import pulumi + from pulumi_kubernetes.helm.v4 import Chart,RepositoryOptsArgs + + nginx = Chart("nginx", + chart="nginx", + repository_opts=RepositoryOptsArgs( + repo="https://charts.bitnami.com/bitnami", + ) + ) + ``` + ### OCI Chart + ```python + import pulumi + from pulumi_kubernetes.helm.v4 import Chart + + nginx = Chart("nginx", + chart="oci://registry-1.docker.io/bitnamicharts/nginx", + version="16.0.7", + ) + ``` + ### Chart Values + ```python + \"\"\"A Kubernetes Python Pulumi program\"\"\" + + import pulumi + from pulumi_kubernetes.helm.v4 import Chart,RepositoryOptsArgs + + nginx = Chart("nginx", + chart="nginx", + repository_opts=RepositoryOptsArgs( + repo="https://charts.bitnami.com/bitnami" + ), + value_yaml_files=[ + pulumi.FileAsset("./values.yaml") + ], + values={ + "service": { + "type": "ClusterIP" + }, + "notes": pulumi.FileAsset("./notes.txt") + } + ) + ``` + ### Chart Namespace + ```python + import pulumi + from pulumi_kubernetes.meta.v1 import ObjectMetaArgs + from pulumi_kubernetes.core.v1 import Namespace + from pulumi_kubernetes.helm.v4 import Chart,RepositoryOptsArgs + + ns = Namespace("nginx", + metadata=ObjectMetaArgs( + name="nginx", + ) + ) + nginx = Chart("nginx", + namespace=ns.metadata.name, + chart="nginx", + repository_opts=RepositoryOptsArgs( + repo="https://charts.bitnami.com/bitnami", + ) + ) + ``` + + :param str resource_name: The name of the resource. + :param pulumi.ResourceOptions opts: Options for the resource. + :param pulumi.Input[str] chart: Chart name to be installed. A path may be used. + :param pulumi.Input[bool] dependency_update: Run helm dependency update before installing the chart. + :param pulumi.Input[bool] devel: Use chart development versions, too. Equivalent to version '>0.0.0-0'. If `version` is set, this is ignored. + :param pulumi.Input[Union[pulumi.Asset, pulumi.Archive]] keyring: Location of public keys used for verification. Used only if `verify` is true + :param pulumi.Input[str] name: Release name. + :param pulumi.Input[str] namespace: Namespace for the release. + :param pulumi.Input[pulumi.InputType['PostRendererArgs']] post_renderer: Specification defining the post-renderer to use. + :param pulumi.Input[pulumi.InputType['RepositoryOptsArgs']] repository_opts: Specification defining the Helm chart repository to use. + :param pulumi.Input[str] resource_prefix: An optional prefix for the auto-generated resource names. Example: A resource created with resourcePrefix="foo" would produce a resource named "foo:resourceName". + :param pulumi.Input[bool] skip_await: By default, the provider waits until all resources are in a ready state before marking the release as successful. Setting this to true will skip such await logic. + :param pulumi.Input[bool] skip_crds: If set, no CRDs will be installed. By default, CRDs are installed if not already present. + :param pulumi.Input[Sequence[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]]] value_yaml_files: List of assets (raw yaml files). Content is read and merged with values. + :param pulumi.Input[Mapping[str, Any]] values: Custom values set for the release. + :param pulumi.Input[bool] verify: Verify the chart's integrity. + :param pulumi.Input[str] version: Specify the chart version to install. If this is not specified, the latest version is installed. + """ + ... + @overload + def __init__(__self__, + resource_name: str, + args: ChartArgs, + opts: Optional[pulumi.ResourceOptions] = None): + """ + Chart is a component representing a collection of resources described by a Helm Chart. + Helm charts are a popular packaging format for Kubernetes applications, and published + to registries such as [Artifact Hub](https://artifacthub.io/packages/search?kind=0&sort=relevance&page=1). + + Chart does not use Tiller or create a Helm Release; the semantics are equivalent to + running `helm template --dry-run=server` and then using Pulumi to deploy the resulting YAML manifests. + This allows you to apply [Pulumi Transformations](https://www.pulumi.com/docs/concepts/options/transformations/) and + [Pulumi Policies](https://www.pulumi.com/docs/using-pulumi/crossguard/) to the Kubernetes resources. + + You may also want to consider the `Release` resource as an alternative method for managing helm charts. For more + information about the trade-offs between these options, see: [Choosing the right Helm resource for your use case](https://www.pulumi.com/registry/packages/kubernetes/how-to-guides/choosing-the-right-helm-resource-for-your-use-case). + + ### Chart Resolution + + The Helm Chart can be fetched from any source that is accessible to the `helm` command line. + The following variations are supported: + + 1. By chart reference with repo prefix: `chart: "example/mariadb"` + 2. By path to a packaged chart: `chart: "./nginx-1.2.3.tgz"` + 3. By path to an unpacked chart directory: `chart: "./nginx"` + 4. By absolute URL: `chart: "https://example.com/charts/nginx-1.2.3.tgz"` + 5. By chart reference with repo URL: `chart: "nginx", repositoryOpts: { repo: "https://example.com/charts/" }` + 6. By OCI registry: `chart: "oci://example.com/charts/nginx", version: "1.2.3"` + + A chart reference is a convenient way of referencing a chart in a chart repository. + + When you use a chart reference with a repo prefix (`example/mariadb`), Pulumi will look in Helm's local configuration + for a chart repository named `example`, and will then look for a chart in that repository whose name is `mariadb`. + It will install the latest stable version of that chart, unless you specify `devel` to also include + development versions (alpha, beta, and release candidate releases), or supply a version number with `version`. + + Use the `verify` and optional `keyring` inputs to enable Chart verification. + By default, Pulumi uses the keyring at `$HOME/.gnupg/pubring.gpg`. See: [Helm Provenance and Integrity](https://helm.sh/docs/topics/provenance/). + + ### Chart Values + + [Values files](https://helm.sh/docs/chart_template_guide/values_files/#helm) (`values.yaml`) may be supplied + with the `valueYamlFiles` input, accepting [Pulumi Assets](https://www.pulumi.com/docs/concepts/assets-archives/#assets). + + A map of chart values may also be supplied with the `values` input, with highest precedence. You're able to use literals, + nested maps, [Pulumi outputs](https://www.pulumi.com/docs/concepts/inputs-outputs/), and Pulumi assets as values. + Assets are automatically opened and converted to a string. + + Note that the use of expressions (e.g. `--set service.type`) is not supported. + + ### Chart Dependency Resolution + + For unpacked chart directories, Pulumi automatically rebuilds the dependencies if dependencies are missing + and a `Chart.lock` file is present (see: [Helm Dependency Build](https://helm.sh/docs/helm/helm_dependency_build/)). + Use the `dependencyUpdate` input to have Pulumi update the dependencies (see: [Helm Dependency Update](https://helm.sh/docs/helm/helm_dependency_update/)). + + ### Templating + + The `Chart` resource renders the templates from your chart and then manages the resources directly with the + Pulumi Kubernetes provider. A default namespace is applied based on the `namespace` input, the provider's + configured namespace, and the active Kubernetes context. Use the `skipCrds` option to skip installing the + Custom Resource Definition (CRD) objects located in the chart's `crds/` special directory. + + Use the `postRenderer` input to pipe the rendered manifest through a [post-rendering command](https://helm.sh/docs/topics/advanced/#post-rendering). + + ### Resource Ordering + + Sometimes resources must be applied in a specific order. For example, a namespace resource must be + created before any namespaced resources, or a Custom Resource Definition (CRD) must be pre-installed. + + Pulumi uses heuristics to determine which order to apply and delete objects within the Chart. Pulumi also + waits for each object to be fully reconciled, unless `skipAwait` is enabled. + + Pulumi supports the `config.kubernetes.io/depends-on` annotation to declare an explicit dependency on a given resource. + The annotation accepts a list of resource references, delimited by commas. + + Note that references to resources outside the Chart aren't supported. + + **Resource reference** + + A resource reference is a string that uniquely identifies a resource. + + It consists of the group, kind, name, and optionally the namespace, delimited by forward slashes. + + | Resource Scope | Format | + | :--------------- | :--------------------------------------------- | + | namespace-scoped | `/namespaces///` | + | cluster-scoped | `//` | + + For resources in the “core” group, the empty string is used instead (for example: `/namespaces/test/Pod/pod-a`). + + ## Example Usage + ### Local Chart Directory + ```python + import pulumi + from pulumi_kubernetes.helm.v4 import Chart + + nginx = Chart("nginx", + chart="./nginx" + ) + ``` + ### Repository Chart + ```python + import pulumi + from pulumi_kubernetes.helm.v4 import Chart,RepositoryOptsArgs + + nginx = Chart("nginx", + chart="nginx", + repository_opts=RepositoryOptsArgs( + repo="https://charts.bitnami.com/bitnami", + ) + ) + ``` + ### OCI Chart + ```python + import pulumi + from pulumi_kubernetes.helm.v4 import Chart + + nginx = Chart("nginx", + chart="oci://registry-1.docker.io/bitnamicharts/nginx", + version="16.0.7", + ) + ``` + ### Chart Values + ```python + \"\"\"A Kubernetes Python Pulumi program\"\"\" + + import pulumi + from pulumi_kubernetes.helm.v4 import Chart,RepositoryOptsArgs + + nginx = Chart("nginx", + chart="nginx", + repository_opts=RepositoryOptsArgs( + repo="https://charts.bitnami.com/bitnami" + ), + value_yaml_files=[ + pulumi.FileAsset("./values.yaml") + ], + values={ + "service": { + "type": "ClusterIP" + }, + "notes": pulumi.FileAsset("./notes.txt") + } + ) + ``` + ### Chart Namespace + ```python + import pulumi + from pulumi_kubernetes.meta.v1 import ObjectMetaArgs + from pulumi_kubernetes.core.v1 import Namespace + from pulumi_kubernetes.helm.v4 import Chart,RepositoryOptsArgs + + ns = Namespace("nginx", + metadata=ObjectMetaArgs( + name="nginx", + ) + ) + nginx = Chart("nginx", + namespace=ns.metadata.name, + chart="nginx", + repository_opts=RepositoryOptsArgs( + repo="https://charts.bitnami.com/bitnami", + ) + ) + ``` + + :param str resource_name: The name of the resource. + :param ChartArgs args: The arguments to use to populate this resource's properties. + :param pulumi.ResourceOptions opts: Options for the resource. + """ + ... + def __init__(__self__, resource_name: str, *args, **kwargs): + resource_args, opts = _utilities.get_resource_args_opts(ChartArgs, pulumi.ResourceOptions, *args, **kwargs) + if resource_args is not None: + __self__._internal_init(resource_name, opts, **resource_args.__dict__) + else: + __self__._internal_init(resource_name, *args, **kwargs) + + def _internal_init(__self__, + resource_name: str, + opts: Optional[pulumi.ResourceOptions] = None, + chart: Optional[pulumi.Input[str]] = None, + dependency_update: Optional[pulumi.Input[bool]] = None, + devel: Optional[pulumi.Input[bool]] = None, + keyring: Optional[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]] = None, + name: Optional[pulumi.Input[str]] = None, + namespace: Optional[pulumi.Input[str]] = None, + post_renderer: Optional[pulumi.Input[pulumi.InputType['PostRendererArgs']]] = None, + repository_opts: Optional[pulumi.Input[pulumi.InputType['RepositoryOptsArgs']]] = None, + resource_prefix: Optional[pulumi.Input[str]] = None, + skip_await: Optional[pulumi.Input[bool]] = None, + skip_crds: Optional[pulumi.Input[bool]] = None, + value_yaml_files: Optional[pulumi.Input[Sequence[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]]]] = None, + values: Optional[pulumi.Input[Mapping[str, Any]]] = None, + verify: Optional[pulumi.Input[bool]] = None, + version: Optional[pulumi.Input[str]] = None, + __props__=None): + opts = pulumi.ResourceOptions.merge(_utilities.get_resource_opts_defaults(), opts) + if not isinstance(opts, pulumi.ResourceOptions): + raise TypeError('Expected resource options to be a ResourceOptions instance') + if opts.id is not None: + raise ValueError('ComponentResource classes do not support opts.id') + else: + if __props__ is not None: + raise TypeError('__props__ is only valid when passed in combination with a valid opts.id to get an existing resource') + __props__ = ChartArgs.__new__(ChartArgs) + + if chart is None and not opts.urn: + raise TypeError("Missing required property 'chart'") + __props__.__dict__["chart"] = chart + __props__.__dict__["dependency_update"] = dependency_update + __props__.__dict__["devel"] = devel + __props__.__dict__["keyring"] = keyring + __props__.__dict__["name"] = name + __props__.__dict__["namespace"] = namespace + __props__.__dict__["post_renderer"] = post_renderer + __props__.__dict__["repository_opts"] = repository_opts + __props__.__dict__["resource_prefix"] = resource_prefix + __props__.__dict__["skip_await"] = skip_await + __props__.__dict__["skip_crds"] = skip_crds + __props__.__dict__["value_yaml_files"] = value_yaml_files + __props__.__dict__["values"] = values + __props__.__dict__["verify"] = verify + __props__.__dict__["version"] = version + __props__.__dict__["resources"] = None + super(Chart, __self__).__init__( + 'kubernetes:helm.sh/v4:Chart', + resource_name, + __props__, + opts, + remote=True) + + @property + @pulumi.getter + def resources(self) -> pulumi.Output[Optional[Sequence[Any]]]: + """ + Resources created by the Chart. + """ + return pulumi.get(self, "resources") + diff --git a/sdk/python/pulumi_kubernetes/helm/v4/__init__.py b/sdk/python/pulumi_kubernetes/helm/v4/__init__.py new file mode 100644 index 0000000000..ae8c3634bd --- /dev/null +++ b/sdk/python/pulumi_kubernetes/helm/v4/__init__.py @@ -0,0 +1,9 @@ +# coding=utf-8 +# *** WARNING: this file was generated by pulumigen. *** +# *** Do not edit by hand unless you're certain you know what you are doing! *** + +from ... import _utilities +import typing +# Export this package's modules as members: +from .Chart import * +from ._inputs import * diff --git a/sdk/python/pulumi_kubernetes/helm/v4/_inputs.py b/sdk/python/pulumi_kubernetes/helm/v4/_inputs.py new file mode 100644 index 0000000000..07224e3fec --- /dev/null +++ b/sdk/python/pulumi_kubernetes/helm/v4/_inputs.py @@ -0,0 +1,159 @@ +# coding=utf-8 +# *** WARNING: this file was generated by pulumigen. *** +# *** Do not edit by hand unless you're certain you know what you are doing! *** + +import copy +import warnings +import pulumi +import pulumi.runtime +from typing import Any, Mapping, Optional, Sequence, Union, overload +from ... import _utilities + +__all__ = [ + 'PostRendererArgs', + 'RepositoryOptsArgs', +] + +@pulumi.input_type +class PostRendererArgs: + def __init__(__self__, *, + command: pulumi.Input[str], + args: Optional[pulumi.Input[Sequence[pulumi.Input[str]]]] = None): + """ + Specification defining the post-renderer to use. + :param pulumi.Input[str] command: Path to an executable to be used for post rendering. + :param pulumi.Input[Sequence[pulumi.Input[str]]] args: Arguments to pass to the post-renderer command. + """ + pulumi.set(__self__, "command", command) + if args is not None: + pulumi.set(__self__, "args", args) + + @property + @pulumi.getter + def command(self) -> pulumi.Input[str]: + """ + Path to an executable to be used for post rendering. + """ + return pulumi.get(self, "command") + + @command.setter + def command(self, value: pulumi.Input[str]): + pulumi.set(self, "command", value) + + @property + @pulumi.getter + def args(self) -> Optional[pulumi.Input[Sequence[pulumi.Input[str]]]]: + """ + Arguments to pass to the post-renderer command. + """ + return pulumi.get(self, "args") + + @args.setter + def args(self, value: Optional[pulumi.Input[Sequence[pulumi.Input[str]]]]): + pulumi.set(self, "args", value) + + +@pulumi.input_type +class RepositoryOptsArgs: + def __init__(__self__, *, + ca_file: Optional[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]] = None, + cert_file: Optional[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]] = None, + key_file: Optional[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]] = None, + password: Optional[pulumi.Input[str]] = None, + repo: Optional[pulumi.Input[str]] = None, + username: Optional[pulumi.Input[str]] = None): + """ + Specification defining the Helm chart repository to use. + :param pulumi.Input[Union[pulumi.Asset, pulumi.Archive]] ca_file: The Repository's CA File + :param pulumi.Input[Union[pulumi.Asset, pulumi.Archive]] cert_file: The repository's cert file + :param pulumi.Input[Union[pulumi.Asset, pulumi.Archive]] key_file: The repository's cert key file + :param pulumi.Input[str] password: Password for HTTP basic authentication + :param pulumi.Input[str] repo: Repository where to locate the requested chart. If is a URL the chart is installed without installing the repository. + :param pulumi.Input[str] username: Username for HTTP basic authentication + """ + if ca_file is not None: + pulumi.set(__self__, "ca_file", ca_file) + if cert_file is not None: + pulumi.set(__self__, "cert_file", cert_file) + if key_file is not None: + pulumi.set(__self__, "key_file", key_file) + if password is not None: + pulumi.set(__self__, "password", password) + if repo is not None: + pulumi.set(__self__, "repo", repo) + if username is not None: + pulumi.set(__self__, "username", username) + + @property + @pulumi.getter(name="caFile") + def ca_file(self) -> Optional[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]]: + """ + The Repository's CA File + """ + return pulumi.get(self, "ca_file") + + @ca_file.setter + def ca_file(self, value: Optional[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]]): + pulumi.set(self, "ca_file", value) + + @property + @pulumi.getter(name="certFile") + def cert_file(self) -> Optional[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]]: + """ + The repository's cert file + """ + return pulumi.get(self, "cert_file") + + @cert_file.setter + def cert_file(self, value: Optional[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]]): + pulumi.set(self, "cert_file", value) + + @property + @pulumi.getter(name="keyFile") + def key_file(self) -> Optional[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]]: + """ + The repository's cert key file + """ + return pulumi.get(self, "key_file") + + @key_file.setter + def key_file(self, value: Optional[pulumi.Input[Union[pulumi.Asset, pulumi.Archive]]]): + pulumi.set(self, "key_file", value) + + @property + @pulumi.getter + def password(self) -> Optional[pulumi.Input[str]]: + """ + Password for HTTP basic authentication + """ + return pulumi.get(self, "password") + + @password.setter + def password(self, value: Optional[pulumi.Input[str]]): + pulumi.set(self, "password", value) + + @property + @pulumi.getter + def repo(self) -> Optional[pulumi.Input[str]]: + """ + Repository where to locate the requested chart. If is a URL the chart is installed without installing the repository. + """ + return pulumi.get(self, "repo") + + @repo.setter + def repo(self, value: Optional[pulumi.Input[str]]): + pulumi.set(self, "repo", value) + + @property + @pulumi.getter + def username(self) -> Optional[pulumi.Input[str]]: + """ + Username for HTTP basic authentication + """ + return pulumi.get(self, "username") + + @username.setter + def username(self, value: Optional[pulumi.Input[str]]): + pulumi.set(self, "username", value) + + diff --git a/tests/gomega/kube.go b/tests/gomega/kube.go new file mode 100644 index 0000000000..820f03fafc --- /dev/null +++ b/tests/gomega/kube.go @@ -0,0 +1,30 @@ +// Copyright 2016-2024, 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 gomega + +import ( + . "github.com/onsi/gomega/gstruct" //nolint:golint // gomega dsl + gomegatypes "github.com/onsi/gomega/types" +) + +func HaveSkipAwaitAnnotation() gomegatypes.GomegaMatcher { + return MatchProps(IgnoreExtras, Props{ + "metadata": MatchObject(IgnoreExtras, Props{ + "annotations": MatchObject(IgnoreExtras, Props{ + "pulumi.com/skipAwait": MatchValue("true"), + }), + }), + }) +} diff --git a/tests/gomega/matchers.go b/tests/gomega/matchers.go index 30f6c9c940..1f14710fdc 100644 --- a/tests/gomega/matchers.go +++ b/tests/gomega/matchers.go @@ -1,4 +1,17 @@ -//nolint:govet,golint +// Copyright 2016-2024, 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 gomega import ( @@ -6,9 +19,9 @@ import ( "fmt" "strings" - . "github.com/onsi/gomega" + . "github.com/onsi/gomega" //nolint:golint // dot-imports "github.com/onsi/gomega/gcustom" - . "github.com/onsi/gomega/gstruct" + . "github.com/onsi/gomega/gstruct" //nolint:golint // dot-imports gomegatypes "github.com/onsi/gomega/types" "github.com/pulumi/pulumi/sdk/v3/go/common/resource" "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" @@ -18,7 +31,7 @@ import ( // ProtobufStruct matches a protobuf struct by decoding it to a map and then applying the given matcher. func ProtobufStruct(matcher gomegatypes.GomegaMatcher) gomegatypes.GomegaMatcher { - return WithTransform(func(actual structpb.Struct) (map[string]interface{}, error) { + return WithTransform(func(actual structpb.Struct) (map[string]interface{}, error) { //nolint:govet // copylocks m := actual.AsMap() return m, nil }, matcher)