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  }