Skip to content

Commit 879cf5b

Browse files
committed
Label release resources with HelmRelease origin
This commit adds a new post renderer that labels all resources with `helm.toolkit.fluxcd.io/name` and `helm.toolkit.fluxcd.io/namespace` so their source of origin can be traced back by e.g. the Flux UI. The post renderer makes use of the Kustomize API without running a full Kustomize build, by making directly use of the builtin `LabelTransformerPlugin` on a `ResMap` that has been constructed from the bytes of the `bytes.Buffer` given by Helm. Signed-off-by: Hidde Beydals <hello@hidde.co>
1 parent 22027ca commit 879cf5b

File tree

3 files changed

+160
-0
lines changed

3 files changed

+160
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
Copyright 2021 The Flux authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package runner
18+
19+
import (
20+
"bytes"
21+
"fmt"
22+
23+
"sigs.k8s.io/kustomize/api/builtins"
24+
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
25+
"sigs.k8s.io/kustomize/api/resmap"
26+
"sigs.k8s.io/kustomize/api/resource"
27+
kustypes "sigs.k8s.io/kustomize/api/types"
28+
29+
v2 "github.com/fluxcd/helm-controller/api/v2beta1"
30+
)
31+
32+
func newPostRendererOriginLabels(release *v2.HelmRelease) *postRendererOriginLabels {
33+
return &postRendererOriginLabels{
34+
name: release.ObjectMeta.Name,
35+
namespace: release.ObjectMeta.Namespace,
36+
}
37+
}
38+
39+
type postRendererOriginLabels struct {
40+
name string
41+
namespace string
42+
}
43+
44+
func (k *postRendererOriginLabels) Run(renderedManifests *bytes.Buffer) (modifiedManifests *bytes.Buffer, err error) {
45+
resFactory := resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())
46+
resMapFactory := resmap.NewFactory(resFactory, nil)
47+
48+
resMap, err := resMapFactory.NewResMapFromBytes(renderedManifests.Bytes())
49+
if err != nil {
50+
return nil, err
51+
}
52+
53+
labelTransformer := builtins.LabelTransformerPlugin{
54+
Labels: originLabels(k.name, k.namespace),
55+
FieldSpecs: []kustypes.FieldSpec{
56+
{Path: "metadata/labels", CreateIfNotPresent: true},
57+
},
58+
}
59+
if err := labelTransformer.Transform(resMap); err != nil {
60+
return nil, err
61+
}
62+
63+
yaml, err := resMap.AsYaml()
64+
if err != nil {
65+
return nil, err
66+
}
67+
68+
return bytes.NewBuffer(yaml), nil
69+
}
70+
71+
func originLabels(name, namespace string) map[string]string {
72+
return map[string]string{
73+
fmt.Sprintf("%s/name", v2.GroupVersion.Group): name,
74+
fmt.Sprintf("%s/namespace", v2.GroupVersion.Group): namespace,
75+
}
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
Copyright 2021 The Flux authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package runner
18+
19+
import (
20+
"bytes"
21+
"reflect"
22+
"testing"
23+
)
24+
25+
const mixedResourceMock = `apiVersion: v1
26+
kind: Pod
27+
metadata:
28+
name: pod-without-labels
29+
---
30+
apiVersion: v1
31+
kind: Service
32+
metadata:
33+
name: service-with-labels
34+
labels:
35+
existing: label
36+
`
37+
38+
func Test_postRendererOriginLabels_Run(t *testing.T) {
39+
tests := []struct {
40+
name string
41+
renderedManifests string
42+
expectManifests string
43+
expectErr bool
44+
}{
45+
{
46+
name: "labels",
47+
renderedManifests: mixedResourceMock,
48+
expectManifests: `apiVersion: v1
49+
kind: Pod
50+
metadata:
51+
labels:
52+
helm.toolkit.fluxcd.io/name: name
53+
helm.toolkit.fluxcd.io/namespace: namespace
54+
name: pod-without-labels
55+
---
56+
apiVersion: v1
57+
kind: Service
58+
metadata:
59+
labels:
60+
existing: label
61+
helm.toolkit.fluxcd.io/name: name
62+
helm.toolkit.fluxcd.io/namespace: namespace
63+
name: service-with-labels
64+
`,
65+
},
66+
}
67+
for _, tt := range tests {
68+
t.Run(tt.name, func(t *testing.T) {
69+
k := &postRendererOriginLabels{
70+
name: "name",
71+
namespace: "namespace",
72+
}
73+
gotModifiedManifests, err := k.Run(bytes.NewBufferString(tt.renderedManifests))
74+
if (err != nil) != tt.expectErr {
75+
t.Errorf("Run() error = %v, expectErr %v", err, tt.expectErr)
76+
return
77+
}
78+
if !reflect.DeepEqual(gotModifiedManifests, bytes.NewBufferString(tt.expectManifests)) {
79+
t.Errorf("Run() gotModifiedManifests = %v, want %v", gotModifiedManifests, tt.expectManifests)
80+
}
81+
})
82+
}
83+
}

internal/runner/runner.go

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ func postRenderers(hr v2.HelmRelease) (postrender.PostRenderer, error) {
5858
combinedRenderer.addRenderer(newPostRendererKustomize(r.Kustomize))
5959
}
6060
}
61+
combinedRenderer.addRenderer(newPostRendererOriginLabels(&hr))
6162
if len(combinedRenderer.renderers) == 0 {
6263
return nil, nil
6364
}

0 commit comments

Comments
 (0)