diff --git a/pkg/ocm/policylist.go b/pkg/ocm/policylist.go index f92aa1b6f..3238fdc84 100644 --- a/pkg/ocm/policylist.go +++ b/pkg/ocm/policylist.go @@ -3,9 +3,11 @@ package ocm import ( "context" "fmt" + "time" "github.com/golang/glog" "github.com/openshift-kni/eco-goinfra/pkg/clients" + "k8s.io/apimachinery/pkg/util/wait" policiesv1 "open-cluster-management.io/governance-policy-propagator/api/v1" runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -67,3 +69,62 @@ func ListPoliciesInAllNamespaces(apiClient *clients.Settings, return policyObjects, nil } + +// WaitForAllPoliciesComplianceState wait up to timeout until all policies have complianceState. Policies are listed +// with options on every poll and then these policies have their compliance state checked. +func WaitForAllPoliciesComplianceState( + apiClient *clients.Settings, + complianceState policiesv1.ComplianceState, + timeout time.Duration, + options ...runtimeclient.ListOptions) error { + if apiClient == nil { + glog.V(100).Info("Policies 'apiClient' parameter cannot be nil") + + return fmt.Errorf("failed to wait for policies compliance state, 'apiClient' parameter is nil") + } + + err := apiClient.AttachScheme(policiesv1.AddToScheme) + if err != nil { + glog.V(100).Info("Failed to add Policy scheme to client schemes") + + return err + } + + logMessage := fmt.Sprintf("Waiting up to %s until policies have compliance state %s", timeout, complianceState) + passedOptions := runtimeclient.ListOptions{} + + if len(options) > 1 { + glog.V(100).Infof("'options' parameter must be empty or single-valued") + + return fmt.Errorf("error: more than one ListOptions was passed") + } + + if len(options) == 1 { + passedOptions = options[0] + logMessage += fmt.Sprintf(", listing with the options %v", passedOptions) + } + + glog.V(100).Info(logMessage) + + return wait.PollUntilContextTimeout( + context.TODO(), time.Second, timeout, true, func(ctx context.Context) (bool, error) { + policies, err := ListPoliciesInAllNamespaces(apiClient, passedOptions) + if err != nil { + glog.V(100).Infof("Failed to list policies while waiting for compliance state: %v", err) + + return false, nil + } + + for _, policy := range policies { + policyComplianceState := policy.Definition.Status.ComplianceState + if policyComplianceState != complianceState { + glog.V(100).Infof("Policy %s in namespace %s has compliance state %s, not %s", + policy.Definition.Name, policy.Definition.Namespace, policyComplianceState, complianceState) + + return false, nil + } + } + + return true, nil + }) +} diff --git a/pkg/ocm/policylist_test.go b/pkg/ocm/policylist_test.go index 431abf2b6..40290469e 100644 --- a/pkg/ocm/policylist_test.go +++ b/pkg/ocm/policylist_test.go @@ -1,12 +1,16 @@ package ocm import ( + "context" "fmt" "testing" + "time" "github.com/openshift-kni/eco-goinfra/pkg/clients" "github.com/stretchr/testify/assert" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + policiesv1 "open-cluster-management.io/governance-policy-propagator/api/v1" runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -69,3 +73,60 @@ func TestListPoliciesInAllNamespaces(t *testing.T) { } } } + +func TestWaitForAllPoliciesComplianceState(t *testing.T) { + testCases := []struct { + compliant bool + client bool + listOptions []runtimeclient.ListOptions + expectedError error + }{ + { + compliant: true, + client: true, + listOptions: nil, + expectedError: nil, + }, + { + compliant: false, + client: true, + listOptions: nil, + expectedError: context.DeadlineExceeded, + }, + { + compliant: true, + client: false, + listOptions: nil, + expectedError: fmt.Errorf("failed to wait for policies compliance state, 'apiClient' parameter is nil"), + }, + { + compliant: true, + client: true, + listOptions: []runtimeclient.ListOptions{ + {LabelSelector: labels.NewSelector()}, + {LabelSelector: labels.NewSelector()}, + }, + expectedError: fmt.Errorf("error: more than one ListOptions was passed"), + }, + } + + for _, testCase := range testCases { + var testSettings *clients.Settings + + if testCase.client { + policy := buildDummyPolicy(defaultPolicyName, defaultPolicyNsName) + + if testCase.compliant { + policy.Status.ComplianceState = policiesv1.Compliant + } + + testSettings = clients.GetTestClients(clients.TestClientParams{ + K8sMockObjects: []runtime.Object{policy}, + SchemeAttachers: policyTestSchemes, + }) + } + + err := WaitForAllPoliciesComplianceState(testSettings, policiesv1.Compliant, time.Second, testCase.listOptions...) + assert.Equal(t, testCase.expectedError, err) + } +}