* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* installer is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain n copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
******************************************************************/
package componentfactory
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
bkev1beta1 "gopkg.openfuyao.cn/cluster-api-provider-bke/api/capbke/v1beta1"
cvv1alpha1 "gopkg.openfuyao.cn/cluster-api-provider-bke/api/v1alpha1"
"gopkg.openfuyao.cn/cluster-api-provider-bke/pkg/phaseframe"
"gopkg.openfuyao.cn/cluster-api-provider-bke/pkg/phaseframe/phases"
releasemanifest "gopkg.openfuyao.cn/cluster-api-provider-bke/pkg/release/manifest"
"gopkg.openfuyao.cn/cluster-api-provider-bke/pkg/upgrade"
)
func TestRegisterInlinePhasesFromBundle(t *testing.T) {
bundle := testReleaseBundle(t)
f, err := NewFactoryFromBundle(bundle)
if err != nil {
t.Fatal(err)
}
phase, err := f.Resolve(upgrade.InlineHandlerEtcdUpgrade, "v2.0.0", &phaseframe.PhaseContext{})
if err != nil {
t.Fatal(err)
}
if phase == nil {
t.Fatal("expected phase instance")
}
_, err = f.Resolve(upgrade.InlineHandlerEtcdUpgrade, "v9.9.9", &phaseframe.PhaseContext{})
if err == nil {
t.Fatal("expected resolve failure for unregistered handler version")
}
}
func TestRegisterInlinePhasesFromBundle_UnknownHandler(t *testing.T) {
bundle := &releasemanifest.Bundle{
Release: cvv1alpha1.ReleaseImage{
Spec: cvv1alpha1.ReleaseImageSpec{
Upgrade: &cvv1alpha1.ReleaseImageUpgradeSpec{
Components: []cvv1alpha1.ReleaseImageUpgradeComponent{{
Name: "custom",
Version: "v1.0.0",
Inline: &cvv1alpha1.ReleaseImageUpgradeInline{
Handler: "UnknownHandler",
Version: "v1.0.0",
},
}},
},
},
},
Components: map[string]cvv1alpha1.ComponentVersion{},
}
_, err := NewFactoryFromBundle(bundle)
if err == nil {
t.Fatal("expected error for unknown handler")
}
}
func TestRegisterInlinePhasesFromBundle_PreUpgradeResourcesInjectsComponentVersion(t *testing.T) {
bundle := &releasemanifest.Bundle{
Release: cvv1alpha1.ReleaseImage{
Spec: cvv1alpha1.ReleaseImageSpec{
Upgrade: &cvv1alpha1.ReleaseImageUpgradeSpec{
Components: []cvv1alpha1.ReleaseImageUpgradeComponent{{
Name: upgrade.ComponentPreUpgradeResources,
Version: "v1.0.0",
Inline: &cvv1alpha1.ReleaseImageUpgradeInline{
Handler: upgrade.InlineHandlerPreUpgradeResources,
Version: "v1.0.0",
},
}},
},
},
},
Components: map[string]cvv1alpha1.ComponentVersion{
releasemanifest.ComponentKey(upgrade.ComponentPreUpgradeResources, "v1.0.0"): {
Spec: cvv1alpha1.ComponentVersionSpec{
Name: upgrade.ComponentPreUpgradeResources,
Version: "v1.0.0",
Inline: &cvv1alpha1.InlineSpec{
Handler: upgrade.InlineHandlerPreUpgradeResources,
Version: "v1.0.0",
},
Resources: []cvv1alpha1.ResourceSpec{{
Kind: "ConfigMap",
APIVersion: "v1",
Namespace: "kube-system",
Name: "bootstrap-config",
}},
},
},
},
}
f, err := NewFactoryFromBundle(bundle)
if err != nil {
t.Fatal(err)
}
phase, err := f.Resolve(upgrade.InlineHandlerPreUpgradeResources, "v1.0.0", phaseframe.NewReconcilePhaseCtx(context.Background()).SetBKECluster(&bkev1beta1.BKECluster{
ObjectMeta: metav1.ObjectMeta{Name: "cluster", Namespace: "default"},
}))
if err != nil {
t.Fatal(err)
}
prePhase, ok := phase.(*phases.EnsurePreUpgradeResources)
if !ok {
t.Fatalf("expected EnsurePreUpgradeResources, got %T", phase)
}
if prePhase.GetComponentVersion() == nil || len(prePhase.GetComponentVersion().Spec.Resources) != 1 {
t.Fatal("expected injected ComponentVersion resources on pre-upgrade phase")
}
}
func TestRegisterInlinePhasesFromBundle_PreUpgradeResourcesRequiresComponentVersion(t *testing.T) {
bundle := &releasemanifest.Bundle{
Release: cvv1alpha1.ReleaseImage{
Spec: cvv1alpha1.ReleaseImageSpec{
Upgrade: &cvv1alpha1.ReleaseImageUpgradeSpec{
Components: []cvv1alpha1.ReleaseImageUpgradeComponent{{
Name: upgrade.ComponentPreUpgradeResources,
Version: "v1.0.0",
Inline: &cvv1alpha1.ReleaseImageUpgradeInline{
Handler: upgrade.InlineHandlerPreUpgradeResources,
Version: "v1.0.0",
},
}},
},
},
},
Components: map[string]cvv1alpha1.ComponentVersion{},
}
_, err := NewFactoryFromBundle(bundle)
if err == nil {
t.Fatal("expected error when pre-upgrade resources component version is missing")
}
}
func TestRegisterInlinePhasesFromBundle_NilFactory(t *testing.T) {
err := RegisterInlinePhasesFromBundle(nil, testReleaseBundle(t))
require.Error(t, err)
assert.Contains(t, err.Error(), "component factory is nil")
}
func TestRegisterInlinePhasesFromBundle_DeduplicatesAndDefaultsInlineVersion(t *testing.T) {
bundle := &releasemanifest.Bundle{
Release: cvv1alpha1.ReleaseImage{
Spec: cvv1alpha1.ReleaseImageSpec{
Upgrade: &cvv1alpha1.ReleaseImageUpgradeSpec{
Components: []cvv1alpha1.ReleaseImageUpgradeComponent{
{
Name: upgrade.ComponentEtcd,
Version: "v1.2.0",
Inline: &cvv1alpha1.ReleaseImageUpgradeInline{
Handler: upgrade.InlineHandlerEtcdUpgrade,
},
},
{
Name: upgrade.ComponentEtcd,
Version: "v1.2.0",
Inline: &cvv1alpha1.ReleaseImageUpgradeInline{
Handler: upgrade.InlineHandlerEtcdUpgrade,
},
},
},
},
},
},
Components: map[string]cvv1alpha1.ComponentVersion{
releasemanifest.ComponentKey(upgrade.ComponentEtcd, "v1.2.0"): {
Spec: cvv1alpha1.ComponentVersionSpec{
Name: upgrade.ComponentEtcd,
Version: "v1.2.0",
Inline: &cvv1alpha1.InlineSpec{Handler: upgrade.InlineHandlerEtcdUpgrade},
},
},
},
}
f := NewComponentFactory()
require.NoError(t, RegisterInlinePhasesFromBundle(f, bundle))
phase, err := f.Resolve(upgrade.InlineHandlerEtcdUpgrade, upgrade.ComponentManifestVersion, &phaseframe.PhaseContext{})
require.NoError(t, err)
assert.NotNil(t, phase)
}
func TestRegisterInlineHandlerAndResolveInlineUpgradeErrors(t *testing.T) {
f := NewComponentFactory()
require.NoError(t, registerInlineHandler(f, upgrade.InlineHandlerEtcdUpgrade, upgrade.ComponentManifestVersion))
phase, err := ResolveInlineUpgrade(f, upgrade.InlineHandlerEtcdUpgrade, upgrade.ComponentManifestVersion, phaseframe.NewReconcilePhaseCtx(context.Background()).SetBKECluster(&bkev1beta1.BKECluster{}))
require.NoError(t, err)
assert.NotNil(t, phase)
_, err = ResolveInlineUpgrade(nil, upgrade.InlineHandlerEtcdUpgrade, upgrade.ComponentManifestVersion, &phaseframe.PhaseContext{})
require.Error(t, err)
assert.Contains(t, err.Error(), "component factory is nil")
}
func TestRegisterInlineComponentErrors(t *testing.T) {
f := NewComponentFactory()
err := registerInlineComponent(f, upgrade.InlineHandlerPreUpgradeResources, upgrade.ComponentManifestVersion, nil)
require.Error(t, err)
assert.Contains(t, err.Error(), "component version is required")
err = registerInlineComponent(f, upgrade.InlineHandlerAgentUpgrade, upgrade.ComponentManifestVersion, nil)
require.NoError(t, err)
err = registerInlineComponent(f, "UnknownHandler", upgrade.ComponentManifestVersion, nil)
require.Error(t, err)
assert.Contains(t, err.Error(), "unknown inline handler")
}
func TestRegisterInlineComponentSuccessPaths(t *testing.T) {
f := NewComponentFactory()
componentVersion := &cvv1alpha1.ComponentVersion{
Spec: cvv1alpha1.ComponentVersionSpec{
Name: upgrade.ComponentPreUpgradeResources,
Version: "v1.0.0",
Resources: []cvv1alpha1.ResourceSpec{{
Kind: "ConfigMap",
Name: "pre-upgrade-config",
Namespace: "kube-system",
}},
},
}
require.NoError(t, registerInlineComponent(f, upgrade.InlineHandlerPreUpgradeResources, upgrade.ComponentManifestVersion, componentVersion))
require.NoError(t, registerInlineHandler(f, upgrade.InlineHandlerAgentUpgrade, upgrade.ComponentManifestVersion))
phase, err := f.Resolve(upgrade.InlineHandlerPreUpgradeResources, upgrade.ComponentManifestVersion, phaseframe.NewReconcilePhaseCtx(context.Background()).SetBKECluster(&bkev1beta1.BKECluster{}))
require.NoError(t, err)
prePhase, ok := phase.(*phases.EnsurePreUpgradeResources)
require.True(t, ok)
require.NotNil(t, prePhase.GetComponentVersion())
assert.Equal(t, "v1.0.0", prePhase.GetComponentVersion().Spec.Version)
agentPhase, err := f.Resolve(upgrade.InlineHandlerAgentUpgrade, upgrade.ComponentManifestVersion, &phaseframe.PhaseContext{})
require.NoError(t, err)
assert.NotNil(t, agentPhase)
}
func TestRegisterInlineComponentSuccessPaths_AllInlineHandlers(t *testing.T) {
f := NewComponentFactory()
handlers := []string{
upgrade.InlineHandlerEtcdUpgrade,
upgrade.InlineHandlerMasterUpgrade,
upgrade.InlineHandlerWorkerUpgrade,
upgrade.InlineHandlerContainerdUpgrade,
}
ctx := phaseframe.NewReconcilePhaseCtx(context.Background()).SetBKECluster(&bkev1beta1.BKECluster{})
for _, handler := range handlers {
require.NoError(t, registerInlineHandler(f, handler, upgrade.ComponentManifestVersion), handler)
phase, err := ResolveInlineUpgrade(f, handler, upgrade.ComponentManifestVersion, ctx)
require.NoError(t, err, handler)
assert.NotNil(t, phase, handler)
}
}
func TestResolveInlineUpgrade_ResolveError(t *testing.T) {
f := NewComponentFactory()
_, err := ResolveInlineUpgrade(f, upgrade.InlineHandlerEtcdUpgrade, upgrade.ComponentManifestVersion, &phaseframe.PhaseContext{})
require.Error(t, err)
assert.Contains(t, err.Error(), "not found")
}
func testReleaseBundle(t *testing.T) *releasemanifest.Bundle {
t.Helper()
return &releasemanifest.Bundle{
Release: cvv1alpha1.ReleaseImage{
Spec: cvv1alpha1.ReleaseImageSpec{
Upgrade: &cvv1alpha1.ReleaseImageUpgradeSpec{
Components: []cvv1alpha1.ReleaseImageUpgradeComponent{
{Name: upgrade.ComponentEtcd, Version: "v1.2.0"},
{Name: upgrade.ComponentContainerd, Version: "v1.7.0"},
},
},
},
},
Components: map[string]cvv1alpha1.ComponentVersion{
releasemanifest.ComponentKey(upgrade.ComponentEtcd, "v1.2.0"): {
Spec: cvv1alpha1.ComponentVersionSpec{
Name: upgrade.ComponentEtcd,
Version: "v1.2.0",
Inline: &cvv1alpha1.InlineSpec{Handler: upgrade.InlineHandlerEtcdUpgrade, Version: "v2.0.0"},
},
},
releasemanifest.ComponentKey(upgrade.ComponentContainerd, "v1.7.0"): {
Spec: cvv1alpha1.ComponentVersionSpec{
Name: upgrade.ComponentContainerd,
Version: "v1.7.0",
Inline: &cvv1alpha1.InlineSpec{Handler: upgrade.InlineHandlerContainerdUpgrade, Version: "v1.0.0"},
},
},
},
}
}