Skip to content

Commit

Permalink
Fix creation of custom image clusters and correctly handles upgrades …
Browse files Browse the repository at this point in the history
…with CVEs. Fixes #1223
  • Loading branch information
ryanemerson committed Sep 13, 2024
1 parent 0a4b1ed commit 2e57340
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 15 deletions.
14 changes: 8 additions & 6 deletions pkg/reconcile/pipeline/infinispan/handler/manage/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,22 +89,24 @@ func UpgradeRequired(i *ispnv1.Infinispan, ctx pipeline.Context) bool {
return false
}

// If the Operand is marked as a CVE base-image release, then we perform the upgrade as a StatefulSet rolling upgrade
// as the server components are not changed.
customImage := i.Status.Operand.CustomImage
if !customImage && requestedOperand.CVE && installedOperand.UpstreamVersion.EQ(*requestedOperand.UpstreamVersion) {
// If the Operand is marked as a CVE base-image release and no custom image is installed/requested, then we perform the
// upgrade as a StatefulSet rolling upgrade as the server components are not changed.
if requestedOperand.CVE &&
installedOperand.UpstreamVersion.EQ(*requestedOperand.UpstreamVersion) &&
i.Spec.Image == nil &&
!i.Status.Operand.CustomImage {
return false
}

if requestedOperand.EQ(installedOperand) {
if i.Spec.Image == nil {
// If the currently installed Operand was a custom image, but spec.Image is now nil, then we need to
// initiate a new upgrade to ensure that the default image associated with the Operand is installed
return customImage
return i.Status.Operand.CustomImage
}
// If operand versions match, but the FQN of the image differ, then we must schedule an upgrade so the user
// can transition to a custom/patched version of the Operand without having to recreate the Infinispan CR
return *i.Spec.Image != installedOperand.Image
return *i.Spec.Image != i.Status.Operand.Image
}
return true
}
Expand Down
51 changes: 42 additions & 9 deletions test/e2e/infinispan/upgrade_operand_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,28 @@ func TestOperandCVEHotRodRolling(t *testing.T) {
genericTestForContainerUpdated(*spec, modifier, verifier)
}

func TestSpecImage(t *testing.T) {
defer testKube.CleanNamespaceAndLogOnPanic(t, tutils.Namespace)

// Create Infinispan Cluster using the latest Operand version, but using the image of the preceding Operand
// Ensures that creating a cluster with an initial spec.image value does not cause infinite StatefulSet updates
versionManager := tutils.VersionManager()
operand := versionManager.Operands[len(versionManager.Operands)-2]
spec := tutils.DefaultSpec(t, testKube, func(i *ispnv1.Infinispan) {
i.Spec.Image = pointer.String(operand.Image)
})
testKube.CreateInfinispan(spec, tutils.Namespace)
testKube.WaitForInfinispanPods(1, tutils.SinglePodTimeout, spec.Name, tutils.Namespace)
testKube.WaitForInfinispanCondition(spec.Name, spec.Namespace, ispnv1.ConditionWellFormed)
testKube.WaitForInfinispanState(spec.Name, spec.Namespace, func(i *ispnv1.Infinispan) bool {
return i.IsConditionTrue(ispnv1.ConditionWellFormed) &&
i.Status.Operand.CustomImage &&
i.Status.Operand.Version == versionManager.Latest().Ref() &&
i.Status.Operand.Image == operand.Image &&
i.Status.Operand.Phase == ispnv1.OperandPhaseRunning
})
}

func TestSpecImageUpdate(t *testing.T) {
defer testKube.CleanNamespaceAndLogOnPanic(t, tutils.Namespace)

Expand All @@ -237,15 +259,10 @@ func TestSpecImageUpdate(t *testing.T) {
ispn.Spec.Image = pointer.String(customImage)
}),
)
testKube.WaitForInfinispanState(spec.Name, spec.Namespace, func(i *ispnv1.Infinispan) bool {
return !i.IsConditionTrue(ispnv1.ConditionWellFormed) &&
i.Status.Operand.Version == operand.Ref() &&
i.Status.Operand.Image == customImage &&
i.Status.Operand.Phase == ispnv1.OperandPhasePending
})

testKube.WaitForInfinispanState(spec.Name, spec.Namespace, func(i *ispnv1.Infinispan) bool {
return i.IsConditionTrue(ispnv1.ConditionWellFormed) &&
i.Status.Operand.CustomImage &&
i.Status.Operand.Version == operand.Ref() &&
i.Status.Operand.Image == customImage &&
i.Status.Operand.Phase == ispnv1.OperandPhaseRunning
Expand All @@ -272,17 +289,33 @@ func TestSpecImageUpdate(t *testing.T) {
ispn.Spec.Version = latestOperand.Ref()
}),
)

testKube.WaitForInfinispanState(spec.Name, spec.Namespace, func(i *ispnv1.Infinispan) bool {
return !i.IsConditionTrue(ispnv1.ConditionWellFormed) &&
return i.IsConditionTrue(ispnv1.ConditionWellFormed) &&
!i.Status.Operand.CustomImage &&
i.Status.Operand.Version == latestOperand.Ref() &&
i.Status.Operand.Image == latestOperand.Image &&
i.Status.Operand.Phase == ispnv1.OperandPhasePending
i.Status.Operand.Phase == ispnv1.OperandPhaseRunning
})

// Ensure that the StatefulSet is on its first generation, i.e. a RollingUpgrade has not been performed
ss = appsv1.StatefulSet{}
tutils.ExpectNoError(testKube.Kubernetes.Client.Get(context.TODO(), types.NamespacedName{Namespace: ispn.Namespace, Name: ispn.GetStatefulSetName()}, &ss))
assert.EqualValues(t, 1, ss.Status.ObservedGeneration)

tutils.ExpectNoError(
testKube.UpdateInfinispan(ispn, func() {
// Update the spec to move to back to the penultimate Operand version to ensure that an upgrade is still
// triggered when the Operand is marked as CVE=true
ispn.Spec.Image = pointer.String(operand.Image)
}),
)

testKube.WaitForInfinispanState(spec.Name, spec.Namespace, func(i *ispnv1.Infinispan) bool {
return i.IsConditionTrue(ispnv1.ConditionWellFormed) &&
i.Status.Operand.CustomImage &&
i.Status.Operand.Version == latestOperand.Ref() &&
i.Status.Operand.Image == latestOperand.Image &&
i.Status.Operand.Image == operand.Image &&
i.Status.Operand.Phase == ispnv1.OperandPhaseRunning
})

Expand Down

0 comments on commit 2e57340

Please sign in to comment.