From 4f4721717182f780875e06cf941031bf12d3839a Mon Sep 17 00:00:00 2001 From: eshulman2 Date: Wed, 24 Dec 2025 17:41:55 +0200 Subject: [PATCH] globalize get dependency helper - create a dependency helper replacing repetative parts of code - update template to use it --- .../data/controller/actuator.go.template | 42 +++++------- internal/util/dependency/helpers.go | 66 +++++++++++++++++++ 2 files changed, 83 insertions(+), 25 deletions(-) create mode 100644 internal/util/dependency/helpers.go diff --git a/cmd/scaffold-controller/data/controller/actuator.go.template b/cmd/scaffold-controller/data/controller/actuator.go.template index dc083e79d..472405919 100644 --- a/cmd/scaffold-controller/data/controller/actuator.go.template +++ b/cmd/scaffold-controller/data/controller/actuator.go.template @@ -25,9 +25,6 @@ import ( "{{ .GophercloudModule }}" corev1 "k8s.io/api/core/v1" -{{- if len .ImportDependencies }} - apierrors "k8s.io/apimachinery/pkg/api/errors" -{{- end }} "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -37,6 +34,9 @@ import ( "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/progress" "github.com/k-orc/openstack-resource-controller/v2/internal/logging" "github.com/k-orc/openstack-resource-controller/v2/internal/osclients" +{{- if len .ImportDependencies }} + "github.com/k-orc/openstack-resource-controller/v2/internal/util/dependency" +{{- end }} orcerrors "github.com/k-orc/openstack-resource-controller/v2/internal/util/errors" ) @@ -106,41 +106,33 @@ func (actuator {{ .PackageName }}Actuator) ListOSResourcesForImport(ctx context. var reconcileStatus progress.ReconcileStatus {{- range .ImportDependencies }} {{ $depNameCamelCase := . | camelCase }} - {{ $depNameCamelCase }} := &orcv1alpha1.{{ . }}{} - if filter.{{ . }}Ref != nil { - {{ $depNameCamelCase }}Key := client.ObjectKey{Name: string(*filter.{{ . }}Ref), Namespace: obj.Namespace} - if err := actuator.k8sClient.Get(ctx, {{ $depNameCamelCase }}Key, {{ $depNameCamelCase }}); err != nil { - if apierrors.IsNotFound(err) { - reconcileStatus = reconcileStatus.WithReconcileStatus( - progress.WaitingOnObject("{{ . }}", {{ $depNameCamelCase }}Key.Name, progress.WaitingOnCreation)) - } else { - reconcileStatus = reconcileStatus.WithReconcileStatus( - progress.WrapError(fmt.Errorf("fetching {{ $depNameCamelCase }} %s: %w", {{ $depNameCamelCase }}Key.Name, err))) - } - } else { - if !orcv1alpha1.IsAvailable({{ $depNameCamelCase }}) || {{ $depNameCamelCase }}.Status.ID == nil { - reconcileStatus = reconcileStatus.WithReconcileStatus( - progress.WaitingOnObject("{{ . }}", {{ $depNameCamelCase }}Key.Name, progress.WaitingOnReady)) - } - } - } + {{ $depNameCamelCase }}, rs := dependency.FetchDependency[*orcv1alpha1.{{ . }}, orcv1alpha1.{{ . }}]( + ctx, actuator.k8sClient, obj.Namespace, + filter.{{ . }}Ref, "{{ . }}", + func(dep *orcv1alpha1.{{ . }}) bool { return orcv1alpha1.IsAvailable(dep) && dep.Status.ID != nil }, + ) + reconcileStatus = reconcileStatus.WithReconcileStatus(rs) {{- end }} - if needsReschedule, _ := reconcileStatus.NeedsReschedule(); needsReschedule { - return nil, reconcileStatus + var {{ range $i, $dep := .ImportDependencies }}{{ if $i }}, {{ end }}{{ $dep | camelCase }}ID{{ end }} string +{{- range .ImportDependencies }} +{{ $depNameCamelCase := . | camelCase }} + if {{ $depNameCamelCase }} != nil { + {{ $depNameCamelCase }}ID = ptr.Deref({{ $depNameCamelCase }}.Status.ID, "") } +{{- end }} {{- end }} listOpts := {{ .GophercloudPackage }}.ListOpts{ Name: string(ptr.Deref(filter.Name, "")), Description: string(ptr.Deref(filter.Description, "")), {{- range .ImportDependencies }} - {{ . }}: ptr.Deref({{ . | camelCase }}.Status.ID, ""), + {{ . }}ID: {{ . | camelCase }}ID, {{- end }} // TODO(scaffolding): Add more import filters } - return actuator.osClient.List{{ .Kind }}s(ctx, listOpts), nil + return actuator.osClient.List{{ .Kind }}s(ctx, listOpts), {{ if len .ImportDependencies }}reconcileStatus{{ else }}nil{{ end }} } func (actuator {{ .PackageName }}Actuator) CreateResource(ctx context.Context, obj orcObjectPT) (*osResourceT, progress.ReconcileStatus) { diff --git a/internal/util/dependency/helpers.go b/internal/util/dependency/helpers.go new file mode 100644 index 000000000..7d4da0484 --- /dev/null +++ b/internal/util/dependency/helpers.go @@ -0,0 +1,66 @@ +/* +Copyright 2025 The ORC Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dependency + +import ( + "context" + "fmt" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "sigs.k8s.io/controller-runtime/pkg/client" + + orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1" + "github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/progress" +) + +// FetchDependency fetches a resource by name and checks if it's ready. +// Unlike GetDependency on DeletionGuardDependency, this doesn't add finalizers +// and is suitable for one-off lookups like resolving refs in import filters. +// +// If name is empty, returns (nil, nil) - no fetch is performed. +// +// Returns: +// - The fetched object (nil if name is empty, not found, or not ready) +// - ReconcileStatus indicating wait state or error (nil if name is empty) +func FetchDependency[TP DependencyType[T], T any]( + ctx context.Context, + k8sClient client.Client, + namespace string, + name *orcv1alpha1.KubernetesNameRef, + kind string, + isReady func(TP) bool, +) (TP, progress.ReconcileStatus) { + if name == nil { + return nil, nil + } + + var obj TP = new(T) + objectKey := client.ObjectKey{Name: string(*name), Namespace: namespace} + + if err := k8sClient.Get(ctx, objectKey, obj); err != nil { + if apierrors.IsNotFound(err) { + return nil, progress.NewReconcileStatus().WaitingOnObject(kind, string(*name), progress.WaitingOnCreation) + } + return nil, progress.WrapError(fmt.Errorf("fetching %s %s: %w", kind, string(*name), err)) + } + + if !isReady(obj) { + return nil, progress.NewReconcileStatus().WaitingOnObject(kind, string(*name), progress.WaitingOnReady) + } + + return obj, nil +}