github.com/nmstate/kubernetes-nmstate@v0.82.0/test/e2e/policy/conditions.go (about) 1 /* 2 Copyright The Kubernetes NMState Authors. 3 4 5 Licensed under the Apache License, Version 2.0 (the "License"); 6 you may not use this file except in compliance with the License. 7 You may obtain a copy of the License at 8 9 http://www.apache.org/licenses/LICENSE-2.0 10 11 Unless required by applicable law or agreed to in writing, software 12 distributed under the License is distributed on an "AS IS" BASIS, 13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 See the License for the specific language governing permissions and 15 limitations under the License. 16 */ 17 18 package policy 19 20 import ( 21 "context" 22 "fmt" 23 "time" 24 25 . "github.com/onsi/gomega" 26 . "github.com/onsi/gomega/gstruct" 27 gomegatypes "github.com/onsi/gomega/types" 28 29 "k8s.io/apimachinery/pkg/types" 30 "sigs.k8s.io/yaml" 31 32 corev1 "k8s.io/api/core/v1" 33 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 34 35 "github.com/nmstate/kubernetes-nmstate/api/shared" 36 nmstatev1 "github.com/nmstate/kubernetes-nmstate/api/v1" 37 nmstatev1beta1 "github.com/nmstate/kubernetes-nmstate/api/v1beta1" 38 testenv "github.com/nmstate/kubernetes-nmstate/test/env" 39 ) 40 41 const ( 42 ReadTimeout = 180 * time.Second 43 ReadInterval = 1 * time.Second 44 TestPolicy = "test-policy" 45 ) 46 47 func EnactmentsStatusToYaml() string { 48 enactmentsStatus := IndexEnactmentStatusByName() 49 manifest, err := yaml.Marshal(enactmentsStatus) 50 Expect(err).ToNot(HaveOccurred()) 51 return string(manifest) 52 } 53 54 func NodeNetworkConfigurationEnactment(key types.NamespacedName) nmstatev1beta1.NodeNetworkConfigurationEnactment { 55 enactment := nmstatev1beta1.NodeNetworkConfigurationEnactment{} 56 Eventually(func() error { 57 return testenv.Client.Get(context.TODO(), key, &enactment) 58 }, ReadTimeout, ReadInterval).ShouldNot(HaveOccurred()) 59 return enactment 60 } 61 62 func IndexEnactmentStatusByName() map[string]shared.NodeNetworkConfigurationEnactmentStatus { 63 enactmentList := nmstatev1beta1.NodeNetworkConfigurationEnactmentList{} 64 Eventually(func() error { 65 return testenv.Client.List(context.TODO(), &enactmentList) 66 }, ReadTimeout, ReadInterval).ShouldNot(HaveOccurred()) 67 enactmentStatusByName := map[string]shared.NodeNetworkConfigurationEnactmentStatus{} 68 for _, enactment := range enactmentList.Items { 69 enactmentStatusByName[enactment.Name] = enactment.Status 70 } 71 return enactmentStatusByName 72 } 73 74 func EnactmentConditionsStatus(node, policy string) shared.ConditionList { 75 return NodeNetworkConfigurationEnactment(shared.EnactmentKey(node, policy)).Status.Conditions 76 } 77 78 func EnactmentConditionsStatusForPolicyEventually(node string, policy string) AsyncAssertion { 79 return Eventually(func() shared.ConditionList { 80 return EnactmentConditionsStatus(node, policy) 81 }, 180*time.Second, 1*time.Second) 82 } 83 84 func EnactmentConditionsStatusForPolicyConsistently(node, policy string) AsyncAssertion { 85 return Consistently(func() shared.ConditionList { 86 return EnactmentConditionsStatus(node, policy) 87 }, 5*time.Second, 1*time.Second) 88 } 89 90 func EnactmentConditionsStatusEventually(node string) AsyncAssertion { 91 return EnactmentConditionsStatusForPolicyEventually(node, TestPolicy) 92 } 93 94 func EnactmentConditionsStatusConsistently(node string) AsyncAssertion { 95 return EnactmentConditionsStatusForPolicyConsistently(node, TestPolicy) 96 } 97 98 // Status In case a condition does not exist create with Unknown type, this way 99 // is easier to just use gomega matchers to check in a homogenous way that 100 // condition is not present or unknown. 101 func Status(policyName string) shared.ConditionList { 102 policy := NodeNetworkConfigurationPolicy(policyName) 103 conditions := shared.ConditionList{} 104 for _, policyConditionType := range shared.NodeNetworkConfigurationPolicyConditionTypes { 105 condition := policy.Status.Conditions.Find(policyConditionType) 106 if condition == nil { 107 condition = &shared.Condition{ 108 Type: policyConditionType, 109 Status: corev1.ConditionUnknown, 110 } 111 } 112 conditions = append(conditions, *condition) 113 } 114 return conditions 115 } 116 117 func NodeNetworkConfigurationPolicy(policyName string) nmstatev1.NodeNetworkConfigurationPolicy { 118 key := types.NamespacedName{Name: policyName} 119 policy := nmstatev1.NodeNetworkConfigurationPolicy{} 120 EventuallyWithOffset(1, func() error { 121 return testenv.Client.Get(context.TODO(), key, &policy) 122 }, ReadTimeout, ReadInterval).ShouldNot(HaveOccurred()) 123 return policy 124 } 125 126 func StatusForPolicyEventually(policy string) AsyncAssertion { 127 return Eventually(func() shared.ConditionList { 128 return Status(policy) 129 }, 480*time.Second, 1*time.Second) 130 } 131 132 func StatusForPolicyConsistently(policy string) AsyncAssertion { 133 return Consistently(func() shared.ConditionList { 134 return Status(policy) 135 }, 5*time.Second, 1*time.Second) 136 } 137 138 func StatusEventually() AsyncAssertion { 139 return StatusForPolicyEventually(TestPolicy) 140 } 141 142 func StatusConsistently() AsyncAssertion { 143 return StatusForPolicyConsistently(TestPolicy) 144 } 145 146 func ContainPolicyAvailable() gomegatypes.GomegaMatcher { 147 return ContainElement(MatchFields(IgnoreExtras, Fields{ 148 "Type": Equal(shared.NodeNetworkConfigurationPolicyConditionAvailable), 149 "Status": Equal(corev1.ConditionTrue), 150 "Reason": Not(BeEmpty()), 151 "Message": Not(BeEmpty()), 152 })) 153 } 154 155 func ContainPolicyDegraded() gomegatypes.GomegaMatcher { 156 return ContainElement(MatchFields(IgnoreExtras, Fields{ 157 "Type": Equal(shared.NodeNetworkConfigurationPolicyConditionDegraded), 158 "Status": Equal(corev1.ConditionTrue), 159 "Reason": Not(BeEmpty()), 160 "Message": Not(BeEmpty()), 161 })) 162 } 163 164 func WaitForPolicyTransitionUpdateWithTime(policy string, applyTime time.Time) { 165 // the k8s times at status are rounded to seconds 166 roundedApplyTime := metav1.NewTime(applyTime).Rfc3339Copy().Time 167 EventuallyWithOffset(1, func() time.Time { 168 availableCondition := Status(policy).Find(shared.NodeNetworkConfigurationPolicyConditionAvailable) 169 return availableCondition.LastTransitionTime.Time 170 }, 4*time.Minute, 5*time.Second).Should(BeTemporally(">=", roundedApplyTime), 171 fmt.Sprintf("Policy %s should have updated transition time", policy)) 172 } 173 174 func WaitForPolicyTransitionUpdate(policy string) { 175 WaitForPolicyTransitionUpdateWithTime(policy, time.Now()) 176 } 177 178 func WaitForAvailableTestPolicy() { 179 WaitForAvailablePolicy(TestPolicy) 180 } 181 182 func WaitForDegradedTestPolicy() { 183 WaitForDegradedPolicy(TestPolicy) 184 } 185 186 func WaitForAvailablePolicy(policy string) { 187 WaitForPolicy(policy, ContainPolicyAvailable()) 188 } 189 190 func WaitForDegradedPolicy(policy string) { 191 WaitForPolicy(policy, ContainPolicyDegraded()) 192 } 193 194 func WaitForPolicy(policy string, matcher gomegatypes.GomegaMatcher) { 195 StatusForPolicyEventually(policy). 196 Should( 197 SatisfyAny(ContainPolicyAvailable(), ContainPolicyDegraded()), 198 func() string { 199 return fmt.Sprintf("should reach terminal status at NNCP '%s', \n current enactments statuses:\n%s", 200 policy, EnactmentsStatusToYaml()) 201 }, 202 ) 203 Expect(Status(policy)).To(matcher, "should reach expected status at NNCP '%s', \n current enactments statuses:\n%s", 204 policy, EnactmentsStatusToYaml()) 205 } 206 207 func filterOutMessageAndTimestampFromConditions(conditions shared.ConditionList) shared.ConditionList { 208 modifiedConditions := shared.ConditionList{} 209 for _, condition := range conditions { 210 modifiedConditions = append(modifiedConditions, shared.Condition{ 211 Type: condition.Type, 212 Status: condition.Status, 213 Reason: condition.Reason, 214 }) 215 } 216 return modifiedConditions 217 } 218 219 func MatchConditionsFrom(conditionsSetter func(*shared.ConditionList, string)) gomegatypes.GomegaMatcher { 220 expectedConditions := shared.ConditionList{} 221 conditionsSetter(&expectedConditions, "") 222 expectedConditions = filterOutMessageAndTimestampFromConditions(expectedConditions) 223 return WithTransform(filterOutMessageAndTimestampFromConditions, ConsistOf(expectedConditions)) 224 }