github.com/pulumi/pulumi-kubernetes/sdk/v3@v3.30.2/go/kubernetes/helm/v3/chart.go (about)

     1  // Copyright 2016-2021, Pulumi Corporation.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // *** WARNING: this file was generated by pulumigen. ***
    16  // *** Do not edit by hand unless you're certain you know what you are doing! ***
    17  
    18  package helm
    19  
    20  import (
    21  	"encoding/json"
    22  	"fmt"
    23  
    24  	"github.com/pkg/errors"
    25  	"github.com/pulumi/pulumi-kubernetes/sdk/v3/go/kubernetes/yaml"
    26  	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    27  )
    28  
    29  // Chart is a component representing a collection of resources described by an arbitrary Helm
    30  // Chart. The Chart can be fetched from any source that is accessible to the `helm` command
    31  // line. Values in the `values.yml` file can be overridden using `ChartOpts.values` (equivalent
    32  // to `--set` or having multiple `values.yml` files). Objects can be transformed arbitrarily by
    33  // supplying callbacks to `ChartOpts.transformations`.
    34  //
    35  // `Chart` does not use Tiller. The Chart specified is copied and expanded locally; the semantics
    36  // are equivalent to running `helm template` and then using Pulumi to manage the resulting YAML
    37  // manifests. Any values that would be retrieved in-cluster are assigned fake values, and
    38  // none of Tiller's server-side validity testing is executed.
    39  //
    40  // ## Example Usage
    41  // ### Local Chart Directory
    42  //
    43  // ```go
    44  // package main
    45  //
    46  // import (
    47  //
    48  //	"github.com/pulumi/pulumi-kubernetes/sdk/v3/go/kubernetes/helm/v3"
    49  //	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    50  //
    51  // )
    52  //
    53  //	func main() {
    54  //	    pulumi.Run(func(ctx *pulumi.Context) error {
    55  //	        _, err := helm.NewChart(ctx, "nginx-ingress", helm.ChartArgs{
    56  //	            Path: pulumi.String("./nginx-ingress"),
    57  //	        })
    58  //	        if err != nil {
    59  //	            return err
    60  //	        }
    61  //
    62  //	        return nil
    63  //	    })
    64  //	}
    65  //
    66  // ```
    67  // ### Remote Chart
    68  //
    69  // ```go
    70  // package main
    71  //
    72  // import (
    73  //
    74  //	"github.com/pulumi/pulumi-kubernetes/sdk/v3/go/kubernetes/helm/v3"
    75  //	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    76  //
    77  // )
    78  //
    79  //	func main() {
    80  //	    pulumi.Run(func(ctx *pulumi.Context) error {
    81  //	        _, err := helm.NewChart(ctx, "nginx-ingress", helm.ChartArgs{
    82  //	            Chart:   pulumi.String("nginx-ingress"),
    83  //	            Version: pulumi.String("1.24.4"),
    84  //	            FetchArgs: helm.FetchArgs{
    85  //	                Repo: pulumi.String("https://charts.helm.sh/stable"),
    86  //	            },
    87  //	        })
    88  //	        if err != nil {
    89  //	            return err
    90  //	        }
    91  //
    92  //	        return nil
    93  //	    })
    94  //	}
    95  //
    96  // ```
    97  // ### Set Chart values
    98  //
    99  // ```go
   100  // package main
   101  //
   102  // import (
   103  //
   104  //	"github.com/pulumi/pulumi-kubernetes/sdk/v3/go/kubernetes/helm/v3"
   105  //	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
   106  //
   107  // )
   108  //
   109  //	func main() {
   110  //	    pulumi.Run(func(ctx *pulumi.Context) error {
   111  //	        _, err := helm.NewChart(ctx, "nginx-ingress", helm.ChartArgs{
   112  //	            Chart:   pulumi.String("nginx-ingress"),
   113  //	            Version: pulumi.String("1.24.4"),
   114  //	            FetchArgs: helm.FetchArgs{
   115  //	                Repo: pulumi.String("https://charts.helm.sh/stable"),
   116  //	            },
   117  //	            Values: pulumi.Map{
   118  //	                "controller": pulumi.Map{
   119  //	                    "metrics": pulumi.Map{
   120  //	                        "enabled": pulumi.Bool(true),
   121  //	                    },
   122  //	                },
   123  //	            },
   124  //	        })
   125  //	        if err != nil {
   126  //	            return err
   127  //	        }
   128  //
   129  //	        return nil
   130  //	    })
   131  //	}
   132  //
   133  // ```
   134  // ### Deploy Chart into Namespace
   135  //
   136  // ```go
   137  // package main
   138  //
   139  // import (
   140  //
   141  //	"github.com/pulumi/pulumi-kubernetes/sdk/v3/go/kubernetes/helm/v3"
   142  //	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
   143  //
   144  // )
   145  //
   146  //	func main() {
   147  //	    pulumi.Run(func(ctx *pulumi.Context) error {
   148  //	        _, err := helm.NewChart(ctx, "nginx-ingress", helm.ChartArgs{
   149  //	            Chart:     pulumi.String("nginx-ingress"),
   150  //	            Version:   pulumi.String("1.24.4"),
   151  //	            Namespace: pulumi.String("test-namespace"),
   152  //	            FetchArgs: helm.FetchArgs{
   153  //	                Repo: pulumi.String("https://charts.helm.sh/stable"),
   154  //	            },
   155  //	        })
   156  //	        if err != nil {
   157  //	            return err
   158  //	        }
   159  //
   160  //	        return nil
   161  //	    })
   162  //	}
   163  //
   164  // ```
   165  // ### Chart with Transformations
   166  //
   167  // ```go
   168  // package main
   169  //
   170  // import (
   171  //
   172  //	"github.com/pulumi/pulumi-kubernetes/sdk/v3/go/kubernetes/helm/v3"
   173  //	"github.com/pulumi/pulumi-kubernetes/sdk/v3/go/kubernetes/yaml"
   174  //	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
   175  //
   176  // )
   177  //
   178  //	func main() {
   179  //	    pulumi.Run(func(ctx *pulumi.Context) error {
   180  //	        _, err := helm.NewChart(ctx, "nginx-ingress", helm.ChartArgs{
   181  //	            Chart:   pulumi.String("nginx-ingress"),
   182  //	            Version: pulumi.String("1.24.4"),
   183  //	            FetchArgs: helm.FetchArgs{
   184  //	                Repo: pulumi.String("https://charts.helm.sh/stable"),
   185  //	            },
   186  //	            Transformations: []yaml.Transformation{
   187  //	                // Make every service private to the cluster, i.e., turn all services into ClusterIP
   188  //	                // instead of LoadBalancer.
   189  //	                func(state map[string]interface{}, opts ...pulumi.ResourceOption) {
   190  //	                    if state["kind"] == "Service" {
   191  //	                        spec := state["spec"].(map[string]interface{})
   192  //	                        spec["type"] = "ClusterIP"
   193  //	                    }
   194  //	                },
   195  //
   196  //	                // Set a resource alias for a previous name.
   197  //	                func(state map[string]interface{}, opts ...pulumi.ResourceOption) {
   198  //	                    if state["kind"] == "Deployment" {
   199  //	                        aliases := pulumi.Aliases([]pulumi.Alias{
   200  //	                            {
   201  //	                                Name: pulumi.String("oldName"),
   202  //	                            },
   203  //	                        })
   204  //	                        opts = append(opts, aliases)
   205  //	                    }
   206  //	                },
   207  //
   208  //	                // Omit a resource from the Chart by transforming the specified resource definition
   209  //	                // to an empty List.
   210  //	                func(state map[string]interface{}, opts ...pulumi.ResourceOption) {
   211  //	                    name := state["metadata"].(map[string]interface{})["name"]
   212  //	                    if state["kind"] == "Pod" && name == "test" {
   213  //	                        state["apiVersion"] = "v1"
   214  //	                        state["kind"] = "List"
   215  //	                    }
   216  //	                },
   217  //	            },
   218  //	        })
   219  //	        if err != nil {
   220  //	            return err
   221  //	        }
   222  //
   223  //	        return nil
   224  //	    })
   225  //	}
   226  //
   227  // ```
   228  type Chart struct {
   229  	pulumi.ResourceState
   230  
   231  	Ready     pulumi.ResourceArrayOutput
   232  	Resources pulumi.Output
   233  }
   234  
   235  // NewChart registers a new resource with the given unique name, arguments, and options.
   236  func NewChart(ctx *pulumi.Context,
   237  	name string, args ChartArgs, opts ...pulumi.ResourceOption) (*Chart, error) {
   238  
   239  	// Register the resulting resource state.
   240  	chart := &Chart{}
   241  	aliases := pulumi.Aliases([]pulumi.Alias{
   242  		{
   243  			Type: pulumi.String("kubernetes:helm.sh/v2:Chart"),
   244  		},
   245  	})
   246  
   247  	var resourceOrInvokeOptions []pulumi.ResourceOrInvokeOption
   248  	for _, o := range opts {
   249  		if asResOrInv, ok := o.(pulumi.ResourceOrInvokeOption); ok {
   250  			resourceOrInvokeOptions = append(resourceOrInvokeOptions, asResOrInv)
   251  		}
   252  	}
   253  	opts = append(opts, aliases)
   254  	err := ctx.RegisterComponentResource("kubernetes:helm.sh/v3:Chart", name, chart, opts...)
   255  	if err != nil {
   256  		return nil, err
   257  	}
   258  
   259  	// Honor the resource name prefix if specified.
   260  	if args.ResourcePrefix != "" {
   261  		name = args.ResourcePrefix + "-" + name
   262  	}
   263  
   264  	resourceOrInvokeOptions = append(resourceOrInvokeOptions, pulumi.Parent(chart))
   265  	resources := args.ToChartArgsOutput().ApplyT(func(args chartArgs) (map[string]pulumi.Resource, error) {
   266  		return parseChart(ctx, name, args, resourceOrInvokeOptions...)
   267  	})
   268  	chart.Resources = resources
   269  
   270  	// Finally, register all of the resources found.
   271  	// Note: Go requires that we "pull" on our futures in order to get them scheduled for execution. Here, we use
   272  	// the engine's RegisterResourceOutputs to wait for the resolution of all resources that this Helm chart created.
   273  	err = ctx.RegisterResourceOutputs(chart, pulumi.Map{"resources": resources})
   274  	if err != nil {
   275  		return nil, errors.Wrap(err, "registering child resources")
   276  	}
   277  
   278  	chart.Ready = resources.ApplyT(func(x interface{}) []pulumi.Resource {
   279  		resources := x.(map[string]pulumi.Resource)
   280  		var outputs []pulumi.Resource
   281  		for _, r := range resources {
   282  			outputs = append(outputs, r)
   283  		}
   284  		return outputs
   285  	}).(pulumi.ResourceArrayOutput)
   286  
   287  	return chart, nil
   288  }
   289  
   290  func parseChart(ctx *pulumi.Context, name string, args chartArgs, opts ...pulumi.ResourceOrInvokeOption,
   291  ) (map[string]pulumi.Resource, error) {
   292  	type jsonOptsArgs struct {
   293  		chartArgs
   294  
   295  		ReleaseName string `json:"release_name,omitempty"`
   296  	}
   297  	jsonOpts := jsonOptsArgs{
   298  		chartArgs:   args,
   299  		ReleaseName: name,
   300  	}
   301  
   302  	b, err := json.Marshal(jsonOpts)
   303  	if err != nil {
   304  		return nil, err
   305  	}
   306  
   307  	var invokeOpts []pulumi.InvokeOption
   308  	var resourceOpts []pulumi.ResourceOption
   309  	for _, o := range opts {
   310  		invokeOpts = append(invokeOpts, o)
   311  		resourceOpts = append(resourceOpts, o)
   312  	}
   313  	objs, err := helmTemplate(ctx, string(b), invokeOpts...)
   314  	if err != nil {
   315  		return nil, err
   316  	}
   317  
   318  	transformations := args.Transformations
   319  	if args.SkipAwait {
   320  		transformations = yaml.AddSkipAwaitTransformation(transformations)
   321  	}
   322  
   323  	resources, err := yaml.ParseYamlObjects(ctx, objs, transformations, args.ResourcePrefix, resourceOpts...)
   324  	if err != nil {
   325  		return nil, err
   326  	}
   327  	return resources, nil
   328  }
   329  
   330  // helmTemplate invokes the function to fetch and template a Helm Chart and decompose it into object structures.
   331  func helmTemplate(
   332  	ctx *pulumi.Context, jsonOpts string, opts ...pulumi.InvokeOption,
   333  ) ([]map[string]interface{}, error) {
   334  	args := struct {
   335  		JsonOpts string `pulumi:"jsonOpts"`
   336  	}{JsonOpts: jsonOpts}
   337  	var ret struct {
   338  		Result []map[string]interface{} `pulumi:"result"`
   339  	}
   340  
   341  	if err := ctx.Invoke("kubernetes:helm:template", &args, &ret, opts...); err != nil {
   342  		return nil, errors.Wrap(err, "failed to invoke helm template")
   343  	}
   344  	return ret.Result, nil
   345  }
   346  
   347  // GetResource returns a resource defined by a built-in Kubernetes group/version/kind, name and namespace.
   348  // For example, GetResource("v1/Pod", "foo", "") would return a Pod called "foo" from the "default" namespace.
   349  func (c *Chart) GetResource(gvk, name, namespace string) pulumi.AnyOutput {
   350  	id := name
   351  	if len(namespace) > 0 && namespace != "default" {
   352  		id = fmt.Sprintf("%s/%s", namespace, name)
   353  	}
   354  	key := fmt.Sprintf("%s::%s", gvk, id)
   355  	return c.Resources.ApplyT(func(x interface{}) interface{} {
   356  		resources := x.(map[string]pulumi.Resource)
   357  		return resources[key]
   358  	}).(pulumi.AnyOutput)
   359  }