Skip to content

Commit

Permalink
Allow makefile substitutions
Browse files Browse the repository at this point in the history
  • Loading branch information
purkhusid committed Jan 20, 2025
1 parent 69b9455 commit 2780adc
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 8 deletions.
4 changes: 1 addition & 3 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ module(
bazel_dep(name = "platforms", version = "0.0.10")
bazel_dep(name = "bazel_skylib", version = "1.7.1")
bazel_dep(name = "rules_go", version = "0.50.1", repo_name = "io_bazel_rules_go")
bazel_dep(name = "aspect_bazel_lib", version = "2.7.2")

# This is unfortunately required by `rules_oci`.
# https://github.com/bazel-contrib/rules_oci/issues/575
bazel_dep(name = "aspect_bazel_lib", version = "2.7.2", dev_dependency = True)
bazel_dep(name = "rules_oci", version = "2.0.0", dev_dependency = True)
bazel_dep(name = "stardoc", version = "0.7.1", dev_dependency = True, repo_name = "io_bazel_stardoc")

Expand Down
1 change: 1 addition & 0 deletions helm/private/helm.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def helm_chart(
chart = chart,
chart_json = chart_json,
crds = crds,
data = data,
deps = deps,
images = images,
templates = templates,
Expand Down
13 changes: 10 additions & 3 deletions helm/private/helm_package.bzl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Helm rules"""

load("@aspect_bazel_lib//lib:expand_make_vars.bzl", "expand_locations", "expand_variables")
load("//helm:providers.bzl", "HelmPackageInfo")
load("//helm/private:helm_utils.bzl", "is_stamping_enabled")
load("//helm/private:json_to_yaml.bzl", "json_to_yaml")
Expand Down Expand Up @@ -108,9 +109,10 @@ def _helm_package_impl(ctx):
args.add("-values", values_yaml)

substitutions_file = ctx.actions.declare_file("{}/substitutions.json".format(ctx.label.name))
expanded_substitutions = {k: expand_variables(ctx, expand_locations(ctx, v, ctx.attr.data)) for k, v in ctx.attr.substitutions.items()}
ctx.actions.write(
output = substitutions_file,
content = json.encode_indent(ctx.attr.substitutions, indent = " " * 4),
content = json.encode_indent(expanded_substitutions, indent = " " * 4),
)
args.add("-substitutions", substitutions_file)

Expand Down Expand Up @@ -196,7 +198,7 @@ def _helm_package_impl(ctx):
executable = ctx.executable._packager,
outputs = [output, metadata_output],
inputs = depset(
ctx.files.templates + ctx.files.files + ctx.files.crds + stamps + image_inputs + deps + [
ctx.files.templates + ctx.files.data + ctx.files.files + ctx.files.crds + stamps + image_inputs + deps + [
chart_yaml,
values_yaml,
templates_manifest,
Expand Down Expand Up @@ -240,6 +242,11 @@ helm_package = rule(
default = [],
allow_files = True,
),
"data": attr.label_list(
doc = "Additional data files used for e.g. template substitutions. Note that these files are not included in the created helm chart but only used during the build process.",
default = [],
allow_files = True,
),
"deps": attr.label_list(
doc = "Other helm packages this package depends on.",
providers = [HelmPackageInfo],
Expand Down Expand Up @@ -276,7 +283,7 @@ helm_package = rule(
values = [1, 0, -1],
),
"substitutions": attr.string_dict(
doc = "A dictionary of substitutions to apply to the `values.yaml` file.",
doc = "A dictionary of substitutions to apply to the `values.yaml` file. Supports make variable expansion. You can append `| readfile` to read the content of the file",
default = {},
),
"templates": attr.label_list(
Expand Down
22 changes: 20 additions & 2 deletions helm/private/packager/packager.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,21 @@ func replaceKeyValues(content string, replacementGroups []ReplacementGroup, must
return content, nil
}

func readFileIfExists(path string) (string, error) {
if strings.HasSuffix(path, "| readfile") {
path = strings.TrimSpace(strings.TrimSuffix(path, "| readfile"))

content, err := os.ReadFile(path)
if err != nil {
return "", fmt.Errorf("Error reading file %s: %w", path, err)
}

return string(content), nil
} else {
return path, nil
}
}

func applySubstitutions(content string, substitutions_file string) (string, error) {
if len(substitutions_file) == 0 {
return content, nil
Expand All @@ -374,8 +389,11 @@ func applySubstitutions(content string, substitutions_file string) (string, erro
}

for key, val := range substitutions {
replaceKey := fmt.Sprintf("{%s}", key)
content = strings.ReplaceAll(content, replaceKey, val)
replacmentValue, err := readFileIfExists(val)
if err != nil {
return "", fmt.Errorf("Error reading file %s: %w", val, err)
}
content = strings.ReplaceAll(content, key, replacmentValue)
}

return content, nil
Expand Down
1 change: 1 addition & 0 deletions tests/substitutions/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
charts
23 changes: 23 additions & 0 deletions tests/substitutions/.helmignore
Original file line number Diff line number Diff line change
@@ -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/
51 changes: 51 additions & 0 deletions tests/substitutions/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
load("@rules_oci//oci:defs.bzl", "oci_image", "oci_push")
load("//helm:defs.bzl", "helm_chart", "helm_lint_test", "helm_template_test")
load("//tests:test_defs.bzl", "helm_package_regex_test")

EXCLUDE_WINDOWS = select({
# TODO: rules_oci is broken on simple windows systems such as the windows github runners
# https://github.com/abrisco/rules_helm/issues/53
"@platforms//os:windows": ["@platforms//:incompatible"],
"//conditions:default": [],
})

oci_image(
name = "image",
base = "@rules_helm_test_container_base",
target_compatible_with = EXCLUDE_WINDOWS,
)

helm_chart(
name = "substitutions",
chart = "Chart.yaml",
data = [
":image.digest",
],
registry_url = "oci://localhost/helm-registry",
substitutions = {
"this_should_be_replaced": "replacement_value",
"image_sha256": "$(execpath :image.digest) | readfile",
},
values = "values.yaml",
visibility = ["//tests/with_chart_deps:__subpackages__"],
)

helm_lint_test(
name = "substitutions_lint_test",
chart = ":substitutions",
)

helm_template_test(
name = "substitutions_template_test",
chart = ":substitutions",
)

helm_package_regex_test(
name = "substitutions_regex_test",
package = ":substitutions",
target_compatible_with = EXCLUDE_WINDOWS,
values_patterns = [
r"replacement_value",
r"sha256:54bee42e3bf22cd1781f5cbc1e1cc9b5cff1d6766355148d66b90f09e9936db3",
],
)
24 changes: 24 additions & 0 deletions tests/substitutions/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
apiVersion: v2
name: substitutions
description: A Helm chart for Kubernetes

# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application

# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.0

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.16.0"
18 changes: 18 additions & 0 deletions tests/substitutions/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: test
spec:
replicas: 1
selector:
matchLabels:
app: test
template:
metadata:
labels:
app: test
spec:
containers:
- name: {{ .Values.stringReplacment }}
image: "nginx@{{ .Values.image.sha256 }}"
imagePullPolicy: IfNotPresent
10 changes: 10 additions & 0 deletions tests/substitutions/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Default values for simple.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.

replicaCount: 1

stringReplacment: this_should_be_replaced

image:
sha256: {image_sha256}

0 comments on commit 2780adc

Please sign in to comment.