Skip to content

Commit 5401c1f

Browse files
committed
Do more to record bootstrap module status
- adapt the code for tracking assemblages and recording sync states and module summaries, from module_controller to bootstrapmodule_controller - use KustomizeReadyState from pkg/api/, consistently - try to get the RBAC annotations more accurate (again) Signed-off-by: Michael Bridgen <mikeb@squaremobius.net>
1 parent 8fdc9cd commit 5401c1f

File tree

14 files changed

+157
-148
lines changed

14 files changed

+157
-148
lines changed

assemblage/api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assemblage/controllers/assemblage_controller.go

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@ import (
99
"errors"
1010
"fmt"
1111

12-
"github.com/fluxcd/pkg/apis/meta"
1312
"github.com/go-logr/logr"
14-
apimeta "k8s.io/apimachinery/pkg/api/meta"
15-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1613
"k8s.io/apimachinery/pkg/runtime"
1714
ctrl "sigs.k8s.io/controller-runtime"
1815
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -122,7 +119,7 @@ func (r *AssemblageReconciler) Reconcile(ctx context.Context, req ctrl.Request)
122119
if syncStatus.State == "" {
123120
switch op {
124121
case controllerutil.OperationResultNone:
125-
syncStatus.State = readyState(&kustom)
122+
syncStatus.State = syncapi.KustomizeReadyState(&kustom)
126123
default:
127124
syncStatus.State = syncapi.StateUpdating
128125
}
@@ -141,25 +138,6 @@ func (r *AssemblageReconciler) Reconcile(ctx context.Context, req ctrl.Request)
141138
return ctrl.Result{}, nil
142139
}
143140

144-
func readyState(obj meta.ObjectWithStatusConditions) syncapi.SyncState {
145-
conditions := obj.GetStatusConditions()
146-
c := apimeta.FindStatusCondition(*conditions, meta.ReadyCondition)
147-
switch {
148-
case c == nil:
149-
return syncapi.StateUpdating
150-
case c.Status == metav1.ConditionTrue:
151-
return syncapi.StateSucceeded
152-
case c.Status == metav1.ConditionFalse:
153-
if c.Reason == meta.ReconciliationFailedReason {
154-
return syncapi.StateFailed
155-
} else {
156-
return syncapi.StateUpdating
157-
}
158-
default: // FIXME possibly StateUnknown?
159-
return syncapi.StateUpdating
160-
}
161-
}
162-
163141
func makeBindingFunc(ctx context.Context, log logr.Logger, namespacedClient client.Client, bindings []syncapi.Binding, stack []string) func(string) string {
164142
memo := map[string]string{}
165143
return func(name string) string {

assemblage/go.mod

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@ go 1.15
44

55
require (
66
github.com/fluxcd/kustomize-controller/api v0.12.0
7-
github.com/fluxcd/pkg/apis/meta v0.9.0
87
github.com/fluxcd/source-controller/api v0.12.2
98
github.com/go-logr/logr v0.4.0
109
github.com/onsi/ginkgo v1.14.1
1110
github.com/onsi/gomega v1.10.2
12-
github.com/squaremo/fleeet/pkg v0.0.2
11+
github.com/squaremo/fleeet/pkg v0.0.3-rc1
1312
k8s.io/api v0.20.4
1413
k8s.io/apimachinery v0.21.0
1514
k8s.io/client-go v0.20.4

assemblage/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,8 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
373373
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
374374
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
375375
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
376-
github.com/squaremo/fleeet/pkg v0.0.2 h1:yMfRYspuDb4S/qdXJ+J1rZjyBLpeNqglRbtXfOseeoM=
377-
github.com/squaremo/fleeet/pkg v0.0.2/go.mod h1:zmOneM0CcQv4RdOgsOCMVzVIlwt0yEGykPvpQqfSG8s=
376+
github.com/squaremo/fleeet/pkg v0.0.3-rc1 h1:IVlqw29VOBly03IyRiuUxvNBreFToxlYN0iYbiZwtUc=
377+
github.com/squaremo/fleeet/pkg v0.0.3-rc1/go.mod h1:zmOneM0CcQv4RdOgsOCMVzVIlwt0yEGykPvpQqfSG8s=
378378
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
379379
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
380380
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

module/api/v1alpha1/bootstrapmodule_types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
syncapi "github.com/squaremo/fleeet/pkg/api"
1111
)
1212

13+
const KindBootstrapModule = "BootstrapModule"
14+
1315
// BootstrapModuleSpec defines the desired state of BootstrapModule
1416
type BootstrapModuleSpec struct {
1517
// Selector gives the criteria for assigning this module to a

module/api/v1alpha1/remoteassemblage_types.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,12 @@ type LocalKubeconfigReference struct {
6363

6464
// RemoteAssemblageStatus defines the observed state of RemoteAssemblage
6565
type RemoteAssemblageStatus struct {
66-
Syncs []syncapi.SyncStatus `json:"syncs,omitempty"`
66+
Syncs []SyncStatus `json:"syncs,omitempty"`
67+
}
68+
69+
type SyncStatus struct {
70+
Name string `json:"name"`
71+
State syncapi.SyncState `json:"state"`
6772
}
6873

6974
//+kubebuilder:object:root=true

module/api/v1alpha1/zz_generated.deepcopy.go

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

module/config/crd/bases/fleet.squaremo.dev_remoteassemblages.yaml

Lines changed: 3 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -149,112 +149,14 @@ spec:
149149
properties:
150150
syncs:
151151
items:
152-
description: SyncStatus gives the status of a specific sync.
153152
properties:
153+
name:
154+
type: string
154155
state:
155-
description: State gives the outcome of last applied sync spec.
156156
type: string
157-
sync:
158-
description: Sync gives the last applied sync spec.
159-
properties:
160-
bindings:
161-
description: Bindings gives a list of variable bindings
162-
to use when evaluating the package spec in the sync
163-
items:
164-
description: Binding specifies how to obtain a value to
165-
bind to a name. The name can then be mentioned elsewhere
166-
in an object, and be replaced with the value as evaluated.
167-
properties:
168-
name:
169-
type: string
170-
objectFieldRef:
171-
properties:
172-
apiVersion:
173-
description: APIVersion gives the APIVersion (<group>/<version>)
174-
for the object's type
175-
type: string
176-
fieldPath:
177-
description: Path is a JSONPointer expression
178-
for finding the value in the object identified
179-
type: string
180-
kind:
181-
description: Kind gives the kind of the object's
182-
type
183-
type: string
184-
name:
185-
description: Name names the object
186-
type: string
187-
required:
188-
- fieldPath
189-
- kind
190-
- name
191-
type: object
192-
value:
193-
type: string
194-
required:
195-
- name
196-
type: object
197-
type: array
198-
name:
199-
description: Name gives the sync a name so it can be correlated
200-
to the status
201-
type: string
202-
package:
203-
default:
204-
kustomize:
205-
path: .
206-
description: Package defines how to deal with the configuration
207-
at the source, e.g., if it's a kustomization (or YAML
208-
files)
209-
properties:
210-
kustomize:
211-
properties:
212-
path:
213-
default: .
214-
description: Path gives the path within the source
215-
to treat as the Kustomization root.
216-
type: string
217-
substitute:
218-
additionalProperties:
219-
type: string
220-
description: Substitute gives a map of names to
221-
values to substitute in the YAML built from the
222-
kustomization.
223-
type: object
224-
type: object
225-
type: object
226-
source:
227-
description: Source gives the specification for how to get
228-
the configuration to be synced
229-
properties:
230-
git:
231-
properties:
232-
url:
233-
description: URL gives the URL for the git repository
234-
type: string
235-
version:
236-
description: Version gives either the revision or
237-
tag at which to get the git repo
238-
properties:
239-
revision:
240-
type: string
241-
tag:
242-
type: string
243-
type: object
244-
required:
245-
- url
246-
- version
247-
type: object
248-
required:
249-
- git
250-
type: object
251-
required:
252-
- name
253-
- source
254-
type: object
255157
required:
158+
- name
256159
- state
257-
- sync
258160
type: object
259161
type: array
260162
type: object

module/config/rbac/role.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ rules:
2121
verbs:
2222
- get
2323
- list
24+
- watch
2425
- apiGroups:
2526
- fleet.squaremo.dev
2627
resources:

module/controllers/bootstrapmodule_controller.go

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ type BootstrapModuleReconciler struct {
3939
//+kubebuilder:rbac:groups=fleet.squaremo.dev,resources=bootstrapmodules/status,verbs=get;update;patch
4040
//+kubebuilder:rbac:groups=fleet.squaremo.dev,resources=bootstrapmodules/finalizers,verbs=update
4141

42+
// These are what the controller creates:
43+
//+kubebuilder:rbac:groups=fleet.squaremo.dev,resources=remoteassemblages,verbs=get;list;watch;create;update;patch;delete
44+
//+kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=gitrepositories,verbs=get;list;watch;create;update;patch;delete
45+
46+
// The controller watches these, to see when the selection changes
4247
//+kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters,verbs=get;list;watch
4348

4449
// Reconcile moves the cluster closer to the desired state, for a particular
@@ -94,7 +99,18 @@ func (r *BootstrapModuleReconciler) Reconcile(ctx context.Context, req ctrl.Requ
9499
Kind: source.Kind,
95100
}
96101

102+
summary := &fleetv1.SyncSummary{}
103+
104+
// Keep track of the assemblages which did require this module;
105+
// afterwards, this will be helpful to determine the assemblages
106+
// which need the module removed.
107+
requiredAsm := map[string]struct{}{}
108+
109+
clusters:
97110
for _, cluster := range clusters.Items {
111+
summary.Total++
112+
requiredAsm[cluster.GetName()] = struct{}{}
113+
98114
asm := &fleetv1.RemoteAssemblage{}
99115
asm.Namespace = cluster.GetNamespace()
100116
asm.Name = cluster.GetName()
@@ -136,19 +152,95 @@ func (r *BootstrapModuleReconciler) Reconcile(ctx context.Context, req ctrl.Requ
136152
return nil
137153
})
138154
if err != nil {
139-
return ctrl.Result{}, err
155+
summary.Failed++
156+
log.Error(err, "updating remote assemblages", "assemblage", asm.Name)
157+
} else {
158+
log.V(1).Info("created/updated RemoteAssemblage", "name", asm.Name, "operation", op)
159+
for _, sync := range asm.Status.Syncs {
160+
if sync.Name == mod.Name {
161+
switch sync.State {
162+
case syncapi.StateSucceeded:
163+
summary.Succeeded++
164+
case syncapi.StateFailed:
165+
summary.Failed++
166+
default:
167+
summary.Updating++
168+
}
169+
continue clusters // all done here
170+
}
171+
// no change made, but status not found -> updating
172+
summary.Updating++
173+
}
174+
}
175+
}
176+
177+
mod.Status.Summary = summary
178+
if err := r.Status().Update(ctx, &mod); err != nil {
179+
return ctrl.Result{}, fmt.Errorf("updating status of bootstrap module: %w", err)
180+
}
181+
182+
// Find all assemblages indexed as owned by (i.e., including) this
183+
// module
184+
var asms fleetv1.RemoteAssemblageList
185+
if err := r.List(ctx, &asms, client.InNamespace(req.Namespace), client.MatchingFields{remoteAssemblageOwnerKey: req.Name}); err != nil {
186+
return ctrl.Result{}, fmt.Errorf("listing assemblages for this module: %w", err)
187+
}
188+
189+
// This loop removes the bootstrap module from any assemblage for a cluster that _wasn't_
190+
// selected. (Remember, these assemblages were selected because they were owned by this module,
191+
// implying that at some point the module was assigned to the cluster)
192+
for _, asm := range asms.Items {
193+
if _, ok := requiredAsm[asm.GetName()]; !ok {
194+
syncs := asm.Spec.Syncs
195+
for i, sync := range syncs {
196+
if sync.Name == mod.Name {
197+
if _, err := controllerutil.CreateOrPatch(ctx, r.Client, &asm, func() error {
198+
asm.Spec.Syncs = append(syncs[:i], syncs[i+1:]...)
199+
removeOwnerRef(&mod, &asm)
200+
return nil
201+
}); err != nil {
202+
log.Error(err, "removing module from remote assemblage", "assemblage", asm.Name)
203+
}
204+
// FIXME: can this `break` from the loop at this point?
205+
}
206+
}
140207
}
141-
log.V(1).Info("created/updated RemoteAssemblage", "name", asm.Name, "operation", op)
142208
}
143-
// TODO find any redundant sources and assemblages and delete them
144209

145210
return ctrl.Result{}, nil
146211
}
147212

213+
const remoteAssemblageOwnerKey = "ownerBootstrapModule"
214+
148215
// SetupWithManager sets up the controller with the Manager.
149216
func (r *BootstrapModuleReconciler) SetupWithManager(mgr ctrl.Manager) error {
217+
// This index is to conveniently find the remote assemblages that a bootstrap module owns (i.e.,
218+
// is included in). The watch on RemoteAssemblages below will enqueue each bootstrap module
219+
// owner of a remote aseemblage that has been updated.
220+
if err := mgr.GetFieldIndexer().IndexField(context.TODO(), &fleetv1.RemoteAssemblage{}, remoteAssemblageOwnerKey, func(obj client.Object) []string {
221+
asm := obj.(*fleetv1.RemoteAssemblage)
222+
var moduleOwners []string
223+
for _, owner := range asm.GetOwnerReferences() {
224+
// FIXME: make this more reliable? What are the consequences of getting another API's
225+
// BootstrapModule mixed in here? Something like this might be better:
226+
// https://github.com/kubernetes-sigs/controller-runtime/blob/v0.11.1/pkg/handler/enqueue_owner.go#L46
227+
if owner.Kind == fleetv1.KindBootstrapModule {
228+
moduleOwners = append(moduleOwners, owner.Name)
229+
}
230+
}
231+
return moduleOwners
232+
}); err != nil {
233+
return err
234+
}
235+
150236
return ctrl.NewControllerManagedBy(mgr).
151237
For(&fleetv1.BootstrapModule{}).
238+
Watches(
239+
&source.Kind{Type: &fleetv1.RemoteAssemblage{}},
240+
&handler.EnqueueRequestForOwner{
241+
OwnerType: &fleetv1.BootstrapModule{},
242+
IsController: false,
243+
}).
152244

153245
// Enqueue all the BootstrapModule objects that pertain to a
154246
// particular cluster

0 commit comments

Comments
 (0)