Skip to content

Commit ade4e37

Browse files
committed
controlplane/status: fix nil deref in updateStatus; guard workspaceApply LastAppliedTime; bump VERSION to 0.4.6 and CHANGELOG for v0.4.6
1 parent 64e72c7 commit ade4e37

3 files changed

Lines changed: 57 additions & 45 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
### Notes
1414
- No functional changes from v0.4.4; this is a metadata/version alignment release.
1515

16+
## [v0.4.6] - 2025-11-11
17+
18+
### Fixed
19+
- CAPTControlPlane: `updateStatus` 内で `WorkspaceTemplateStatus``nil` の場合に panic する不具合を修正。使用直前での初期化を追加し、`workspaceApply``LastAppliedTime` 参照にも `nil` ガードを追加しました。これにより `runtime.sigpanic`(status.go:190 付近)が発生しなくなります。
20+
21+
### Notes
22+
- コントローラ安定性向上のための推奨アップデートです。Helm Chart のバージョンは変更していません。
23+
1624
## [v0.4.4] - 2025-11-04
1725

1826
### Changed

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
VERSION = 0.4.5
1+
VERSION = 0.4.6

internal/controller/controlplane/status.go

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
package controlplane
22

33
import (
4-
"context"
5-
"fmt"
6-
"reflect"
7-
8-
controlplanev1beta1 "github.com/appthrust/capt/api/controlplane/v1beta1"
9-
infrastructurev1beta1 "github.com/appthrust/capt/api/v1beta1"
10-
"github.com/appthrust/capt/internal/controller/controlplane/endpoint"
11-
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
12-
corev1 "k8s.io/api/core/v1"
13-
apierrors "k8s.io/apimachinery/pkg/api/errors"
14-
"k8s.io/apimachinery/pkg/api/meta"
15-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
16-
"k8s.io/apimachinery/pkg/types"
17-
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
18-
"sigs.k8s.io/cluster-api/errors"
19-
"sigs.k8s.io/cluster-api/util/conditions"
20-
ctrl "sigs.k8s.io/controller-runtime"
21-
"sigs.k8s.io/controller-runtime/pkg/client"
22-
"sigs.k8s.io/controller-runtime/pkg/log"
4+
"context"
5+
"fmt"
6+
"reflect"
7+
8+
controlplanev1beta1 "github.com/appthrust/capt/api/controlplane/v1beta1"
9+
infrastructurev1beta1 "github.com/appthrust/capt/api/v1beta1"
10+
"github.com/appthrust/capt/internal/controller/controlplane/endpoint"
11+
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
12+
corev1 "k8s.io/api/core/v1"
13+
apierrors "k8s.io/apimachinery/pkg/api/errors"
14+
"k8s.io/apimachinery/pkg/api/meta"
15+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
16+
"k8s.io/apimachinery/pkg/types"
17+
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
18+
"sigs.k8s.io/cluster-api/errors"
19+
"sigs.k8s.io/cluster-api/util/conditions"
20+
ctrl "sigs.k8s.io/controller-runtime"
21+
"sigs.k8s.io/controller-runtime/pkg/client"
22+
"sigs.k8s.io/controller-runtime/pkg/log"
2323
)
2424

2525
const (
@@ -95,32 +95,32 @@ func (r *Reconciler) updateStatus(
9595
"workspaceStatus", controlPlane.Status.WorkspaceStatus,
9696
"workspaceTemplateStatus", controlPlane.Status.WorkspaceTemplateStatus)
9797

98-
// Decide readiness and which WorkspaceTemplateApply to use for endpoint based on
99-
// either the main control plane workspace OR the kubeconfig workspace.
100-
effectiveApply := workspaceApply
101-
ready := isWorkspaceReady(effectiveApply)
102-
errorMessage := getWorkspaceError(effectiveApply)
103-
104-
// Prefer kubeconfig WorkspaceTemplateApply if it exists and is Ready.
105-
// This allows marking the control plane Ready even when addon installation
106-
// is pending, avoiding deadlocks (e.g., sveltos waiting on Cluster Ready).
107-
kubeApplyName := fmt.Sprintf("%s-kubeconfig-apply", controlPlane.Name)
108-
kubeApply := &infrastructurev1beta1.WorkspaceTemplateApply{}
109-
if err := r.Client.Get(ctx, types.NamespacedName{Name: kubeApplyName, Namespace: controlPlane.Namespace}, kubeApply); err == nil {
110-
if isWorkspaceReady(kubeApply) {
111-
effectiveApply = kubeApply
112-
ready = true
113-
errorMessage = ""
114-
}
115-
}
116-
117-
if !ready {
118-
return r.handleNotReadyStatus(ctx, controlPlane, cluster, errorMessage)
119-
}
98+
// Decide readiness and which WorkspaceTemplateApply to use for endpoint based on
99+
// either the main control plane workspace OR the kubeconfig workspace.
100+
effectiveApply := workspaceApply
101+
ready := isWorkspaceReady(effectiveApply)
102+
errorMessage := getWorkspaceError(effectiveApply)
103+
104+
// Prefer kubeconfig WorkspaceTemplateApply if it exists and is Ready.
105+
// This allows marking the control plane Ready even when addon installation
106+
// is pending, avoiding deadlocks (e.g., sveltos waiting on Cluster Ready).
107+
kubeApplyName := fmt.Sprintf("%s-kubeconfig-apply", controlPlane.Name)
108+
kubeApply := &infrastructurev1beta1.WorkspaceTemplateApply{}
109+
if err := r.Client.Get(ctx, types.NamespacedName{Name: kubeApplyName, Namespace: controlPlane.Namespace}, kubeApply); err == nil {
110+
if isWorkspaceReady(kubeApply) {
111+
effectiveApply = kubeApply
112+
ready = true
113+
errorMessage = ""
114+
}
115+
}
116+
117+
if !ready {
118+
return r.handleNotReadyStatus(ctx, controlPlane, cluster, errorMessage)
119+
}
120120

121121
// Update endpoint from workspace first
122-
if effectiveApply.Status.WorkspaceName != "" {
123-
if apiEndpoint, err := endpoint.GetEndpointFromWorkspace(ctx, r.Client, effectiveApply.Status.WorkspaceName); err != nil {
122+
if effectiveApply.Status.WorkspaceName != "" {
123+
if apiEndpoint, err := endpoint.GetEndpointFromWorkspace(ctx, r.Client, effectiveApply.Status.WorkspaceName); err != nil {
124124
errMsg := fmt.Sprintf("Failed to get endpoint from workspace: %v", err)
125125
return r.setFailedStatus(ctx, controlPlane, cluster, ReasonEndpointUpdateFailed, errMsg)
126126
} else if apiEndpoint != nil {
@@ -187,12 +187,16 @@ func (r *Reconciler) updateStatus(
187187
controlPlane.Status.Phase = controlplanev1beta1.ControlPlaneReadyCondition
188188
controlPlane.Status.Ready = true
189189
controlPlane.Status.Initialized = true
190+
// Ensure WorkspaceTemplateStatus is initialized before accessing its fields
191+
if controlPlane.Status.WorkspaceTemplateStatus == nil {
192+
controlPlane.Status.WorkspaceTemplateStatus = &controlplanev1beta1.WorkspaceTemplateStatus{}
193+
}
190194
controlPlane.Status.WorkspaceTemplateStatus.Ready = true
191195
controlPlane.Status.FailureReason = nil
192196
controlPlane.Status.FailureMessage = nil
193197
controlPlane.Status.WorkspaceTemplateStatus.LastFailureMessage = ""
194198

195-
if workspaceApply.Status.LastAppliedTime != nil {
199+
if workspaceApply != nil && workspaceApply.Status.LastAppliedTime != nil {
196200
controlPlane.Status.WorkspaceTemplateStatus.LastAppliedRevision = workspaceApply.Status.LastAppliedTime.String()
197201
}
198202

0 commit comments

Comments
 (0)