Skip to content
Open
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
14 changes: 12 additions & 2 deletions helm/data_helm_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -887,14 +887,24 @@ func chartPathOptionsModel(model *HelmTemplateModel, meta *Meta, cpo *action.Cha

version := getVersionModel(model)

digest := chartDigest(chartName)
if digest == "" {
digest = chartDigest(version)
}
if digest != "" && !strings.Contains(chartName, "@"+digest) {
chartName = chartName + "@" + digest
}

cpo.CaFile = model.RepositoryCaFile.ValueString()
cpo.CertFile = model.RepositoryCertFile.ValueString()
cpo.KeyFile = model.RepositoryKeyFile.ValueString()
cpo.Keyring = model.Keyring.ValueString()
cpo.RepoURL = repositoryURL
cpo.Verify = model.Verify.ValueBool()
if !useChartVersion(chartName, cpo.RepoURL) {
cpo.Version = version
if digest == "" {
if !useChartVersion(chartName, cpo.RepoURL) {
cpo.Version = version
}
}
cpo.Username = model.RepositoryUsername.ValueString()
cpo.Password = model.RepositoryPassword.ValueString()
Expand Down
113 changes: 102 additions & 11 deletions helm/resource_helm_release.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ type HelmReleaseModel struct {
Timeouts timeouts.Value `tfsdk:"timeouts"`
UpgradeInstall types.Bool `tfsdk:"upgrade_install"`
Values types.List `tfsdk:"values"`
ValuesWO types.List `tfsdk:"values_wo"`
ValuesWORevision types.Int64 `tfsdk:"values_wo_revision"`
Verify types.Bool `tfsdk:"verify"`
Version types.String `tfsdk:"version"`
Wait types.Bool `tfsdk:"wait"`
Expand Down Expand Up @@ -530,6 +532,19 @@ func (r *HelmRelease) Schema(ctx context.Context, req resource.SchemaRequest, re
Description: "List of values in raw YAML format to pass to helm",
ElementType: types.StringType,
},
"values_wo": schema.ListAttribute{
Description: "List of values in raw YAML format that are write-only and will not be stored in state or plan. Use values_wo_revision to trigger updates.",
Optional: true,
WriteOnly: true,
ElementType: types.StringType,
},
"values_wo_revision": schema.Int64Attribute{
Optional: true,
Description: `The current revision of the write-only "values_wo" attribute. Incrementing this integer value will cause Terraform to update the write-only value.`,
Validators: []validator.Int64{
int64validator.AtLeast(1),
},
},
"verify": schema.BoolAttribute{
Optional: true,
Computed: true,
Expand Down Expand Up @@ -840,6 +855,17 @@ func (r *HelmRelease) Create(ctx context.Context, req resource.CreateRequest, re
}
}

if config.ValuesWORevision.ValueInt64() > 0 {
woValues, woDiags := getWriteOnlyValuesYAML(ctx, &config)
resp.Diagnostics.Append(woDiags...)
if resp.Diagnostics.HasError() {
return
}
if len(woValues) > 0 {
values = mergeMaps(values, woValues)
}
}

err = isChartInstallable(c)
if err != nil {
resp.Diagnostics.AddError("Error checking if chart is installable", fmt.Sprintf("Chart is not installable: %s", err))
Expand Down Expand Up @@ -1192,6 +1218,17 @@ func (r *HelmRelease) Update(ctx context.Context, req resource.UpdateRequest, re
}
}

if plan.ValuesWORevision.ValueInt64() > state.ValuesWORevision.ValueInt64() {
woValues, woDiags := getWriteOnlyValuesYAML(ctx, &config)
resp.Diagnostics.Append(woDiags...)
if resp.Diagnostics.HasError() {
return
}
if len(woValues) > 0 {
values = mergeMaps(values, woValues)
}
}

name := plan.Name.ValueString()
release, err := client.Run(name, c, values)
if err != nil {
Expand Down Expand Up @@ -1324,14 +1361,24 @@ func chartPathOptions(model *HelmReleaseModel, meta *Meta, cpo *action.ChartPath

version := getVersion(model)

digest := chartDigest(chartName)
if digest == "" {
digest = chartDigest(version)
}
if digest != "" && !strings.Contains(chartName, "@"+digest) {
chartName = chartName + "@" + digest
}

cpo.CaFile = model.RepositoryCaFile.ValueString()
cpo.CertFile = model.RepositoryCertFile.ValueString()
cpo.KeyFile = model.RepositoryKeyFile.ValueString()
cpo.Keyring = model.Keyring.ValueString()
cpo.RepoURL = repositoryURL
cpo.Verify = model.Verify.ValueBool()
if !useChartVersion(chartName, cpo.RepoURL) {
cpo.Version = version
if digest == "" {
if !useChartVersion(chartName, cpo.RepoURL) {
cpo.Version = version
}
}
cpo.Username = model.RepositoryUsername.ValueString()
cpo.Password = model.RepositoryPassword.ValueString()
Expand All @@ -1340,6 +1387,16 @@ func chartPathOptions(model *HelmReleaseModel, meta *Meta, cpo *action.ChartPath
return cpo, chartName, diags
}

func chartDigest(ref string) string {
if idx := strings.LastIndex(ref, "@sha256:"); idx != -1 {
return ref[idx+1:]
}
if strings.HasPrefix(ref, "sha256:") {
return ref
}
return ""
}

func useChartVersion(chart string, repo string) bool {
// checks if chart is a URL or OCI registry

Expand Down Expand Up @@ -1431,6 +1488,37 @@ func getWriteOnlyValues(ctx context.Context, model *HelmReleaseModel) (map[strin
return base, diags
}

func getWriteOnlyValuesYAML(ctx context.Context, model *HelmReleaseModel) (map[string]interface{}, diag.Diagnostics) {
base := map[string]interface{}{}
diags := diag.Diagnostics{}

if !model.ValuesWO.IsUnknown() && !model.ValuesWO.IsNull() {
tflog.Debug(ctx, "Processing ValuesWO attribute")
for _, raw := range model.ValuesWO.Elements() {
if raw.IsNull() {
continue
}
value, ok := raw.(types.String)
if !ok {
diags.AddError("Type Error", fmt.Sprintf("Expected types.String, got %T", raw))
return nil, diags
}
values := value.ValueString()
if values == "" {
continue
}
currentMap := map[string]interface{}{}
if err := yaml.Unmarshal([]byte(values), &currentMap); err != nil {
diags.AddError("Error unmarshaling values_wo", fmt.Sprintf("---> %v %s", err, values))
return nil, diags
}
base = mergeMaps(base, currentMap)
}
}

return base, diags
}

func getValues(ctx context.Context, model *HelmReleaseModel) (map[string]interface{}, diag.Diagnostics) {
base := map[string]interface{}{}
var diags diag.Diagnostics
Expand Down Expand Up @@ -1735,11 +1823,11 @@ func setReleaseAttributes(ctx context.Context, state *HelmReleaseModel, identity
}
}

// NOTE Don't retrieve values if write-only is being used.
// NOTE Don't retrieve values if any write-only attribute is being used.
// It is not possible to pick out which values are write-only
// at read time because write-only values are ephemeral
valuesstr := types.StringValue("{}")
if state.SetWORevision.ValueInt64() <= 0 {
if state.SetWORevision.ValueInt64() <= 0 && state.ValuesWORevision.ValueInt64() <= 0 {
valuesstr = types.StringValue(values)
}

Expand Down Expand Up @@ -2030,10 +2118,11 @@ func (r *HelmRelease) ModifyPlan(ctx context.Context, req resource.ModifyPlanReq
if config.Version.IsNull() {
plan.Version = types.StringUnknown()
}
resp.Plan.Set(ctx, &plan)
return
}

resp.Diagnostics.AddWarning(
"Manifest deferred to apply time",
"Some values contain unknown references at plan time. The manifest will be computed during apply.",
)
} else {
if plan.PostRender != nil {
binaryPath := plan.PostRender.BinaryPath.ValueString()
argsList := plan.PostRender.Args.Elements()
Expand Down Expand Up @@ -2224,6 +2313,7 @@ func (r *HelmRelease) ModifyPlan(ctx context.Context, req resource.ModifyPlanReq
plan.Metadata = types.ObjectUnknown(metadataAttrTypes())
}

}
} else {
plan.Manifest = types.StringNull()
plan.Resources = types.MapNull(types.StringType)
Expand Down Expand Up @@ -2466,6 +2556,7 @@ func (r *HelmRelease) ImportState(ctx context.Context, req resource.ImportStateR
},
})
state.Values = types.ListNull(types.StringType)
state.ValuesWO = types.ListNull(types.StringType)

tflog.Debug(ctx, fmt.Sprintf("Setting final state: %+v", state))
diags = resp.State.Set(ctx, &state)
Expand Down Expand Up @@ -2529,9 +2620,9 @@ func valuesUnknown(plan HelmReleaseModel) bool {
}
}

setList := []setResourceModel{}
plan.Set.ElementsAs(context.Background(), &setList, false)
for _, s := range setList {
setListVals := []set_listResourceModel{}
plan.SetList.ElementsAs(context.Background(), &setListVals, false)
for _, s := range setListVals {
if s.Value.IsUnknown() {
return true
}
Expand Down
Loading