Skip to content

Commit 418bc3e

Browse files
authored
Merge pull request #58 from weaveworks/docs-for-cluster-generator
Documentation and fixes.
2 parents 15014e8 + 47a0948 commit 418bc3e

4 files changed

Lines changed: 142 additions & 29 deletions

File tree

controllers/gitopsset_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func (r *GitOpsSetReconciler) event(obj *templatesv1.GitOpsSet, severity, msg st
8080
r.EventRecorder.Event(obj, eventtype, reason, msg)
8181
}
8282

83-
//+kubebuilder:rbac:groups=templa3tes.weave.works,resources=gitopssets,verbs=get;list;watch;create;update;patch;delete
83+
//+kubebuilder:rbac:groups=templates.weave.works,resources=gitopssets,verbs=get;list;watch;create;update;patch;delete
8484
//+kubebuilder:rbac:groups=templates.weave.works,resources=gitopssets/status,verbs=get;update;patch
8585
//+kubebuilder:rbac:groups=templates.weave.works,resources=gitopssets/finalizers,verbs=update
8686
//+kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=gitrepositories,verbs=get;list;watch

controllers/templates/generators/cluster/cluster.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,39 +42,41 @@ func (g *ClusterGenerator) Generate(ctx context.Context, sg *templatesv1.GitOpsS
4242
}
4343
g.Logger.Info("generating params from Cluster generator")
4444

45-
var paramsList []map[string]any
46-
4745
selector, err := metav1.LabelSelectorAsSelector(&sg.Cluster.Selector)
4846
if err != nil {
4947
return nil, fmt.Errorf("unable to convert selector: %w", err)
5048
}
5149

52-
if selector.Empty() {
53-
g.Logger.Info("empty GitOpsSetGenerator cluster selector: no clusters are selected")
54-
return nil, nil
55-
}
56-
57-
clusterList := clustersv1.GitopsClusterList{}
5850
listOptions := client.ListOptions{LabelSelector: selector}
5951

52+
clusterList := clustersv1.GitopsClusterList{}
6053
err = g.Client.List(ctx, &clusterList, &listOptions)
6154
if err != nil {
6255
return nil, err
6356
}
6457

58+
var paramsList []map[string]any
6559
for _, cluster := range clusterList.Items {
6660
params := map[string]any{
6761
"ClusterName": cluster.Name,
6862
"ClusterNamespace": cluster.Namespace,
69-
"ClusterLabels": cluster.Labels,
70-
"ClusterAnnotations": cluster.Annotations,
63+
"ClusterLabels": mapOrEmptyMap(cluster.Labels),
64+
"ClusterAnnotations": mapOrEmptyMap(cluster.Annotations),
7165
}
7266
paramsList = append(paramsList, params)
7367
}
7468

7569
return paramsList, nil
7670
}
7771

72+
func mapOrEmptyMap(src map[string]string) map[string]string {
73+
if src == nil {
74+
return map[string]string{}
75+
}
76+
77+
return src
78+
}
79+
7880
// Interval is an implementation of the Generator interface.
7981
func (g *ClusterGenerator) Interval(sg *templatesv1.GitOpsSetGenerator) time.Duration {
8082
return generators.NoRequeueInterval

controllers/templates/generators/cluster/cluster_test.go

Lines changed: 59 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package cluster
33
import (
44
"context"
55
"testing"
6-
"time"
76

87
"github.com/go-logr/logr"
98
"github.com/stretchr/testify/assert"
@@ -23,13 +22,11 @@ func TestClusterGenerator_Generate(t *testing.T) {
2322
sg *templatesv1.GitOpsSetGenerator
2423
clusters []runtime.Object
2524
wantParams []map[string]any
26-
wantErr bool
2725
errContains string
2826
}{
2927
{
3028
name: "return error if sg is nil",
3129
sg: nil,
32-
wantErr: true,
3330
errContains: generators.ErrEmptyGitOpsSet.Error(),
3431
},
3532
{
@@ -38,10 +35,58 @@ func TestClusterGenerator_Generate(t *testing.T) {
3835
wantParams: nil,
3936
},
4037
{
41-
name: "successful clusters listing",
38+
name: "generator with no label selector returns all clusters",
39+
sg: &templatesv1.GitOpsSetGenerator{
40+
Cluster: &templatesv1.ClusterGenerator{},
41+
},
42+
clusters: []runtime.Object{
43+
&clustersv1.GitopsCluster{
44+
ObjectMeta: metav1.ObjectMeta{
45+
Name: "cluster1",
46+
Namespace: "ns1",
47+
Annotations: map[string]string{},
48+
Labels: map[string]string{"test1": "value"},
49+
},
50+
Spec: clustersv1.GitopsClusterSpec{},
51+
},
52+
&clustersv1.GitopsCluster{
53+
ObjectMeta: metav1.ObjectMeta{
54+
Name: "cluster2",
55+
Namespace: "ns2",
56+
Annotations: map[string]string{},
57+
Labels: map[string]string{"test2": "value"},
58+
},
59+
Spec: clustersv1.GitopsClusterSpec{},
60+
},
61+
},
62+
wantParams: []map[string]any{
63+
{
64+
"ClusterAnnotations": map[string]string{},
65+
"ClusterLabels": map[string]string{
66+
"test1": "value",
67+
},
68+
"ClusterName": "cluster1",
69+
"ClusterNamespace": "ns1",
70+
},
71+
{
72+
"ClusterAnnotations": map[string]string{},
73+
"ClusterLabels": map[string]string{
74+
"test2": "value",
75+
},
76+
"ClusterName": "cluster2",
77+
"ClusterNamespace": "ns2",
78+
},
79+
},
80+
},
81+
{
82+
name: "label selector filtering",
4283
sg: &templatesv1.GitOpsSetGenerator{
4384
Cluster: &templatesv1.ClusterGenerator{
44-
Selector: *metav1.AddLabelToSelector(&metav1.LabelSelector{}, "foo", "bar"),
85+
Selector: metav1.LabelSelector{
86+
MatchLabels: map[string]string{
87+
"foo": "bar",
88+
},
89+
},
4590
},
4691
},
4792
clusters: []runtime.Object{
@@ -76,15 +121,15 @@ func TestClusterGenerator_Generate(t *testing.T) {
76121
t.Run(tt.name, func(t *testing.T) {
77122
c := newFakeClient(t, tt.clusters...)
78123
g := NewGenerator(logr.Discard(), c)
79-
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
80-
defer cancel()
81-
gotParams, err := g.Generate(ctx, tt.sg, nil)
82-
if tt.wantErr {
83-
assert.Error(t, err)
124+
125+
gotParams, err := g.Generate(context.TODO(), tt.sg, nil)
126+
127+
if tt.errContains != "" {
84128
assert.Contains(t, err.Error(), tt.errContains)
85-
} else {
86-
assert.NoError(t, err)
129+
return
87130
}
131+
132+
assert.NoError(t, err)
88133
assert.Equal(t, tt.wantParams, gotParams)
89134
})
90135
}
@@ -93,12 +138,8 @@ func TestClusterGenerator_Generate(t *testing.T) {
93138
func newFakeClient(t *testing.T, objs ...runtime.Object) client.WithWatch {
94139
t.Helper()
95140
scheme := runtime.NewScheme()
96-
if err := clustersv1.AddToScheme(scheme); err != nil {
97-
t.Fatal(err)
98-
}
99-
if err := templatesv1.AddToScheme(scheme); err != nil {
100-
t.Fatal(err)
101-
}
141+
assert.NoError(t, clustersv1.AddToScheme(scheme))
142+
assert.NoError(t, templatesv1.AddToScheme(scheme))
102143

103144
return fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(objs...).Build()
104145
}

docs/README.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ We currently provide these generators:
136136
- [gitRepository](#gitrepository-generator)
137137
- [matrix](#matrix-generator)
138138
- [apiClient](#apiclient-generator)
139+
- [cluster](#cluster-generator)
139140

140141
### List generator
141142

@@ -627,6 +628,75 @@ Whatever result is parsed from the API endpoint will be returned as a map in a s
627628

628629
For generation, you might need to use the `repeat` mechanism to generate repeating results.
629630

631+
### Cluster generator
632+
633+
The cluster generator generates from in-cluster GitOpsCluster resources.
634+
635+
For example, this `GitOpsSet` will generate a `Kustomization` resource for each cluster matching the [Label selector](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/).
636+
637+
```yaml
638+
apiVersion: templates.weave.works/v1alpha1
639+
kind: GitOpsSet
640+
metadata:
641+
name: cluster-sample
642+
spec:
643+
generators:
644+
- cluster:
645+
selector:
646+
matchLabels:
647+
env: dev
648+
team: dev-team
649+
templates:
650+
- content:
651+
kind: Kustomization
652+
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
653+
metadata:
654+
name: "{{ .Element.ClusterName }}-demo"
655+
labels:
656+
app.kubernetes.io/name: go-demo
657+
app.kubernetes.io/instance: "{{ .Element.ClusterName }}"
658+
com.example/team: "{{ .Element.ClusterLabels.team }}"
659+
spec:
660+
interval: 5m
661+
path: "./examples/kustomize/environments/{{ .Element.ClusterLabels.env }}"
662+
prune: true
663+
sourceRef:
664+
kind: GitRepository
665+
name: go-demo-repo
666+
```
667+
668+
The following fields are generated for each GitOpsCluster.
669+
670+
- `ClusterName` the name of the cluster
671+
- `ClusterNamespace` the namespace that this cluster is from
672+
- `ClusterLabels` the labels from the metadata field on the GitOpsCluster
673+
- `ClusterAnnotations` the annotations from the metadata field on the GitOpsCluster
674+
675+
If the selector is not provided, all clusters from all namespaces will be returned:
676+
677+
```yaml
678+
apiVersion: templates.weave.works/v1alpha1
679+
kind: GitOpsSet
680+
metadata:
681+
name: cluster-sample
682+
spec:
683+
generators:
684+
- cluster: {}
685+
```
686+
687+
Otherwise if the selector is empty, no clusters will be generated:
688+
689+
```yaml
690+
apiVersion: templates.weave.works/v1alpha1
691+
kind: GitOpsSet
692+
metadata:
693+
name: cluster-sample
694+
spec:
695+
generators:
696+
- cluster:
697+
selector: {}
698+
```
699+
630700
## Templating functions
631701

632702
Currently, the [Sprig](http://masterminds.github.io/sprig/) functions are available in the templating, with some functions removed[^sprig] for security reasons.

0 commit comments

Comments
 (0)