Skip to content

Commit fccf977

Browse files
authored
Merge pull request #85 from weaveworks/config-generator
Config generator.
2 parents 50b6bca + c648c9c commit fccf977

File tree

16 files changed

+928
-119
lines changed

16 files changed

+928
-119
lines changed

api/v1alpha1/gitopsset_types.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@ type ClusterGenerator struct {
3232
Selector metav1.LabelSelector `json:"selector,omitempty"`
3333
}
3434

35+
// ConfigGenerator loads a referenced ConfigMap or
36+
// Secret from the Cluster and makes it available as a resource.
37+
type ConfigGenerator struct {
38+
// Kind of the referent.
39+
// +kubebuilder:validation:Enum=ConfigMap;Secret
40+
// +required
41+
Kind string `json:"kind"`
42+
43+
// Name of the referent.
44+
// +required
45+
Name string `json:"name"`
46+
}
47+
3548
// ListGenerator generates from a hard-coded list.
3649
type ListGenerator struct {
3750
Elements []apiextensionsv1.JSON `json:"elements,omitempty"`
@@ -180,6 +193,7 @@ type GitOpsSetNestedGenerator struct {
180193
Cluster *ClusterGenerator `json:"cluster,omitempty"`
181194
APIClient *APIClientGenerator `json:"apiClient,omitempty"`
182195
ImagePolicy *ImagePolicyGenerator `json:"imagePolicy,omitempty"`
196+
Config *ConfigGenerator `json:"config,omitempty"`
183197
}
184198

185199
// ImagePolicyGenerator generates from the ImagePolicy.
@@ -197,6 +211,7 @@ type GitOpsSetGenerator struct {
197211
Cluster *ClusterGenerator `json:"cluster,omitempty"`
198212
APIClient *APIClientGenerator `json:"apiClient,omitempty"`
199213
ImagePolicy *ImagePolicyGenerator `json:"imagePolicy,omitempty"`
214+
Config *ConfigGenerator `json:"config,omitempty"`
200215
}
201216

202217
// GitOpsSetSpec defines the desired state of GitOpsSet

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/templates.weave.works_gitopssets.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,23 @@ spec:
166166
type: object
167167
x-kubernetes-map-type: atomic
168168
type: object
169+
config:
170+
description: ConfigGenerator loads a referenced ConfigMap or
171+
Secret from the Cluster and makes it available as a resource.
172+
properties:
173+
kind:
174+
description: Kind of the referent.
175+
enum:
176+
- ConfigMap
177+
- Secret
178+
type: string
179+
name:
180+
description: Name of the referent.
181+
type: string
182+
required:
183+
- kind
184+
- name
185+
type: object
169186
gitRepository:
170187
description: GitRepositoryGenerator generates from files in
171188
a Flux GitRepository resource.
@@ -357,6 +374,24 @@ spec:
357374
type: object
358375
x-kubernetes-map-type: atomic
359376
type: object
377+
config:
378+
description: ConfigGenerator loads a referenced ConfigMap
379+
or Secret from the Cluster and makes it available
380+
as a resource.
381+
properties:
382+
kind:
383+
description: Kind of the referent.
384+
enum:
385+
- ConfigMap
386+
- Secret
387+
type: string
388+
name:
389+
description: Name of the referent.
390+
type: string
391+
required:
392+
- kind
393+
- name
394+
type: object
360395
gitRepository:
361396
description: GitRepositoryGenerator generates from
362397
files in a Flux GitRepository resource.

controllers/gitopsset_controller.go

Lines changed: 74 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ import (
1515
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
1616
"github.com/gitops-tools/pkg/sets"
1717
"github.com/go-logr/logr"
18+
corev1 "k8s.io/api/core/v1"
1819
apierrors "k8s.io/apimachinery/pkg/api/errors"
1920
"k8s.io/apimachinery/pkg/api/meta"
2021
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2122
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2223
"k8s.io/apimachinery/pkg/labels"
2324
"k8s.io/apimachinery/pkg/runtime"
24-
"k8s.io/apimachinery/pkg/types"
2525
"k8s.io/client-go/rest"
2626
"sigs.k8s.io/cli-utils/pkg/object"
2727
ctrl "sigs.k8s.io/controller-runtime"
@@ -45,6 +45,8 @@ var accessor = meta.NewAccessor()
4545
const (
4646
gitRepositoryIndexKey string = ".metadata.gitRepository"
4747
imagePolicyIndexKey string = ".metadata.imagePolicy"
48+
configMapIndexKey string = ".metadata.configMap"
49+
secretIndexKey string = ".metadata.secret"
4850
)
4951

5052
type eventRecorder interface {
@@ -308,12 +310,30 @@ func (r *GitOpsSetReconciler) SetupWithManager(mgr ctrl.Manager) error {
308310
return fmt.Errorf("failed setting index fields: %w", err)
309311
}
310312

313+
if err := mgr.GetCache().IndexField(
314+
context.TODO(), &templatesv1.GitOpsSet{}, configMapIndexKey, indexConfig("ConfigMap")); err != nil {
315+
return fmt.Errorf("failed setting index fields: %w", err)
316+
}
317+
318+
if err := mgr.GetCache().IndexField(
319+
context.TODO(), &templatesv1.GitOpsSet{}, secretIndexKey, indexConfig("Secret")); err != nil {
320+
return fmt.Errorf("failed setting index fields: %w", err)
321+
}
322+
311323
builder := ctrl.NewControllerManagedBy(mgr).
312324
For(&templatesv1.GitOpsSet{}, builder.WithPredicates(
313325
predicate.Or(predicate.GenerationChangedPredicate{}, predicates.ReconcileRequestedPredicate{}))).
314326
Watches(
315327
&source.Kind{Type: &sourcev1.GitRepository{}},
316328
handler.EnqueueRequestsFromMapFunc(r.gitRepositoryToGitOpsSet),
329+
).
330+
Watches(
331+
&source.Kind{Type: &corev1.ConfigMap{}},
332+
handler.EnqueueRequestsFromMapFunc(r.configMapToGitOpsSet),
333+
).
334+
Watches(
335+
&source.Kind{Type: &corev1.Secret{}},
336+
handler.EnqueueRequestsFromMapFunc(r.secretToGitOpsSet),
317337
)
318338

319339
// Only watch for GitopsCluster objects if the Cluster generator is enabled.
@@ -360,7 +380,7 @@ func (r *GitOpsSetReconciler) gitOpsClusterToGitOpsSet(o client.Object) []reconc
360380
var result []reconcile.Request
361381
for _, v := range list.Items {
362382
if matchCluster(gitOpsCluster, &v) {
363-
result = append(result, reconcile.Request{NamespacedName: types.NamespacedName{Name: v.GetName(), Namespace: v.GetNamespace()}})
383+
result = append(result, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(&v)})
364384
}
365385
}
366386

@@ -439,41 +459,39 @@ func selectorMatchesCluster(labelSelector metav1.LabelSelector, cluster *cluster
439459
func (r *GitOpsSetReconciler) gitRepositoryToGitOpsSet(obj client.Object) []reconcile.Request {
440460
// TODO: Store the applied version of GitRepositories in the Status, and don't
441461
// retrigger if the commit-id isn't different.
442-
ctx := context.Background()
443-
var list templatesv1.GitOpsSetList
444-
445-
if err := r.List(ctx, &list, client.MatchingFields{
446-
gitRepositoryIndexKey: client.ObjectKeyFromObject(obj).String(),
447-
}); err != nil {
448-
return nil
449-
}
450-
451-
result := []reconcile.Request{}
452-
for _, v := range list.Items {
453-
result = append(result, reconcile.Request{NamespacedName: types.NamespacedName{Name: v.GetName(), Namespace: v.GetNamespace()}})
454-
}
455-
456-
return result
462+
return r.queryIndexedGitOpsSets(gitRepositoryIndexKey, obj)
457463
}
458464

459465
func (r *GitOpsSetReconciler) imagePolicyToGitOpsSet(obj client.Object) []reconcile.Request {
466+
return r.queryIndexedGitOpsSets(imagePolicyIndexKey, obj)
467+
}
468+
469+
func (r *GitOpsSetReconciler) queryIndexedGitOpsSets(key string, obj client.Object) []reconcile.Request {
460470
ctx := context.Background()
461471
var list templatesv1.GitOpsSetList
462472

463-
if err := r.List(ctx, &list, client.MatchingFields{
464-
imagePolicyIndexKey: client.ObjectKeyFromObject(obj).String(),
465-
}); err != nil {
473+
if err := r.List(ctx, &list,
474+
client.MatchingFields{key: client.ObjectKeyFromObject(obj).String()},
475+
client.InNamespace(obj.GetNamespace())); err != nil {
466476
return nil
467477
}
468478

469479
result := []reconcile.Request{}
470-
for _, v := range list.Items {
471-
result = append(result, reconcile.Request{NamespacedName: types.NamespacedName{Name: v.GetName(), Namespace: v.GetNamespace()}})
480+
for i := range list.Items {
481+
result = append(result, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(&list.Items[i])})
472482
}
473483

474484
return result
475485
}
476486

487+
func (r *GitOpsSetReconciler) configMapToGitOpsSet(obj client.Object) []reconcile.Request {
488+
return r.queryIndexedGitOpsSets(configMapIndexKey, obj)
489+
}
490+
491+
func (r *GitOpsSetReconciler) secretToGitOpsSet(obj client.Object) []reconcile.Request {
492+
return r.queryIndexedGitOpsSets(secretIndexKey, obj)
493+
}
494+
477495
func (r *GitOpsSetReconciler) makeImpersonationClient(namespace, serviceAccountName string) (client.Client, error) {
478496
copyCfg := rest.CopyConfig(r.Config)
479497

@@ -516,6 +534,40 @@ func indexGitRepositories(o client.Object) []string {
516534
return referencedNames
517535
}
518536

537+
func indexConfig(kind string) func(o client.Object) []string {
538+
return func(o client.Object) []string {
539+
ks, ok := o.(*templatesv1.GitOpsSet)
540+
if !ok {
541+
panic(fmt.Sprintf("Expected a GitOpsSet, got %T", o))
542+
}
543+
544+
referencedResources := []*templatesv1.ConfigGenerator{}
545+
for _, gen := range ks.Spec.Generators {
546+
if gen.Config != nil && gen.Config.Kind == kind {
547+
referencedResources = append(referencedResources, gen.Config)
548+
}
549+
if gen.Matrix != nil && gen.Matrix.Generators != nil {
550+
for _, matrixGen := range gen.Matrix.Generators {
551+
if matrixGen.Config != nil && matrixGen.Config.Kind == kind {
552+
referencedResources = append(referencedResources, matrixGen.Config)
553+
}
554+
}
555+
}
556+
}
557+
558+
if len(referencedResources) == 0 {
559+
return nil
560+
}
561+
562+
referencedNames := []string{}
563+
for _, grg := range referencedResources {
564+
referencedNames = append(referencedNames, fmt.Sprintf("%s/%s", ks.GetNamespace(), grg.Name))
565+
}
566+
567+
return referencedNames
568+
}
569+
}
570+
519571
func indexImagePolicies(o client.Object) []string {
520572
ks, ok := o.(*templatesv1.GitOpsSet)
521573
if !ok {
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package config
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"time"
7+
8+
"github.com/go-logr/logr"
9+
templatesv1 "github.com/weaveworks/gitopssets-controller/api/v1alpha1"
10+
"github.com/weaveworks/gitopssets-controller/controllers/templates/generators"
11+
corev1 "k8s.io/api/core/v1"
12+
"sigs.k8s.io/controller-runtime/pkg/client"
13+
)
14+
15+
// ConfigGenerator generates a single resource from a referenced ConfigMap or
16+
// Secret.
17+
type ConfigGenerator struct {
18+
client.Client
19+
logr.Logger
20+
}
21+
22+
// GeneratorFactory is a function for creating per-reconciliation generators for
23+
// the ConfigGenerator.
24+
func GeneratorFactory(l logr.Logger, c client.Client) generators.Generator {
25+
return NewGenerator(l, c)
26+
}
27+
28+
// NewGenerator creates and returns a new config generator.
29+
func NewGenerator(l logr.Logger, c client.Client) *ConfigGenerator {
30+
return &ConfigGenerator{
31+
Client: c,
32+
Logger: l,
33+
}
34+
}
35+
36+
func (g *ConfigGenerator) Generate(ctx context.Context, sg *templatesv1.GitOpsSetGenerator, ks *templatesv1.GitOpsSet) ([]map[string]any, error) {
37+
if sg == nil {
38+
return nil, generators.ErrEmptyGitOpsSet
39+
}
40+
41+
if sg.Config == nil {
42+
return nil, nil
43+
}
44+
g.Logger.Info("generating params from Config generator")
45+
46+
var paramsList []map[string]any
47+
48+
switch sg.Config.Kind {
49+
case "ConfigMap":
50+
data, err := configMapToParams(ctx, g.Client, client.ObjectKey{Name: sg.Config.Name, Namespace: ks.GetNamespace()})
51+
if err != nil {
52+
return nil, err
53+
}
54+
paramsList = append(paramsList, data)
55+
56+
case "Secret":
57+
data, err := secretToParams(ctx, g.Client, client.ObjectKey{Name: sg.Config.Name, Namespace: ks.GetNamespace()})
58+
if err != nil {
59+
return nil, err
60+
}
61+
paramsList = append(paramsList, data)
62+
63+
default:
64+
return nil, fmt.Errorf("unknown Config Kind %q %q", sg.Config.Kind, sg.Config.Name)
65+
}
66+
67+
return paramsList, nil
68+
}
69+
70+
// Interval is an implementation of the Generator interface.
71+
func (g *ConfigGenerator) Interval(sg *templatesv1.GitOpsSetGenerator) time.Duration {
72+
return generators.NoRequeueInterval
73+
}
74+
75+
func configMapToParams(ctx context.Context, k8sClient client.Client, key client.ObjectKey) (map[string]any, error) {
76+
var configMap corev1.ConfigMap
77+
78+
if err := k8sClient.Get(ctx, key, &configMap); err != nil {
79+
return nil, err
80+
}
81+
82+
return mapToAnyMap(configMap.Data), nil
83+
}
84+
85+
func secretToParams(ctx context.Context, k8sClient client.Client, key client.ObjectKey) (map[string]any, error) {
86+
var secret corev1.Secret
87+
88+
if err := k8sClient.Get(ctx, key, &secret); err != nil {
89+
return nil, err
90+
}
91+
92+
return mapToAnyMap(secret.Data), nil
93+
}
94+
95+
func mapToAnyMap[V any](m map[string]V) map[string]any {
96+
result := map[string]any{}
97+
98+
for k, v := range m {
99+
result[k] = v
100+
}
101+
102+
return result
103+
}

0 commit comments

Comments
 (0)