Skip to content

Commit 7f59ed3

Browse files
authored
fix: infinite supergraph reconcile loop (#35)
* fix: infinite supergraph reconcile loop * fix: lint
1 parent eed9f0d commit 7f59ed3

5 files changed

Lines changed: 220 additions & 406 deletions

File tree

internal/controllers/supergraph_controller.go

Lines changed: 76 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,13 @@ func (r *SuperGraphReconciler) reconcile(ctx context.Context, supergraph infrav1
192192
}
193193

194194
if apierrors.IsNotFound(err) {
195-
return supergraph, ctrl.Result{}, fmt.Errorf("schema %s not found", supergraph.Spec.Schema.Name)
195+
supergraph = infrav1beta1.SuperGraphReady(supergraph, metav1.ConditionFalse, "ReconciliationFailed", "schema not found")
196+
return supergraph, ctrl.Result{}, nil
196197
}
197198

198199
if graphschema.Status.ConfigMap.Name == "" {
199-
return supergraph, ctrl.Result{}, fmt.Errorf("supergraphschema is not ready")
200+
supergraph = infrav1beta1.SuperGraphReady(supergraph, metav1.ConditionFalse, "ReconciliationFailed", "graphql schema is not ready")
201+
return supergraph, ctrl.Result{}, nil
200202
}
201203

202204
var schema corev1.ConfigMap
@@ -226,7 +228,7 @@ func (r *SuperGraphReconciler) reconcile(ctx context.Context, supergraph infrav1
226228
)
227229

228230
controllerOwner := true
229-
template := &appsv1.Deployment{
231+
deploymentTemplate := &appsv1.Deployment{
230232
ObjectMeta: metav1.ObjectMeta{
231233
Name: fmt.Sprintf("apollo-router-%s", supergraph.Name),
232234
Namespace: supergraph.Namespace,
@@ -254,49 +256,49 @@ func (r *SuperGraphReconciler) reconcile(ctx context.Context, supergraph infrav1
254256
}
255257

256258
if supergraph.Spec.DeploymentTemplate != nil {
257-
template.Labels = supergraph.Spec.DeploymentTemplate.Labels
258-
template.Annotations = supergraph.Spec.DeploymentTemplate.Annotations
259-
supergraph.Spec.DeploymentTemplate.Spec.Template.DeepCopyInto(&template.Spec.Template)
260-
template.Spec.MinReadySeconds = supergraph.Spec.DeploymentTemplate.Spec.MinReadySeconds
261-
template.Spec.Paused = supergraph.Spec.DeploymentTemplate.Spec.Paused
262-
template.Spec.ProgressDeadlineSeconds = supergraph.Spec.DeploymentTemplate.Spec.ProgressDeadlineSeconds
263-
template.Spec.Replicas = supergraph.Spec.DeploymentTemplate.Spec.Replicas
264-
template.Spec.RevisionHistoryLimit = supergraph.Spec.DeploymentTemplate.Spec.RevisionHistoryLimit
265-
template.Spec.Strategy = supergraph.Spec.DeploymentTemplate.Spec.Strategy
259+
deploymentTemplate.Labels = supergraph.Spec.DeploymentTemplate.Labels
260+
deploymentTemplate.Annotations = supergraph.Spec.DeploymentTemplate.Annotations
261+
supergraph.Spec.DeploymentTemplate.Spec.Template.DeepCopyInto(&deploymentTemplate.Spec.Template)
262+
deploymentTemplate.Spec.MinReadySeconds = supergraph.Spec.DeploymentTemplate.Spec.MinReadySeconds
263+
deploymentTemplate.Spec.Paused = supergraph.Spec.DeploymentTemplate.Spec.Paused
264+
deploymentTemplate.Spec.ProgressDeadlineSeconds = supergraph.Spec.DeploymentTemplate.Spec.ProgressDeadlineSeconds
265+
deploymentTemplate.Spec.Replicas = supergraph.Spec.DeploymentTemplate.Spec.Replicas
266+
deploymentTemplate.Spec.RevisionHistoryLimit = supergraph.Spec.DeploymentTemplate.Spec.RevisionHistoryLimit
267+
deploymentTemplate.Spec.Strategy = supergraph.Spec.DeploymentTemplate.Spec.Strategy
266268
}
267269

268-
if template.Labels == nil {
269-
template.Labels = make(map[string]string)
270+
if deploymentTemplate.Labels == nil {
271+
deploymentTemplate.Labels = make(map[string]string)
270272
}
271273

272-
if template.Spec.Template.Labels == nil {
273-
template.Spec.Template.Labels = make(map[string]string)
274+
if deploymentTemplate.Spec.Template.Labels == nil {
275+
deploymentTemplate.Spec.Template.Labels = make(map[string]string)
274276
}
275277

276-
template.Spec.Selector = &metav1.LabelSelector{
278+
deploymentTemplate.Spec.Selector = &metav1.LabelSelector{
277279
MatchLabels: map[string]string{
278280
"app.kubernetes.io/instance": "apollo-router",
279281
"app.kubernetes.io/name": "apollo-router",
280282
"apollo-controller/supergraph": supergraph.Name,
281283
},
282284
}
283285

284-
if template.Spec.Replicas == nil {
285-
template.Spec.Replicas = &replicas
286+
if deploymentTemplate.Spec.Replicas == nil {
287+
deploymentTemplate.Spec.Replicas = &replicas
286288
}
287289

288-
template.Spec.Template.Labels["app.kubernetes.io/instance"] = "apollo-router"
289-
template.Spec.Template.Labels["app.kubernetes.io/name"] = "apollo-router"
290-
template.Spec.Template.Labels["apollo-controller/supergraph"] = supergraph.Name
291-
template.Labels["app.kubernetes.io/instance"] = "apollo-router"
292-
template.Labels["app.kubernetes.io/name"] = "apollo-router"
293-
template.Labels["apollo-controller/supergraph"] = supergraph.Name
290+
deploymentTemplate.Spec.Template.Labels["app.kubernetes.io/instance"] = "apollo-router"
291+
deploymentTemplate.Spec.Template.Labels["app.kubernetes.io/name"] = "apollo-router"
292+
deploymentTemplate.Spec.Template.Labels["apollo-controller/supergraph"] = supergraph.Name
293+
deploymentTemplate.Labels["app.kubernetes.io/instance"] = "apollo-router"
294+
deploymentTemplate.Labels["app.kubernetes.io/name"] = "apollo-router"
295+
deploymentTemplate.Labels["apollo-controller/supergraph"] = supergraph.Name
294296

295-
if template.Annotations == nil {
296-
template.Annotations = make(map[string]string)
297+
if deploymentTemplate.Annotations == nil {
298+
deploymentTemplate.Annotations = make(map[string]string)
297299
}
298300

299-
template.Spec.Template.Spec.Volumes = append(template.Spec.Template.Spec.Volumes, corev1.Volume{
301+
deploymentTemplate.Spec.Template.Spec.Volumes = append(deploymentTemplate.Spec.Template.Spec.Volumes, corev1.Volume{
300302
Name: "schema",
301303
VolumeSource: corev1.VolumeSource{
302304
ConfigMap: &corev1.ConfigMapVolumeSource{
@@ -367,13 +369,13 @@ func (r *SuperGraphReconciler) reconcile(ctx context.Context, supergraph infrav1
367369
},
368370
}
369371

370-
containers, err = merge.MergePatchContainers(containers, template.Spec.Template.Spec.Containers)
372+
containers, err = merge.MergePatchContainers(containers, deploymentTemplate.Spec.Template.Spec.Containers)
371373
if err != nil {
372374
return supergraph, ctrl.Result{}, err
373375
}
374376

375-
template.Spec.Template.Spec.Containers = containers
376-
r.Log.Info("create apollo-router deployment", "deployment-name", template.Name)
377+
deploymentTemplate.Spec.Template.Spec.Containers = containers
378+
r.Log.Info("create apollo-router deployment", "deployment-name", deploymentTemplate.Name)
377379

378380
svcTemplate := &corev1.Service{
379381
ObjectMeta: metav1.ObjectMeta{
@@ -405,55 +407,15 @@ func (r *SuperGraphReconciler) reconcile(ctx context.Context, supergraph infrav1
405407
},
406408
}
407409

408-
var svc corev1.Service
409-
err = r.Get(ctx, client.ObjectKey{
410-
Namespace: svcTemplate.Namespace,
411-
Name: svcTemplate.Name,
412-
}, &svc)
413-
414-
if err != nil && !apierrors.IsNotFound(err) {
410+
if err := r.createOrUpdateWithOwnershipValidation(ctx, &supergraph, svcTemplate); err != nil {
415411
return supergraph, ctrl.Result{}, err
416412
}
417413

418-
if apierrors.IsNotFound(err) {
419-
if err := r.Create(ctx, svcTemplate); err != nil {
420-
return supergraph, ctrl.Result{}, err
421-
}
422-
} else {
423-
if !isOwner(&supergraph, &svc) {
424-
return supergraph, ctrl.Result{}, fmt.Errorf("can not take ownership of existing service: %s", svc.Name)
425-
}
426-
427-
if err := r.Update(ctx, svcTemplate); err != nil {
428-
return supergraph, ctrl.Result{}, err
429-
}
430-
}
431-
432-
var deployment appsv1.Deployment
433-
err = r.Get(ctx, client.ObjectKey{
434-
Namespace: template.Namespace,
435-
Name: template.Name,
436-
}, &deployment)
437-
438-
if err != nil && !apierrors.IsNotFound(err) {
414+
if err := r.createOrUpdateWithOwnershipValidation(ctx, &supergraph, deploymentTemplate); err != nil {
439415
return supergraph, ctrl.Result{}, err
440416
}
441417

442-
if apierrors.IsNotFound(err) {
443-
if err := r.Create(ctx, template); err != nil {
444-
return supergraph, ctrl.Result{}, err
445-
}
446-
} else {
447-
if !isOwner(&supergraph, &deployment) {
448-
return supergraph, ctrl.Result{}, fmt.Errorf("can not take ownership of existing deployment: %s", deployment.Name)
449-
}
450-
451-
if err := r.Update(ctx, template); err != nil {
452-
return supergraph, ctrl.Result{}, err
453-
}
454-
}
455-
456-
supergraph = infrav1beta1.SuperGraphReady(supergraph, metav1.ConditionTrue, "ReconciliationSuccessful", fmt.Sprintf("deployment/%s created", template.Name))
418+
supergraph = infrav1beta1.SuperGraphReady(supergraph, metav1.ConditionTrue, "ReconciliationSuccessful", fmt.Sprintf("deployment/%s created", deploymentTemplate.Name))
457419
return supergraph, ctrl.Result{}, nil
458420
}
459421

@@ -475,19 +437,19 @@ func (r *SuperGraphReconciler) reconcileConfig(ctx context.Context, supergraph i
475437
},
476438
}
477439

478-
routerConfig := make(map[string]interface{})
440+
routerConfig := make(map[string]any)
479441
if len(supergraph.Spec.RouterConfig.Raw) > 0 {
480442
if err := json.Unmarshal(supergraph.Spec.RouterConfig.Raw, &routerConfig); err != nil {
481443
return supergraph, ctrl.Result{}, fmt.Errorf("failed to deserialize router config: %w", err)
482444
}
483445
}
484446

485-
if healthCheckVal, ok := routerConfig["health_check"].(map[string]interface{}); ok {
447+
if healthCheckVal, ok := routerConfig["health_check"].(map[string]any); ok {
486448
if _, exists := healthCheckVal["listen"]; !exists {
487449
healthCheckVal["listen"] = "0.0.0.0:8088"
488450
}
489451
} else {
490-
routerConfig["health_check"] = map[string]interface{}{
452+
routerConfig["health_check"] = map[string]any{
491453
"listen": "0.0.0.0:8088",
492454
}
493455
}
@@ -497,34 +459,55 @@ func (r *SuperGraphReconciler) reconcileConfig(ctx context.Context, supergraph i
497459
return supergraph, ctrl.Result{}, fmt.Errorf("failed to serialize router config to YAML: %w", err)
498460
}
499461

500-
cm.BinaryData = make(map[string][]byte)
501-
cm.BinaryData["router.yaml"] = routerConfigYAML
462+
cm.Data = make(map[string]string)
463+
cm.Data["router.yaml"] = string(routerConfigYAML)
502464

503-
var existingSpec corev1.ConfigMap
504-
err = r.Get(ctx, client.ObjectKey{
505-
Namespace: cm.Namespace,
506-
Name: cm.Name,
507-
}, &existingSpec)
465+
if err := r.createOrUpdateWithOwnershipValidation(ctx, &supergraph, cm); err != nil {
466+
return supergraph, ctrl.Result{}, err
467+
}
468+
469+
supergraph.Status.ConfigMap = corev1.LocalObjectReference{
470+
Name: cm.Name,
471+
}
472+
473+
return supergraph, ctrl.Result{}, nil
474+
}
475+
476+
func (r *SuperGraphReconciler) createOrUpdateWithOwnershipValidation(ctx context.Context, owner client.Object, obj client.Object) error {
477+
existing := obj.DeepCopyObject().(client.Object)
478+
err := r.Get(ctx, client.ObjectKey{
479+
Namespace: obj.GetNamespace(),
480+
Name: obj.GetName(),
481+
}, existing)
508482

509483
if err != nil && !apierrors.IsNotFound(err) {
510-
return supergraph, ctrl.Result{}, err
484+
return err
511485
}
512486

513487
if apierrors.IsNotFound(err) {
514-
if err := r.Create(ctx, cm); err != nil {
515-
return supergraph, ctrl.Result{}, err
488+
if err := r.Create(ctx, obj); err != nil {
489+
return err
516490
}
517491
} else {
518-
if err := r.Update(ctx, cm); err != nil {
519-
return supergraph, ctrl.Result{}, err
492+
if !isOwner(owner, existing) {
493+
return fmt.Errorf("can not take ownership of existing resource: %s", obj.GetName())
520494
}
521-
}
522495

523-
supergraph.Status.ConfigMap = corev1.LocalObjectReference{
524-
Name: cm.Name,
496+
obj.GetObjectKind().SetGroupVersionKind(existing.GetObjectKind().GroupVersionKind())
497+
err := r.Patch(
498+
ctx,
499+
obj,
500+
client.Apply,
501+
client.FieldOwner("apollo-controller"),
502+
client.ForceOwnership,
503+
)
504+
505+
if err != nil {
506+
return fmt.Errorf("can not patch resource: %w", err)
507+
}
525508
}
526509

527-
return supergraph, ctrl.Result{}, nil
510+
return nil
528511
}
529512

530513
func (r *SuperGraphReconciler) patchStatus(ctx context.Context, supergraph *infrav1beta1.SuperGraph) error {

internal/controllers/supergraph_controller_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ var _ = Describe("SuperGraph controller", func() {
8686
Type: v1beta1.ConditionReady,
8787
Status: metav1.ConditionFalse,
8888
Reason: "ReconciliationFailed",
89-
Message: fmt.Sprintf("schema %s not found", ""),
89+
Message: "schema not found",
9090
},
9191
},
9292
}

internal/controllers/supergraphschema_controller.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,11 @@ func (r *SuperGraphSchemaReconciler) reconcile(ctx context.Context, schema infra
310310
return r.handleReconcilerState(ctx, schema, pod, checksum, logger)
311311
}
312312

313+
if len(subgraphs) == 0 {
314+
schema = infrav1beta1.SuperGraphSchemaReady(schema, metav1.ConditionFalse, "ReconciliationFailed", "empty subgraph catalog")
315+
return schema, ctrl.Result{}, nil
316+
}
317+
313318
return r.createReconciler(ctx, schema, subgraphs, checksum, logger)
314319
}
315320

0 commit comments

Comments
 (0)