Skip to content

Commit d995333

Browse files
committed
Standardize controller for GitOps Toolkit
Signed-off-by: Stefan Prodan <[email protected]>
1 parent e1c3c2f commit d995333

14 files changed

+302
-153
lines changed

.github/dependabot.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ updates:
44
- package-ecosystem: "gomod"
55
directory: "/"
66
schedule:
7-
interval: "daily"
7+
interval: "monthly"
88
groups:
99
flux-deps:
1010
patterns:
@@ -22,15 +22,15 @@ updates:
2222
- package-ecosystem: "github-actions"
2323
directory: "/"
2424
schedule:
25-
interval: "daily"
25+
interval: "monthly"
2626
groups:
2727
ci:
2828
patterns:
2929
- "*"
3030
- package-ecosystem: "docker"
3131
directory: "/"
3232
schedule:
33-
interval: "daily"
33+
interval: "monthly"
3434
groups:
3535
docker:
3636
patterns:

CONTRIBUTING.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ meeting](https://docs.google.com/document/d/1l_M0om0qUEN_NNiGgpqJ2tvsF2iioHkaARD
4040
### How to run the test suite
4141

4242
Prerequisites:
43-
* go >= 1.20
44-
* docker >= 20.10
45-
* kustomize >= 4.4
43+
* go >= 1.25
44+
* docker >= 28.3
45+
* kustomize >= 5.7
4646

4747
You can run the unit tests by simply doing
4848

README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
# source-watcher
22

3-
[![test](https://github.com/fluxcd/source-watcher/workflows/test/badge.svg)](https://github.com/fluxcd/source-watcher/actions)
3+
[![test](https://github.com/fluxcd/source-watcher/workflows/e2e/badge.svg)](https://github.com/fluxcd/source-watcher/actions)
4+
[![report](https://goreportcard.com/badge/github.com/fluxcd/source-watcher)](https://goreportcard.com/report/github.com/fluxcd/source-watcher)
5+
[![license](https://img.shields.io/github/license/fluxcd/source-watcher.svg)](https://github.com/fluxcd/source-watcher/blob/main/LICENSE)
6+
[![release](https://img.shields.io/github/release/fluxcd/source-watcher/all.svg)](https://github.com/fluxcd/source-watcher/releases)
47

5-
A Flux extension controller that enables advanced source composition and decomposition patterns for GitOps workflows.
8+
The source-watcher is a [GitOps toolkit](https://fluxcd.io/flux/components/) controller
9+
that extends Flux with advanced source composition and decomposition patterns.
610

711
## Overview
812

9-
source-watcher introduces the **ArtifactGenerator** API, which allows you to:
13+
The source-watcher controller implements the [ArtifactGenerator](docs/README.md) API,
14+
which allows Flux users to:
1015

1116
- 🔗 **Compose** multiple Flux sources (GitRepository, OCIRepository, Bucket) into a single deployable artifact
1217
- 📦 **Decompose** monorepos into multiple independent artifacts with separate deployment lifecycles
@@ -99,7 +104,7 @@ spec:
99104
```
100105
101106
Each service gets its own ExternalArtifact with an independent revision.
102-
Changes to `services/auth/` only trigger reconciliation of the auth-service,
107+
Changes to `services/auth/` only trigger the reconciliation of the auth-service,
103108
leaving other services untouched.
104109

105110
### Example: Helm Chart Composition
@@ -122,7 +127,7 @@ spec:
122127
name: podinfo-values
123128
artifacts:
124129
- name: podinfo-composite
125-
originRevision: "@chart" # Track chart version
130+
originRevision: "@chart"
126131
copy:
127132
- from: "@chart/"
128133
to: "@artifact/"
@@ -180,10 +185,6 @@ Copy operations support glob patterns, exclusions and YAML merge:
180185
- **Selective Deployment** - Deploy only changed components from large repositories
181186
by decomposing them into dedicated artifacts.
182187

183-
## API Reference
184-
185-
- [ArtifactGenerator CRD](docs/spec/v1beta1/artifactgenerators.md)
186-
187188
## Contributing
188189

189190
This project is Apache 2.0 licensed and accepts contributions via GitHub pull requests.

api/v1beta1/artifactgenerator_types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,10 @@ func (in *ArtifactGenerator) HasArtifactInInventory(name, namespace, digest stri
233233

234234
// +kubebuilder:object:root=true
235235
// +kubebuilder:subresource:status
236+
// +kubebuilder:resource:shortName=ag
237+
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
238+
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
239+
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""
236240

237241
// ArtifactGenerator is the Schema for the artifactgenerators API.
238242
type ArtifactGenerator struct {

cmd/main.go

Lines changed: 65 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,35 @@ limitations under the License.
1717
package main
1818

1919
import (
20+
"fmt"
2021
"os"
2122
"time"
2223

23-
"github.com/fluxcd/pkg/runtime/acl"
24-
"github.com/fluxcd/pkg/runtime/probes"
2524
flag "github.com/spf13/pflag"
2625
corev1 "k8s.io/api/core/v1"
2726
"k8s.io/apimachinery/pkg/runtime"
2827
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
2928
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
3029
_ "k8s.io/client-go/plugin/pkg/client/auth"
3130
"k8s.io/utils/ptr"
32-
ctrl "sigs.k8s.io/controller-runtime"
31+
ctrlruntime "sigs.k8s.io/controller-runtime"
3332
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
3433
ctrlcfg "sigs.k8s.io/controller-runtime/pkg/config"
3534
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
3635

37-
artcfg "github.com/fluxcd/pkg/artifact/config"
38-
artdigest "github.com/fluxcd/pkg/artifact/digest"
39-
artsrv "github.com/fluxcd/pkg/artifact/server"
40-
artstore "github.com/fluxcd/pkg/artifact/storage"
36+
"github.com/fluxcd/pkg/artifact/config"
37+
"github.com/fluxcd/pkg/artifact/digest"
38+
"github.com/fluxcd/pkg/artifact/server"
39+
"github.com/fluxcd/pkg/artifact/storage"
40+
"github.com/fluxcd/pkg/runtime/acl"
4141
"github.com/fluxcd/pkg/runtime/client"
42+
ctrl "github.com/fluxcd/pkg/runtime/controller"
43+
"github.com/fluxcd/pkg/runtime/features"
44+
"github.com/fluxcd/pkg/runtime/jitter"
45+
"github.com/fluxcd/pkg/runtime/leaderelection"
4246
"github.com/fluxcd/pkg/runtime/logger"
4347
"github.com/fluxcd/pkg/runtime/pprof"
48+
"github.com/fluxcd/pkg/runtime/probes"
4449
sourcev1 "github.com/fluxcd/source-controller/api/v1"
4550

4651
swapi "github.com/fluxcd/source-watcher/api/v1beta1"
@@ -50,7 +55,7 @@ import (
5055

5156
var (
5257
scheme = runtime.NewScheme()
53-
setupLog = ctrl.Log.WithName("setup")
58+
setupLog = ctrlruntime.Log.WithName("setup")
5459
)
5560

5661
func init() {
@@ -64,66 +69,83 @@ func main() {
6469
const controllerName = "source-watcher"
6570

6671
var (
67-
metricsAddr string
68-
healthAddr string
69-
enableLeaderElection bool
70-
concurrent int
71-
httpRetry int
72-
requeueDependency time.Duration
73-
artifactOptions artcfg.Options
74-
aclOptions acl.Options
75-
clientOptions client.Options
76-
logOptions logger.Options
72+
metricsAddr string
73+
healthAddr string
74+
concurrent int
75+
httpRetry int
76+
reconciliationTimeout time.Duration
77+
requeueDependency time.Duration
78+
artifactOptions config.Options
79+
aclOptions acl.Options
80+
clientOptions client.Options
81+
logOptions logger.Options
82+
leaderElectionOptions leaderelection.Options
83+
rateLimiterOptions ctrl.RateLimiterOptions
84+
intervalJitterOptions jitter.IntervalOptions
85+
featureGates features.FeatureGates
7786
)
7887

7988
flag.IntVar(&concurrent, "concurrent", 10, "The number of concurrent resource reconciles.")
8089
flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
8190
flag.StringVar(&healthAddr, "health-addr", ":9440", "The address the health endpoint binds to.")
82-
flag.BoolVar(&enableLeaderElection, "enable-leader-election", false,
83-
"Enable leader election for controller manager. "+
84-
"Enabling this will ensure there is only one active controller manager.")
8591
flag.IntVar(&httpRetry, "http-retry", 9,
8692
"The maximum number of retries when failing to fetch artifacts over HTTP.")
93+
flag.DurationVar(&reconciliationTimeout, "reconciliation-timeout", 10*time.Minute,
94+
"The maximum duration of a reconciliation.")
8795
flag.DurationVar(&requeueDependency, "requeue-dependency", 5*time.Second,
8896
"The interval at which failing dependencies are reevaluated.")
8997

9098
artifactOptions.BindFlags(flag.CommandLine)
9199
aclOptions.BindFlags(flag.CommandLine)
92100
clientOptions.BindFlags(flag.CommandLine)
93101
logOptions.BindFlags(flag.CommandLine)
102+
leaderElectionOptions.BindFlags(flag.CommandLine)
103+
rateLimiterOptions.BindFlags(flag.CommandLine)
104+
intervalJitterOptions.BindFlags(flag.CommandLine)
105+
featureGates.BindFlags(flag.CommandLine)
94106

95107
flag.Parse()
96108

97-
ctrl.SetLogger(logger.NewLogger(logOptions))
109+
ctrlruntime.SetLogger(logger.NewLogger(logOptions))
98110

99-
algo, err := artdigest.AlgorithmForName(artifactOptions.ArtifactDigestAlgo)
111+
digestAlgo, err := digest.AlgorithmForName(artifactOptions.ArtifactDigestAlgo)
100112
if err != nil {
101113
setupLog.Error(err, "unable to configure canonical digest algorithm")
102114
os.Exit(1)
103115
}
104-
artdigest.Canonical = algo
116+
digest.Canonical = digestAlgo
105117

106-
storage, err := artstore.New(&artifactOptions)
118+
artifactStorage, err := storage.New(&artifactOptions)
107119
if err != nil {
108120
setupLog.Error(err, "unable to configure artifact storage")
109121
os.Exit(1)
110122
}
111-
setupLog.Info("storage setup for " + storage.BasePath)
123+
setupLog.Info("storage setup for " + artifactStorage.BasePath)
112124

113-
ctx := ctrl.SetupSignalHandler()
114-
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
125+
if err := intervalJitterOptions.SetGlobalJitter(nil); err != nil {
126+
setupLog.Error(err, "unable to set global jitter")
127+
os.Exit(1)
128+
}
129+
130+
ctx := ctrlruntime.SetupSignalHandler()
131+
leaderElectionID := fmt.Sprintf("%s-%s", controllerName, "leader-election")
132+
mgr, err := ctrlruntime.NewManager(ctrlruntime.GetConfigOrDie(), ctrlruntime.Options{
115133
Scheme: scheme,
116134
Metrics: metricsserver.Options{
117135
BindAddress: metricsAddr,
118136
ExtraHandlers: pprof.GetHandlers(),
119137
},
120138
HealthProbeBindAddress: healthAddr,
121-
LeaderElection: enableLeaderElection,
122-
LeaderElectionID: controllerName,
123-
LeaderElectionReleaseOnCancel: true,
139+
LeaderElection: leaderElectionOptions.Enable,
140+
LeaderElectionID: leaderElectionID,
141+
LeaderElectionReleaseOnCancel: leaderElectionOptions.ReleaseOnCancel,
142+
LeaseDuration: &leaderElectionOptions.LeaseDuration,
143+
RenewDeadline: &leaderElectionOptions.RenewDeadline,
144+
Logger: ctrlruntime.Log,
124145
Controller: ctrlcfg.Controller{
125146
MaxConcurrentReconciles: concurrent,
126147
RecoverPanic: ptr.To(true),
148+
ReconciliationTimeout: reconciliationTimeout,
127149
},
128150
Client: ctrlclient.Options{
129151
Cache: &ctrlclient.CacheOptions{
@@ -136,38 +158,44 @@ func main() {
136158
os.Exit(1)
137159
}
138160

161+
// Note that the liveness check will pass beyond this point, but the readiness
162+
// check will continue to fail until this controller instance is elected leader.
163+
probes.SetupChecks(mgr, setupLog)
164+
139165
if err = (&controller.ArtifactGeneratorReconciler{
140166
ControllerName: controllerName,
141167
Client: mgr.GetClient(),
142168
APIReader: mgr.GetAPIReader(),
143169
Scheme: mgr.GetScheme(),
144170
EventRecorder: mgr.GetEventRecorderFor(controllerName),
145-
Storage: storage,
171+
Storage: artifactStorage,
146172
ArtifactFetchRetries: httpRetry,
147173
DependencyRequeueInterval: requeueDependency,
148174
NoCrossNamespaceRefs: aclOptions.NoCrossNamespaceRefs,
149-
}).SetupWithManager(ctx, mgr); err != nil {
175+
}).SetupWithManager(ctx, mgr, controller.ArtifactGeneratorReconcilerOptions{
176+
RateLimiter: ctrl.GetRateLimiter(rateLimiterOptions),
177+
}); err != nil {
150178
setupLog.Error(err, "unable to create controller", "controller", swapi.ArtifactGeneratorKind)
151179
os.Exit(1)
152180
}
153181
// +kubebuilder:scaffold:builder
154182

155183
go func() {
156-
// Block until our controller manager is elected leader. We presume our
184+
// Block until our controller manager is elected leader.We presume our
157185
// entire process will terminate if we lose leadership, so we don't need
158186
// to handle that.
159187
<-mgr.Elected()
160188

161189
// Start the artifact server if running as leader.
190+
// This will make the readiness check pass and Kubernetes will start
191+
// routing traffic from kustomize-controller and helm-controller to this instance.
162192
setupLog.Info("starting storage server on " + artifactOptions.StorageAddress)
163-
if err := artsrv.Start(ctx, &artifactOptions); err != nil {
193+
if err := server.Start(ctx, &artifactOptions); err != nil {
164194
setupLog.Error(err, "artifact server error")
165195
os.Exit(1)
166196
}
167197
}()
168198

169-
probes.SetupChecks(mgr, setupLog)
170-
171199
setupLog.Info("starting manager")
172200
if err := mgr.Start(ctx); err != nil {
173201
setupLog.Error(err, "problem running manager")

config/crd/bases/source.extensions.fluxcd.io_artifactgenerators.yaml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,22 @@ spec:
1111
kind: ArtifactGenerator
1212
listKind: ArtifactGeneratorList
1313
plural: artifactgenerators
14+
shortNames:
15+
- ag
1416
singular: artifactgenerator
1517
scope: Namespaced
1618
versions:
17-
- name: v1beta1
19+
- additionalPrinterColumns:
20+
- jsonPath: .metadata.creationTimestamp
21+
name: Age
22+
type: date
23+
- jsonPath: .status.conditions[?(@.type=="Ready")].status
24+
name: Ready
25+
type: string
26+
- jsonPath: .status.conditions[?(@.type=="Ready")].message
27+
name: Status
28+
type: string
29+
name: v1beta1
1830
schema:
1931
openAPIV3Schema:
2032
description: ArtifactGenerator is the Schema for the artifactgenerators API.

config/rbac/role.yaml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ rules:
1313
- delete
1414
- get
1515
- list
16-
- patch
16+
- patchStatus
1717
- update
1818
- watch
1919
- apiGroups:
@@ -28,5 +28,17 @@ rules:
2828
- artifactgenerators/status
2929
verbs:
3030
- get
31-
- patch
31+
- patchStatus
3232
- update
33+
- apiGroups:
34+
- source.toolkit.fluxcd.io
35+
resources:
36+
- '*'
37+
verbs:
38+
- create
39+
- delete
40+
- get
41+
- list
42+
- patchStatus
43+
- update
44+
- watch

0 commit comments

Comments
 (0)