Skip to content

Commit

Permalink
Merge pull request #28491 from deads2k/proof-ssa
Browse files Browse the repository at this point in the history
NO-JIRA: add simple test for server side apply to demonstrate field clearing
  • Loading branch information
openshift-merge-bot[bot] committed Mar 23, 2024
2 parents 734b389 + 1eb5134 commit 49de0ae
Show file tree
Hide file tree
Showing 3 changed files with 281 additions and 0 deletions.
247 changes: 247 additions & 0 deletions test/extended/operators/server_side_apply_examples.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
package operators

import (
"context"

applycorev1 "k8s.io/client-go/applyconfigurations/core/v1"

corev1 "k8s.io/api/core/v1"
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
imageutils "k8s.io/kubernetes/test/utils/image"

applyconfigv1 "github.com/openshift/client-go/config/applyconfigurations/config/v1"

configv1 "github.com/openshift/api/config/v1"

"github.com/davecgh/go-spew/spew"
g "github.com/onsi/ginkgo/v2"
o "github.com/onsi/gomega"
exutil "github.com/openshift/origin/test/extended/util"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/test/e2e/framework"
admissionapi "k8s.io/pod-security-admission/api"
)

var _ = g.Describe("[sig-apimachinery]", func() {

defer g.GinkgoRecover()

oc := exutil.NewCLIWithPodSecurityLevel("server-side-apply-examples", admissionapi.LevelPrivileged)
fieldManager := metav1.ApplyOptions{
FieldManager: "e2e=test",
}

g.Describe("server-side-apply should function properly", func() {
g.It("should clear fields when they are no longer being applied on CRDs", func() {
ctx := context.Background()
isMicroShift, err := exutil.IsMicroShiftCluster(oc.AdminKubeClient())
o.Expect(err).NotTo(o.HaveOccurred())
if isMicroShift {
g.Skip("microshift lacks the API")
}

_, err = oc.AdminConfigClient().ConfigV1().ClusterOperators().Create(ctx, &configv1.ClusterOperator{
ObjectMeta: metav1.ObjectMeta{
Name: "test-instance",
},
}, metav1.CreateOptions{})
o.Expect(err).NotTo(o.HaveOccurred())
defer oc.AdminConfigClient().ConfigV1().ClusterOperators().Delete(ctx, "test-instance", metav1.DeleteOptions{})

addFirstCondition := applyconfigv1.ClusterOperator("test-instance").
WithStatus(applyconfigv1.ClusterOperatorStatus().
WithConditions(applyconfigv1.ClusterOperatorStatusCondition().
WithType("FirstType").
WithStatus(configv1.ConditionTrue).
WithReason("Dummy").
WithMessage("No Value").
WithLastTransitionTime(metav1.Now()),
),
)
_, err = oc.AdminConfigClient().ConfigV1().ClusterOperators().ApplyStatus(ctx, addFirstCondition, fieldManager)
o.Expect(err).NotTo(o.HaveOccurred())

currInstance, err := oc.AdminConfigClient().ConfigV1().ClusterOperators().Get(ctx, "test-instance", metav1.GetOptions{})
o.Expect(err).NotTo(o.HaveOccurred())
if !containsCondition(currInstance.Status.Conditions, "FirstType") {
framework.Logf("got conditions: %v", spew.Sdump(currInstance.Status.Conditions))
g.Fail("missing FirstType condition")
}

addJustSecondCondition := applyconfigv1.ClusterOperator("test-instance").
WithStatus(applyconfigv1.ClusterOperatorStatus().
WithConditions(applyconfigv1.ClusterOperatorStatusCondition().
WithType("SecondType").
WithStatus(configv1.ConditionTrue).
WithReason("Dummy").
WithMessage("No Value").
WithLastTransitionTime(metav1.Now()),
),
)
_, err = oc.AdminConfigClient().ConfigV1().ClusterOperators().ApplyStatus(ctx, addJustSecondCondition, fieldManager)
o.Expect(err).NotTo(o.HaveOccurred())

currInstance, err = oc.AdminConfigClient().ConfigV1().ClusterOperators().Get(ctx, "test-instance", metav1.GetOptions{})
o.Expect(err).NotTo(o.HaveOccurred())
if !containsCondition(currInstance.Status.Conditions, "SecondType") {
g.Fail("missing SecondType condition")
}
if containsCondition(currInstance.Status.Conditions, "FirstType") {
g.Fail("has FirstType condition unexpectedly")
}
})

g.It("should clear fields when they are no longer being applied in FeatureGates", func() {
ctx := context.Background()
isSelfManagedHA, err := exutil.IsSelfManagedHA(ctx, oc.AdminConfigClient())
o.Expect(err).NotTo(o.HaveOccurred())
isSingleNode, err := exutil.IsSelfManagedHA(ctx, oc.AdminConfigClient())
o.Expect(err).NotTo(o.HaveOccurred())
if !isSelfManagedHA && !isSingleNode {
g.Skip("only SelfManagedHA and SingleNode have mutable FeatureGates")
}

addFirstCondition := applyconfigv1.FeatureGate("cluster").
WithStatus(applyconfigv1.FeatureGateStatus().
WithConditions(
metav1.Condition{
Type: "FirstType",
Status: metav1.ConditionTrue,
LastTransitionTime: metav1.Now(),
Reason: "Dummy",
Message: "No Value",
},
),
)
_, err = oc.AdminConfigClient().ConfigV1().FeatureGates().ApplyStatus(ctx, addFirstCondition, fieldManager)
o.Expect(err).NotTo(o.HaveOccurred())

currInstance, err := oc.AdminConfigClient().ConfigV1().FeatureGates().Get(ctx, "cluster", metav1.GetOptions{})
o.Expect(err).NotTo(o.HaveOccurred())
if !containsMetaCondition(currInstance.Status.Conditions, "FirstType") {
framework.Logf("got conditions: %v", spew.Sdump(currInstance.Status.Conditions))
g.Fail("missing FirstType condition")
}

addJustSecondCondition := applyconfigv1.FeatureGate("cluster").
WithStatus(applyconfigv1.FeatureGateStatus().
WithConditions(
metav1.Condition{
Type: "SecondType",
Status: metav1.ConditionTrue,
LastTransitionTime: metav1.Now(),
Reason: "Dummy",
Message: "No Value",
},
),
)
_, err = oc.AdminConfigClient().ConfigV1().FeatureGates().ApplyStatus(ctx, addJustSecondCondition, fieldManager)
o.Expect(err).NotTo(o.HaveOccurred())

currInstance, err = oc.AdminConfigClient().ConfigV1().FeatureGates().Get(ctx, "cluster", metav1.GetOptions{})
o.Expect(err).NotTo(o.HaveOccurred())
if !containsMetaCondition(currInstance.Status.Conditions, "SecondType") {
g.Fail("missing SecondType condition")
}
if containsMetaCondition(currInstance.Status.Conditions, "FirstType") {
g.Fail("has FirstType condition unexpectedly")
}
})

g.It("should clear fields when they are no longer being applied in built-in APIs", func() {
ctx := context.Background()

_, err := oc.AdminKubeClient().CoreV1().Pods(oc.Namespace()).Create(ctx, pausePod("test-instance"), metav1.CreateOptions{})
o.Expect(err).NotTo(o.HaveOccurred())
defer oc.AdminKubeClient().CoreV1().Pods(oc.Namespace()).Delete(ctx, "test-instance", metav1.DeleteOptions{})

addFirstCondition := applycorev1.Pod("test-instance", oc.Namespace()).
WithStatus(applycorev1.PodStatus().
WithConditions(applycorev1.PodCondition().
WithType("FirstType").
WithStatus(corev1.ConditionTrue).
WithReason("Dummy").
WithMessage("No Value").
WithLastTransitionTime(metav1.Now()),
),
)
_, err = oc.AdminKubeClient().CoreV1().Pods(oc.Namespace()).ApplyStatus(ctx, addFirstCondition, fieldManager)
o.Expect(err).NotTo(o.HaveOccurred())

currInstance, err := oc.AdminKubeClient().CoreV1().Pods(oc.Namespace()).Get(ctx, "test-instance", metav1.GetOptions{})
o.Expect(err).NotTo(o.HaveOccurred())
if !containsPodCondition(currInstance.Status.Conditions, "FirstType") {
framework.Logf("got conditions: %v", spew.Sdump(currInstance.Status.Conditions))
g.Fail("missing FirstType condition")
}

addJustSecondCondition := applycorev1.Pod("test-instance", oc.Namespace()).
WithStatus(applycorev1.PodStatus().
WithConditions(applycorev1.PodCondition().
WithType("SecondType").
WithStatus(corev1.ConditionTrue).
WithReason("Dummy").
WithMessage("No Value").
WithLastTransitionTime(metav1.Now()),
),
)
_, err = oc.AdminKubeClient().CoreV1().Pods(oc.Namespace()).ApplyStatus(ctx, addJustSecondCondition, fieldManager)
o.Expect(err).NotTo(o.HaveOccurred())

currInstance, err = oc.AdminKubeClient().CoreV1().Pods(oc.Namespace()).Get(ctx, "test-instance", metav1.GetOptions{})
o.Expect(err).NotTo(o.HaveOccurred())
if !containsPodCondition(currInstance.Status.Conditions, "SecondType") {
g.Fail("missing SecondType condition")
}
if containsPodCondition(currInstance.Status.Conditions, "FirstType") {
g.Fail("has FirstType condition unexpectedly")
}
})
})
})

func containsCondition(podConditions []configv1.ClusterOperatorStatusCondition, name string) bool {
for _, curr := range podConditions {
if string(curr.Type) == name {
return true
}
}
return false
}

func containsMetaCondition(podConditions []metav1.Condition, name string) bool {
for _, curr := range podConditions {
if string(curr.Type) == name {
return true
}
}
return false
}

func containsPodCondition(podConditions []corev1.PodCondition, name string) bool {
for _, curr := range podConditions {
if string(curr.Type) == name {
return true
}
}
return false
}

func pausePod(name string) *corev1.Pod {
return &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: corev1.PodSpec{
SecurityContext: e2epod.GetRestrictedPodSecurityContext(),
Containers: []corev1.Container{
{
Name: "pause-container",
Image: imageutils.GetPauseImageName(),
SecurityContext: e2epod.GetRestrictedContainerSecurityContext(),
},
},
},
}

}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions test/extended/util/framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import (
operatorv1 "github.com/openshift/api/operator/v1"
securityv1 "github.com/openshift/api/security/v1"
buildv1clienttyped "github.com/openshift/client-go/build/clientset/versioned/typed/build/v1"
clientconfigv1 "github.com/openshift/client-go/config/clientset/versioned"
configclient "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
imagev1typedclient "github.com/openshift/client-go/image/clientset/versioned/typed/image/v1"
"github.com/openshift/library-go/pkg/build/naming"
Expand Down Expand Up @@ -2141,6 +2142,33 @@ func IsNamespaceExist(kubeClient *kubernetes.Clientset, namespace string) (bool,
return true, nil
}

func IsSelfManagedHA(ctx context.Context, configClient clientconfigv1.Interface) (bool, error) {
infrastructure, err := configClient.ConfigV1().Infrastructures().Get(ctx, "cluster", metav1.GetOptions{})
if err != nil {
return false, nil
}

return infrastructure.Status.ControlPlaneTopology == configv1.HighlyAvailableTopologyMode, nil
}

func IsSingleNode(ctx context.Context, configClient clientconfigv1.Interface) (bool, error) {
infrastructure, err := configClient.ConfigV1().Infrastructures().Get(ctx, "cluster", metav1.GetOptions{})
if err != nil {
return false, nil
}

return infrastructure.Status.ControlPlaneTopology == configv1.SingleReplicaTopologyMode, nil
}

func IsHypershift(ctx context.Context, configClient clientconfigv1.Interface) (bool, error) {
infrastructure, err := configClient.ConfigV1().Infrastructures().Get(ctx, "cluster", metav1.GetOptions{})
if err != nil {
return false, nil
}

return infrastructure.Status.ControlPlaneTopology == configv1.ExternalTopologyMode, nil
}

// IsMicroShiftCluster returns "true" if a cluster is MicroShift,
// "false" otherwise. It needs kube-admin client as input.
func IsMicroShiftCluster(kubeClient k8sclient.Interface) (bool, error) {
Expand Down

0 comments on commit 49de0ae

Please sign in to comment.