Skip to content

Commit cfc03ca

Browse files
authored
Merge pull request #47 from yeahdongcn/feat/conditions
Update config to support conditions and operator
2 parents 4e01114 + 60d3995 commit cfc03ca

File tree

17 files changed

+388
-31
lines changed

17 files changed

+388
-31
lines changed

Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ examples: build kubernetes-split-yaml ## Test the binary against the examples.
5252
cd examples/memcached-operator; KUSTOHELMIZE=../../bin/kustohelmize make helm
5353

5454
.PHONY: test
55-
test: go-test build kubernetes-split-yaml 0100 0200 0300 0400 0500 0600 0700 0800 0900 ## Test the binary.
55+
test: go-test build kubernetes-split-yaml 0100 0200 0300 0400 0500 0600 0700 0800 0900 1000 ## Test the binary.
5656

5757
.PHONY: go-test
5858
go-test:
@@ -112,6 +112,12 @@ go-test:
112112
$(HELM) lint test/output/0900/mychart
113113
$(HELM) install --dry-run --generate-name test/output/0900/mychart -n default
114114

115+
.PHONY: 1000
116+
1000: build
117+
$(KUSTOHELMIZE) create --from=test/testdata/0800_deployment.yaml test/output/1000/mychart
118+
$(HELM) lint test/output/1000/mychart
119+
$(HELM) install --dry-run --generate-name test/output/1000/mychart -n default
120+
115121
##@ Tools
116122

117123
KUBERNETES-SPLIT-YAML = $(shell pwd)/bin/kubernetes-split-yaml

examples/README.md

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,33 @@ We also introduce the `strategy` in the configuration file.
252252
{{- end }}
253253
```
254254

255+
The `control-if` strategy also supports specifying one or more conditions:
256+
257+
```yaml
258+
spec.template.spec.containers[0].ports[2]:
259+
- strategy: control-if
260+
conditions:
261+
- key: "!sharedValues.webhook.disabled"
262+
- key: sharedValues.tls.enabled
263+
conditionOperator: and
264+
```
265+
266+
A condition can be negated by prefixing the key with `!`. The `conditionOperator` can be `and` or `or`.</br>
267+
268+
This generates the following Helm template:
269+
270+
```yaml
271+
{{- if and (not .Values.webhook.disabled) .Values.tls.enabled }}
272+
- containerPort: 9443
273+
name: webhook-server
274+
protocol: TCP
275+
{{- end }}
276+
```
277+
278+
> __Note__: The following `condition` and `conditionValue` are deprecated in version `0.5.0`. Use `conditions` instead.
279+
280+
<details>
281+
255282
The `control-if` strategy also supports specifying a condition:
256283

257284
```yaml
@@ -287,7 +314,9 @@ We also introduce the `strategy` in the configuration file.
287314
{{- end }}
288315
```
289316

290-
1. `control-if-yaml`
317+
</details>
318+
319+
2. `control-if-yaml`
291320

292321
```
293322
{{- if .Values.operator.initContainer.imagePullSecrets }}
@@ -316,7 +345,7 @@ We also introduce the `strategy` in the configuration file.
316345
{{- end }}
317346
```
318347

319-
1. `control-range`
348+
3. `control-range`
320349

321350
```
322351
imagePullSecrets:
@@ -325,7 +354,7 @@ We also introduce the `strategy` in the configuration file.
325354
{{- end }}
326355
```
327356

328-
1. `file-if`
357+
4. `file-if`
329358

330359
Conditionally includes or omits an entire resource manifest.
331360

@@ -352,7 +381,7 @@ We also introduce the `strategy` in the configuration file.
352381
key: sharedValues.promethues.enabled
353382
```
354383

355-
1. `inline-regex`
384+
5. `inline-regex`
356385

357386
Allows insertion of a templated value as part of an overall string, such as the value for a pod's command line argument.
358387

@@ -382,7 +411,7 @@ We also introduce the `strategy` in the configuration file.
382411
This is how to do it.
383412

384413
1. Edit the kustohelmize configuration file.
385-
1. Set up the deployment's configuration like this:
414+
2. Set up the deployment's configuration like this:
386415

387416
```yaml
388417
fileConfig:
@@ -400,7 +429,7 @@ We also introduce the `strategy` in the configuration file.
400429
key: manager.probe.port
401430
```
402431

403-
1. The emitted Helm template will be:
432+
3. The emitted Helm template will be:
404433

405434
```yaml
406435
- args:
@@ -423,7 +452,7 @@ We also introduce the `strategy` in the configuration file.
423452

424453
The match group in the regex `(\d+)` is templated with the `.Values` identified by `key`. Currently, only one replacement per list item is possible.
425454

426-
1. `append-with`
455+
6. `append-with`
427456

428457
Allows appending a list of values to an existing list in the Helm template.
429458

pkg/config/config.go

Lines changed: 77 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -42,23 +42,31 @@ const (
4242
XPathStrategyAppendWith XPathStrategy = "append-with"
4343
)
4444

45+
type Condition struct {
46+
Key string `yaml:"key,omitempty"`
47+
Value bool `yaml:"value,omitempty"`
48+
}
49+
4550
type XPathConfig struct {
46-
Strategy XPathStrategy `yaml:"strategy"`
47-
Key string `yaml:"key"`
48-
Value interface{} `yaml:"value,omitempty"`
49-
DefaultValue interface{} `yaml:"defaultValue,omitempty"`
50-
Regex string `yaml:"regex,omitempty"`
51-
RegexCompiled *regexp2.Regexp `yaml:"-"`
52-
Condition string `yaml:"condition,omitempty"`
53-
ConditionValue bool `yaml:"conditionValue,omitempty" default:"false"`
51+
Strategy XPathStrategy `yaml:"strategy"`
52+
Key string `yaml:"key"`
53+
Value interface{} `yaml:"value,omitempty"`
54+
DefaultValue interface{} `yaml:"defaultValue,omitempty"`
55+
Regex string `yaml:"regex,omitempty"`
56+
RegexCompiled *regexp2.Regexp `yaml:"-"`
57+
Conditions []Condition `yaml:"conditions,omitempty"`
58+
ConditionOperator *string `yaml:"conditionOperator,omitempty"`
59+
// Deprecated
60+
Condition string `yaml:"condition,omitempty"`
61+
ConditionValue bool `yaml:"conditionValue,omitempty"`
5462
}
5563

56-
func (x *XPathConfig) ValueRequiresQuote() bool {
57-
if x.Value == nil {
64+
func (xc *XPathConfig) ValueRequiresQuote() bool {
65+
if xc.Value == nil {
5866
return false
5967
}
6068

61-
switch value := x.Value.(type) {
69+
switch value := xc.Value.(type) {
6270
case bool, int, float64:
6371
return true
6472
case string:
@@ -206,8 +214,12 @@ func (cc *ChartConfig) Values() (string, error) {
206214

207215
kvs := []kvPair{{c.Key, c.Value}}
208216
if c.Condition != "" {
209-
condition := strings.TrimPrefix(c.Condition, "!")
210-
kvs = append(kvs, kvPair{condition, c.ConditionValue})
217+
conditionKey := strings.TrimPrefix(c.Condition, "!")
218+
kvs = append(kvs, kvPair{conditionKey, c.ConditionValue})
219+
}
220+
for _, condition := range c.Conditions {
221+
conditionKey := strings.TrimPrefix(condition.Key, "!")
222+
kvs = append(kvs, kvPair{conditionKey, condition.Value})
211223
}
212224
for _, kv := range kvs {
213225
substrings := strings.Split(kv.Key, XPathSeparator)
@@ -295,14 +307,40 @@ func (c *ChartConfig) formatKey(key, prefix string, keyType KeyType, strategy XP
295307
}
296308

297309
func (c *ChartConfig) GetFormattedCondition(xc *XPathConfig, prefix string) (string, bool) {
298-
not := strings.HasPrefix(xc.Condition, "!")
299-
condition := strings.TrimPrefix(xc.Condition, "!")
300-
key, keyType := c.determineKeyType(condition)
301-
if key == "" {
302-
return key, not
310+
formatCondition := func(condition string) (string, bool) {
311+
not := strings.HasPrefix(condition, "!")
312+
conditionKey := strings.TrimPrefix(condition, "!")
313+
key, keyType := c.determineKeyType(conditionKey)
314+
if key == "" {
315+
return "", not
316+
}
317+
return c.formatKey(key, prefix, keyType, xc.Strategy), not
303318
}
304-
key = c.formatKey(key, prefix, keyType, xc.Strategy)
305-
return key, not
319+
320+
formatMultipleConditions := func(conditions []Condition) (string, bool) {
321+
keys := make([]string, len(conditions))
322+
for i, condition := range conditions {
323+
key, not := formatCondition(condition.Key)
324+
if key != "" {
325+
if not {
326+
key = fmt.Sprintf("(not %s)", key)
327+
}
328+
keys[i] = key
329+
}
330+
}
331+
332+
return fmt.Sprintf("%s %s", *xc.ConditionOperator, strings.Join(keys, " ")), false
333+
}
334+
335+
if xc.Condition != "" {
336+
return formatCondition(xc.Condition)
337+
} else if len(xc.Conditions) > 0 {
338+
if len(xc.Conditions) == 1 {
339+
return formatCondition(xc.Conditions[0].Key)
340+
}
341+
return formatMultipleConditions(xc.Conditions)
342+
}
343+
return "", false
306344
}
307345

308346
func (c *ChartConfig) GetFormattedKeyWithDefaultValue(xc *XPathConfig, prefix string) (string, KeyType) {
@@ -322,6 +360,10 @@ func (c *ChartConfig) Validate() error {
322360
// - file-if can only be present at root level file configs
323361
// - globalConfig cannot contain a root level entry
324362
// - inline-regex must have regex property, and the regex must compile and contain exactly one capture group
363+
// - control-if and control-if-yaml with multiple conditions:
364+
// - must have conditionOperator property
365+
// - conditionOperator must be 'and' or 'or'
366+
// - other strategies cannot have condition or conditions property
325367
if _, ok := c.GlobalConfig[XPathRoot]; ok {
326368
return fmt.Errorf("cannot have root level config in GlobalConfig")
327369
}
@@ -343,6 +385,21 @@ func (c *ChartConfig) Validate() error {
343385
return fmt.Errorf("'%s' strategy '%s': regular expression '%s' must have exactly one replacement group", manifest, strategy, xpathConfig.Regex)
344386
}
345387
xpathConfigs[i].RegexCompiled = rx
388+
} else if strategy == XPathStrategyControlIf || strategy == XPathStrategyControlIfYAML {
389+
if len(xpathConfig.Conditions) > 1 {
390+
if xpathConfig.ConditionOperator == nil {
391+
return fmt.Errorf("'%s' strategy '%s' must have 'conditionOperator' property", manifest, strategy)
392+
}
393+
}
394+
if xpathConfig.ConditionOperator != nil {
395+
if *xpathConfig.ConditionOperator != "and" && *xpathConfig.ConditionOperator != "or" {
396+
return fmt.Errorf("'%s' strategy '%s' conditionOperator must be 'and' or 'or'", manifest, strategy)
397+
}
398+
}
399+
} else {
400+
if xpathConfig.Condition != "" || len(xpathConfig.Conditions) > 0 {
401+
return fmt.Errorf("'%s' strategy '%s' cannot have 'condition' or 'conditions' property", manifest, strategy)
402+
}
346403
}
347404
}
348405
}

pkg/config/config_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ func TestKeyExist2(t *testing.T) {
1313
config := NewChartConfig(logger, "chart")
1414
config.SharedValues["replicas"] = 1
1515
xc := XPathConfig{
16-
Strategy: XPathStrategyInline,
17-
Key: fmt.Sprintf("%s%s%s", sharedValuesPrefix, XPathSeparator, "replicas"),
16+
Key: fmt.Sprintf("%s%s%s", sharedValuesPrefix, XPathSeparator, "replicas"),
1817
}
1918
key, exist := config.keyExist(xc.Key)
2019
require.True(t, exist)

test/output/0800/mychart-generated/nginx-deployment-deployment.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,6 @@ spec:
2626
- name: https
2727
containerPort: 443
2828
protocol: TCP
29+
- containerPort: 9443
30+
name: webhook-server
31+
protocol: TCP

test/output/0800/mychart/templates/nginx-deployment-deployment.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,6 @@ spec:
3535
name: https
3636
protocol: TCP
3737
{{- end }}
38+
- containerPort: 9443
39+
name: webhook-server
40+
protocol: TCP

test/output/0900/mychart-generated/nginx-deployment-deployment.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,6 @@ spec:
2626
- name: https
2727
containerPort: 443
2828
protocol: TCP
29+
- containerPort: 9443
30+
name: webhook-server
31+
protocol: TCP

test/output/0900/mychart.config

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ fileConfig:
3131
condition: sharedValues.arg0.enabled
3232
spec.template.spec.containers[0].ports[0]:
3333
- strategy: control-if
34-
key: ""
3534
condition: sharedValues.http.enabled
3635
spec.template.spec.containers[0].ports:
3736
- strategy: append-with

test/output/0900/mychart/templates/nginx-deployment-deployment.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ spec:
3131
- containerPort: 443
3232
name: https
3333
protocol: TCP
34+
- containerPort: 9443
35+
name: webhook-server
36+
protocol: TCP
3437
{{- with .Values.ports }}
3538
{{- toYaml . | nindent 12 }}
3639
{{- end }}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: nginx-deployment
5+
namespace: default
6+
spec:
7+
selector:
8+
matchLabels:
9+
app: nginx
10+
replicas: 2
11+
template:
12+
metadata:
13+
labels:
14+
app: nginx
15+
spec:
16+
containers:
17+
- name: nginx
18+
image: nginx:latest
19+
args:
20+
- arg0
21+
- arg1
22+
ports:
23+
- name: http
24+
containerPort: 80
25+
protocol: TCP
26+
- name: https
27+
containerPort: 443
28+
protocol: TCP
29+
- containerPort: 9443
30+
name: webhook-server
31+
protocol: TCP

test/output/1000/mychart.config

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
logger: {}
2+
chartname: mychart
3+
sharedValues:
4+
affinity: {}
5+
arg0:
6+
enabled: false
7+
http:
8+
enabled: true
9+
https:
10+
enabled: true
11+
webhook:
12+
disabled: false
13+
tls:
14+
enabled: true
15+
nodeSelector: {}
16+
podSecurityContext: {}
17+
ports:
18+
- containerPort: 6666
19+
name: some_other_port
20+
protocol: TCP
21+
resources: {}
22+
securityContext: {}
23+
tolerations: {}
24+
globalConfig:
25+
metadata.labels:
26+
- strategy: newline
27+
key: mychart.labels
28+
metadata.name:
29+
- strategy: inline
30+
key: mychart.fullname
31+
fileConfig:
32+
test/output/1000/mychart-generated/nginx-deployment-deployment.yaml:
33+
spec.template.spec.containers[0].args[0]:
34+
- strategy: control-if
35+
condition: sharedValues.arg0.enabled
36+
spec.template.spec.containers[0].ports[0]:
37+
- strategy: control-if
38+
condition: sharedValues.http.enabled
39+
spec.template.spec.containers[0].ports[2]:
40+
- strategy: control-if
41+
conditions:
42+
- key: "!sharedValues.webhook.disabled"
43+
- key: sharedValues.tls.enabled
44+
conditionOperator: and
45+
spec.template.spec.containers[0].ports:
46+
- strategy: append-with
47+
key: sharedValues.ports

0 commit comments

Comments
 (0)