Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for using values from yaml files #1828

Merged
merged 6 commits into from
Dec 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- Helm Release: fix username fetch option (https://github.com/pulumi/pulumi-kubernetes/pull/1824)
- Helm Release: Use URN name as base for autonaming, Drop warning, fix default value for
keyring (https://github.com/pulumi/pulumi-kubernetes/pull/1826)
- Helm Release: Add support for loading values from yaml files (https://github.com/pulumi/pulumi-kubernetes/pull/1828)

- Fix CRD upgrades (https://github.com/pulumi/pulumi-kubernetes/pull/1819)

Expand Down
2 changes: 1 addition & 1 deletion provider/cmd/pulumi-resource-kubernetes/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -41736,7 +41736,7 @@
"items": {
"$ref": "pulumi.json#/Asset"
},
"description": "List of assets (raw yaml files). Content is read and merged with values. Not yet supported."
"description": "List of assets (raw yaml files). Content is read and merged with values (with values taking precedence)."
},
"values": {
"type": "object",
Expand Down
2 changes: 1 addition & 1 deletion provider/pkg/gen/overlays.go
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ var helmV3ReleaseResource = pschema.ResourceSpec{
Ref: "pulumi.json#/Asset",
},
},
Description: "List of assets (raw yaml files). Content is read and merged with values. Not yet supported.",
Description: "List of assets (raw yaml files). Content is read and merged with values (with values taking precedence).",
},
"values": {
TypeSpec: pschema.TypeSpec{
Expand Down
31 changes: 28 additions & 3 deletions provider/pkg/provider/helm_release.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"log"
"net/url"
"os"
"reflect"
"strings"
"time"

Expand Down Expand Up @@ -113,8 +114,6 @@ type Release struct {
SkipCrds bool `json:"skipCrds,omitempty"`
// Time in seconds to wait for any individual kubernetes operation.
Timeout int `json:"timeout,omitempty"`
// ValueYamlFiles List of assets (raw yaml files) to pass to helm.
//ValueYamlFiles []*resource.Asset `json:"valueYamlFiles,omitempty"`
// Verify the package before installing it.
Verify bool `json:"verify,omitempty"`
// Specify the exact chart version to install. If this is not specified, the latest version is installed.
Expand Down Expand Up @@ -250,11 +249,37 @@ func (r *helmReleaseProvider) getActionConfig(namespace string) (*action.Configu

func decodeRelease(pm resource.PropertyMap) (*Release, error) {
var release Release
values := map[string]interface{}{}
stripped := pm.MapRepl(nil, mapReplStripSecrets)
logger.V(9).Infof("Decoding release: %#v", stripped)

if pm.HasValue("valueYamlFiles") {
v := stripped["valueYamlFiles"]
switch reflect.TypeOf(v).Kind() {
case reflect.Slice, reflect.Array:
s := reflect.ValueOf(v)
for i := 0; i < s.Len(); i++ {
val := s.Index(i).Interface()
switch t := val.(type) {
case *resource.Asset:
b, err := t.Bytes()
if err != nil {
return nil, err
}
if err = yaml.Unmarshal(b, &values); err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("unsupported type for 'valueYamlFiles' arg: %T", v)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to also support Archives? I just noticed that the SDK type supports both Assets and Archives.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trying to keep this in-line with the helm CLI. The CLI takes yaml files and not archives. We can reconsider if we get requests from users. I can't think of an obvious use case where archives would be preferred.

}
}
}
}

if err := mapstructure.Decode(stripped, &release); err != nil {
return nil, fmt.Errorf("decoding failure: %w", err)
}
release.Values = mergeMaps(release.Values, values)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The precedence - if it's not clear - will prioritize the values in the values field over what is specified in the files.

return &release, nil
}

Expand Down Expand Up @@ -1067,7 +1092,7 @@ func setReleaseAttributes(release *Release, r *release.Release, isPreview bool)
release.Chart = r.Chart.Metadata.Name
}
logger.V(9).Infof("Setting release values: %+v", r.Config)
release.Values = r.Config
release.Values = mergeMaps(release.Values, r.Config)
release.Version = r.Chart.Metadata.Version

_, resources, err := convertYAMLManifestToJSON(r.Manifest)
Expand Down
6 changes: 0 additions & 6 deletions provider/pkg/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,6 @@ func (k *kubeProvider) CheckConfig(ctx context.Context, req *pulumirpc.CheckRequ
Label: fmt.Sprintf("%s.news", label),
KeepUnknowns: true,
SkipNulls: true,
RejectAssets: true,
})
if err != nil {
return nil, pkgerrors.Wrapf(err, "CheckConfig failed because of malformed resource inputs")
Expand Down Expand Up @@ -299,7 +298,6 @@ func (k *kubeProvider) DiffConfig(ctx context.Context, req *pulumirpc.DiffReques
Label: fmt.Sprintf("%s.news", label),
KeepUnknowns: true,
SkipNulls: true,
RejectAssets: true,
})
if err != nil {
return nil, pkgerrors.Wrapf(err, "DiffConfig failed because of malformed resource inputs")
Expand Down Expand Up @@ -1209,7 +1207,6 @@ func (k *kubeProvider) Check(ctx context.Context, req *pulumirpc.CheckRequest) (
Label: fmt.Sprintf("%s.news", label),
KeepUnknowns: true,
SkipNulls: true,
RejectAssets: true,
KeepSecrets: true,
})
if err != nil {
Expand Down Expand Up @@ -1431,7 +1428,6 @@ func (k *kubeProvider) Diff(ctx context.Context, req *pulumirpc.DiffRequest) (*p
Label: fmt.Sprintf("%s.news", label),
KeepUnknowns: true,
SkipNulls: true,
RejectAssets: true,
KeepSecrets: true,
})
if err != nil {
Expand Down Expand Up @@ -1642,7 +1638,6 @@ func (k *kubeProvider) Create(
Label: fmt.Sprintf("%s.properties", label),
KeepUnknowns: true,
SkipNulls: true,
RejectAssets: true,
KeepSecrets: true,
})
if err != nil {
Expand Down Expand Up @@ -2078,7 +2073,6 @@ func (k *kubeProvider) Update(
Label: fmt.Sprintf("%s.news", label),
KeepUnknowns: true,
SkipNulls: true,
RejectAssets: true,
KeepSecrets: true,
})
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion sdk/dotnet/Helm/V3/Release.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ public partial class Release : KubernetesResource
public Output<int> Timeout { get; private set; } = null!;

/// <summary>
/// List of assets (raw yaml files). Content is read and merged with values. Not yet supported.
/// List of assets (raw yaml files). Content is read and merged with values (with values taking precedence).
/// </summary>
[Output("valueYamlFiles")]
public Output<ImmutableArray<AssetOrArchive>> ValueYamlFiles { get; private set; } = null!;
Expand Down
2 changes: 1 addition & 1 deletion sdk/go/kubernetes/helm/v3/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ type Release struct {
Status ReleaseStatusOutput `pulumi:"status"`
// Time in seconds to wait for any individual kubernetes operation.
Timeout pulumi.IntPtrOutput `pulumi:"timeout"`
// List of assets (raw yaml files). Content is read and merged with values. Not yet supported.
// List of assets (raw yaml files). Content is read and merged with values (with values taking precedence).
ValueYamlFiles pulumi.AssetOrArchiveArrayOutput `pulumi:"valueYamlFiles"`
// Custom values set for the release.
Values pulumi.MapOutput `pulumi:"values"`
Expand Down
2 changes: 1 addition & 1 deletion sdk/nodejs/helm/v3/release.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export class Release extends pulumi.CustomResource {
*/
public readonly timeout!: pulumi.Output<number>;
/**
* List of assets (raw yaml files). Content is read and merged with values. Not yet supported.
* List of assets (raw yaml files). Content is read and merged with values (with values taking precedence).
*/
public readonly valueYamlFiles!: pulumi.Output<(pulumi.asset.Asset | pulumi.asset.Archive)[]>;
/**
Expand Down
2 changes: 1 addition & 1 deletion sdk/python/pulumi_kubernetes/helm/v3/Release.py
Original file line number Diff line number Diff line change
Expand Up @@ -1043,7 +1043,7 @@ def timeout(self) -> pulumi.Output[Optional[int]]:
@pulumi.getter(name="valueYamlFiles")
def value_yaml_files(self) -> pulumi.Output[Optional[Sequence[Union[pulumi.Asset, pulumi.Archive]]]]:
"""
List of assets (raw yaml files). Content is read and merged with values. Not yet supported.
List of assets (raw yaml files). Content is read and merged with values (with values taking precedence).
"""
return pulumi.get(self, "value_yaml_files")

Expand Down
59 changes: 42 additions & 17 deletions tests/sdk/nodejs/examples/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,32 +390,57 @@ func TestAccProvider(t *testing.T) {

func TestHelmRelease(t *testing.T) {
skipIfShort(t)
validationFunc := func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
assert.NotEmpty(t, stackInfo.Outputs["redisMasterClusterIP"].(string))
assert.Equal(t, stackInfo.Outputs["status"], "deployed")
for _, res := range stackInfo.Deployment.Resources {
if res.Type == "kubernetes:helm.sh/v3:Release" {
version, has := res.Inputs["version"]
assert.True(t, has)
stat, has := res.Outputs["status"]
assert.True(t, has)
specMap, is := stat.(map[string]interface{})
assert.True(t, is)
versionOut, has := specMap["version"]
assert.True(t, has)
assert.Equal(t, version, versionOut)
values, has := res.Outputs["values"]
assert.True(t, has)
assert.Contains(t, values, "cluster")
valMap := values.(map[string]interface{})
assert.Equal(t, valMap["cluster"], map[string]interface{}{
"enabled": true,
"slaveCount": float64(2),
})
// not asserting contents since the secret is hard to assert equality on.
assert.Contains(t, values, "global")
assert.Contains(t, values, "metrics")
assert.Equal(t, valMap["metrics"], map[string]interface{}{
"enabled": true,
"service": map[string]interface{}{
"annotations": map[string]interface{}{
"prometheus.io/port": "9127",
},
},
})
assert.Contains(t, values,"rbac")
assert.Equal(t, valMap["rbac"], map[string]interface{}{
"create": true,
})
}
}
}
test := getBaseOptions(t).
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "helm-release", "step1"),
SkipRefresh: false,
Verbose: true,
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
assert.NotEmpty(t, stackInfo.Outputs["redisMasterClusterIP"].(string))
assert.Equal(t, stackInfo.Outputs["status"], "deployed")
for _, res := range stackInfo.Deployment.Resources {
if res.Type == "kubernetes:helm.sh/v3:Release" {
version, has := res.Inputs["version"]
assert.True(t, has)
stat, has := res.Outputs["status"]
assert.True(t, has)
specMap, is := stat.(map[string]interface{})
assert.True(t, is)
versionOut, has := specMap["version"]
assert.True(t, has)
assert.Equal(t, version, versionOut)
}
}
},
ExtraRuntimeValidation: validationFunc,
EditDirs: []integration.EditDir{
{
Dir: filepath.Join(getCwd(t), "helm-release", "step2"),
Additive: true,
ExtraRuntimeValidation: validationFunc,
},
},
})
Expand Down
11 changes: 2 additions & 9 deletions tests/sdk/nodejs/examples/helm-release/step1/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as pulumi from "@pulumi/pulumi";
import * as k8s from "@pulumi/kubernetes";
import * as random from "@pulumi/random";

import { FileAsset } from "@pulumi/pulumi/asset";

const redisPassword = pulumi.secret("$053cr3t!");

Expand All @@ -19,19 +19,12 @@ const release = new k8s.helm.v3.Release("release", {
},
version: "13.0.0",
namespace: namespace.metadata.name,
valueYamlFiles: [new FileAsset("./metrics.yml")],
values: {
cluster: {
enabled: true,
slaveCount: 2,
},
metrics: {
enabled: true,
service: {
annotations: {
"prometheus.io/port": "9127",
}
},
},
global: {
redis: {
password: redisPassword,
Expand Down
5 changes: 5 additions & 0 deletions tests/sdk/nodejs/examples/helm-release/step1/metrics.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
metrics:
enabled: true
service:
annotations:
"prometheus.io/port": "9127"
15 changes: 4 additions & 11 deletions tests/sdk/nodejs/examples/helm-release/step2/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as pulumi from "@pulumi/pulumi";
import * as k8s from "@pulumi/kubernetes";
import * as random from "@pulumi/random";

import { FileAsset } from "@pulumi/pulumi/asset";

const redisPassword = pulumi.secret("$053cr3t!");

Expand All @@ -19,18 +19,11 @@ const release = new k8s.helm.v3.Release("release", {
},
version: "13.0.1", // <--- change
namespace: namespace.metadata.name,
valueYamlFiles: [new FileAsset("./metrics.yml")],
values: {
cluster: {
enabled: true,
slaveCount: 3,
},
metrics: {
enabled: true,
service: {
annotations: {
"prometheus.io/port": "9127",
}
},
slaveCount: 2,
},
global: {
redis: {
Expand All @@ -46,4 +39,4 @@ const release = new k8s.helm.v3.Release("release", {

const srv = k8s.core.v1.Service.get("redis-master-svc", pulumi.interpolate`${release.status.namespace}/${release.status.name}-redis-master`);
export const redisMasterClusterIP = srv.spec.clusterIP;
export const status = release.status;
export const status = release.status.status;