<?xml version="1.0" encoding="utf-8"?><testsuites name="pytest tests"><testsuite name="pytest" errors="0" failures="5" skipped="0" tests="9" time="400.894" timestamp="2026-09-15T13:32:44.323804+00:00" hostname="maas-group-test-l8rk2-e2e-maas-openshift-pod"><testcase classname="tests.test_api_keys.TestAPIKeySubscriptionPhases" name="test_create_key_for_pending_subscription" time="92.584"><failure message="TimeoutError: MaaSSubscription e2e-apikey-pending-sub did not reach phase 'Active' within 90s (current: phase=None, modelRefStatuses=0)">self = &lt;test_api_keys.TestAPIKeySubscriptionPhases object at 0x7ffa2bf6a940&gt;

    @pytest.mark.serial
    def test_create_key_for_pending_subscription(self):
        """API key creation succeeds for Pending subscription."""
        ns = _ns()
        subscription_name = "e2e-apikey-pending-sub"
        auth_name = "e2e-apikey-pending-auth"
        sa_name = "e2e-apikey-pending-sa"
    
        try:
            oc_token = _create_sa_token(sa_name, namespace=MODEL_NAMESPACE)
            sa_user = _sa_to_user(sa_name, namespace=MODEL_NAMESPACE)
    
            _create_test_auth_policy(auth_name, MODEL_REF, users=[sa_user])
            _create_test_subscription(subscription_name, MODEL_REF, users=[sa_user])
&gt;           _wait_for_maas_subscription_phase(subscription_name, namespace=ns)

test/e2e/tests/test_api_keys.py:1665: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

name = 'e2e-apikey-pending-sub', expected_phase = 'Active'
namespace = 'models-as-a-service', timeout = 90, require_model_statuses = False

    def _wait_for_maas_subscription_phase(name, expected_phase="Active", namespace=None, timeout=MAAS_SUBSCRIPTION_PHASE_TIMEOUT, require_model_statuses=False):
        """Wait for MaaSSubscription to reach a specific phase.
    
        Args:
            name: Name of the MaaSSubscription
            expected_phase: Phase to wait for (default: "Active")
            namespace: Namespace (defaults to _ns())
            timeout: Maximum wait time in seconds (default: 60)
            require_model_statuses: If True, also requires modelRefStatuses to be populated
                                    (default: False). Set to True for status reporting tests.
    
        Returns:
            The subscription CR dict when the expected phase is reached
    
        Raises:
            TimeoutError: If MaaSSubscription doesn't reach expected phase within timeout
        """
        namespace = namespace or _ns()
        deadline = time.time() + timeout
        log.info(f"Waiting for MaaSSubscription {name} to reach phase '{expected_phase}' (timeout: {timeout}s)...")
    
        while time.time() &lt; deadline:
            cr = _get_cr("maassubscription", name, namespace)
            if cr:
                status = cr.get("status", {})
                phase = status.get("phase")
                model_statuses = status.get("modelRefStatuses", [])
    
                if phase == expected_phase:
                    if require_model_statuses:
                        expected_count = len(cr.get("spec", {}).get("modelRefs", []))
                        if len(model_statuses) &gt;= expected_count:
                            log.info(f"MaaSSubscription {name} reached phase '{expected_phase}' with {len(model_statuses)}/{expected_count} modelRefStatuses")
                            return cr
                    else:
                        log.info(f"MaaSSubscription {name} reached phase '{expected_phase}'")
                        return cr
                log.debug(f"MaaSSubscription {name}: phase={phase}, modelRefStatuses={len(model_statuses)}")
            time.sleep(2)
    
        # Timeout - return current state for debugging
        cr = _get_cr("maassubscription", name, namespace)
        status = cr.get("status", {}) if cr else {}
&gt;       raise TimeoutError(
            f"MaaSSubscription {name} did not reach phase '{expected_phase}' within {timeout}s "
            f"(current: phase={status.get('phase')}, modelRefStatuses={len(status.get('modelRefStatuses', []))})"
        )
E       TimeoutError: MaaSSubscription e2e-apikey-pending-sub did not reach phase 'Active' within 90s (current: phase=None, modelRefStatuses=0)

test/e2e/tests/test_helper.py:1334: TimeoutError</failure></testcase><testcase classname="tests.test_api_keys.TestAPIKeySubscriptionPhases" name="test_reject_key_for_unreconciled_subscription" time="18.667" /><testcase classname="tests.test_negative_security.TestAuthPolicyRemoval" name="test_authpolicy_deletion_revokes_access" time="9.364" /><testcase classname="tests.test_subscription.TestSubscriptionEnforcement" name="test_rate_limit_exhaustion_gets_429" time="91.763"><failure message="TimeoutError: MaaSSubscription e2e-rate-limit-test-subscription did not reach phase 'Active' within 90s (current: phase=None, modelRefStatuses=0)">self = &lt;test_subscription.TestSubscriptionEnforcement object at 0x7ffa2be7cc70&gt;

    @pytest.mark.serial
    def test_rate_limit_exhaustion_gets_429(self):
        """
        Test that a user gets 429 when they actually exceed their token rate limit.
    
        This test creates a dedicated subscription with a very low token limit,
        sends enough requests to exhaust it, and verifies a 429 response.
    
        Uses the unconfigured model to avoid interfering with other tests.
        """
        # Use unconfigured model to isolate this test
        model_ref = UNCONFIGURED_MODEL_REF
        model_path = UNCONFIGURED_MODEL_PATH
    
        # Create unique subscription and auth policy names
        auth_policy_name = "e2e-rate-limit-test-auth"
        subscription_name = "e2e-rate-limit-test-subscription"
    
        # Low limit so we exhaust it quickly. Actual tokens consumed per
        # response are non-deterministic (max_tokens is a ceiling, not exact),
        # so we send enough requests to be confident we hit the limit without
        # asserting exactly when the 429 arrives.
        token_limit = 10
        window = "1m"
        total_requests = 15
    
        try:
            # 1. Create auth policy allowing system:authenticated
            _create_test_auth_policy(
                name=auth_policy_name,
                model_refs=[model_ref],
                groups=["system:authenticated"]
            )
            _wait_for_maas_auth_policy_phase(auth_policy_name, require_enforced=False)
    
            # 2. Create subscription with low token limit
            _create_test_subscription(
                name=subscription_name,
                model_refs=[model_ref],
                groups=["system:authenticated"],
                token_limit=token_limit,
                window=window
            )
&gt;           _wait_for_maas_subscription_phase(subscription_name)

test/e2e/tests/test_subscription.py:594: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

name = 'e2e-rate-limit-test-subscription', expected_phase = 'Active'
namespace = 'models-as-a-service', timeout = 90, require_model_statuses = False

    def _wait_for_maas_subscription_phase(name, expected_phase="Active", namespace=None, timeout=MAAS_SUBSCRIPTION_PHASE_TIMEOUT, require_model_statuses=False):
        """Wait for MaaSSubscription to reach a specific phase.
    
        Args:
            name: Name of the MaaSSubscription
            expected_phase: Phase to wait for (default: "Active")
            namespace: Namespace (defaults to _ns())
            timeout: Maximum wait time in seconds (default: 60)
            require_model_statuses: If True, also requires modelRefStatuses to be populated
                                    (default: False). Set to True for status reporting tests.
    
        Returns:
            The subscription CR dict when the expected phase is reached
    
        Raises:
            TimeoutError: If MaaSSubscription doesn't reach expected phase within timeout
        """
        namespace = namespace or _ns()
        deadline = time.time() + timeout
        log.info(f"Waiting for MaaSSubscription {name} to reach phase '{expected_phase}' (timeout: {timeout}s)...")
    
        while time.time() &lt; deadline:
            cr = _get_cr("maassubscription", name, namespace)
            if cr:
                status = cr.get("status", {})
                phase = status.get("phase")
                model_statuses = status.get("modelRefStatuses", [])
    
                if phase == expected_phase:
                    if require_model_statuses:
                        expected_count = len(cr.get("spec", {}).get("modelRefs", []))
                        if len(model_statuses) &gt;= expected_count:
                            log.info(f"MaaSSubscription {name} reached phase '{expected_phase}' with {len(model_statuses)}/{expected_count} modelRefStatuses")
                            return cr
                    else:
                        log.info(f"MaaSSubscription {name} reached phase '{expected_phase}'")
                        return cr
                log.debug(f"MaaSSubscription {name}: phase={phase}, modelRefStatuses={len(model_statuses)}")
            time.sleep(2)
    
        # Timeout - return current state for debugging
        cr = _get_cr("maassubscription", name, namespace)
        status = cr.get("status", {}) if cr else {}
&gt;       raise TimeoutError(
            f"MaaSSubscription {name} did not reach phase '{expected_phase}' within {timeout}s "
            f"(current: phase={status.get('phase')}, modelRefStatuses={len(status.get('modelRefStatuses', []))})"
        )
E       TimeoutError: MaaSSubscription e2e-rate-limit-test-subscription did not reach phase 'Active' within 90s (current: phase=None, modelRefStatuses=0)

test/e2e/tests/test_helper.py:1334: TimeoutError</failure></testcase><testcase classname="tests.test_subscription.TestSubscriptionEnforcement" name="test_models_endpoint_exempt_from_rate_limiting" time="91.942"><failure message="TimeoutError: MaaSSubscription e2e-models-exempt-test-subscription did not reach phase 'Active' within 90s (current: phase=None, modelRefStatuses=0)">self = &lt;test_subscription.TestSubscriptionEnforcement object at 0x7ffa2be7ceb0&gt;

    @pytest.mark.serial
    def test_models_endpoint_exempt_from_rate_limiting(self):
        """
        Test that /v1/models endpoint remains accessible when token quota is exhausted.
    
        This verifies that users can discover model capabilities even when they've
        used all their inference tokens. The /v1/models endpoint is a discovery/metadata
        endpoint that does not consume tokens and should remain accessible.
    
        Ref: https://issues.redhat.com/browse/RHOAIENG-46770
    
        Test steps:
        1. Create subscription with very low token limit (15 tokens)
        2. Exhaust the limit with inference requests (5 requests × 3 tokens = 15)
        3. Verify inference requests get 429 (rate limited)
        4. Verify /v1/models endpoint still returns 200 (not rate limited)
        """
        # Use unconfigured model to isolate this test
        model_ref = UNCONFIGURED_MODEL_REF
        model_path = UNCONFIGURED_MODEL_PATH
    
        # Create unique subscription and auth policy names
        auth_policy_name = "e2e-models-exempt-test-auth"
        subscription_name = "e2e-models-exempt-test-subscription"
    
        # Very low limit for fast, deterministic test
        # With 3 token limit and max_tokens=1, we're guaranteed to exhaust quota within 5 requests
        # (even if each request uses exactly 1 token: 5 requests &gt; 3 token limit)
        token_limit = 3
        window = "1m"
        max_tokens = 1
    
        try:
            # 1. Create auth policy allowing system:authenticated
            _create_test_auth_policy(
                name=auth_policy_name,
                model_refs=[model_ref],
                groups=["system:authenticated"]
            )
            _wait_for_maas_auth_policy_phase(auth_policy_name, timeout=90, require_auth_policies=False)
    
            # 2. Create subscription with low token limit
            _create_test_subscription(
                name=subscription_name,
                model_refs=[model_ref],
                groups=["system:authenticated"],
                token_limit=token_limit,
                window=window
            )
&gt;           _wait_for_maas_subscription_phase(subscription_name, timeout=90)

test/e2e/tests/test_subscription.py:715: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

name = 'e2e-models-exempt-test-subscription', expected_phase = 'Active'
namespace = 'models-as-a-service', timeout = 90, require_model_statuses = False

    def _wait_for_maas_subscription_phase(name, expected_phase="Active", namespace=None, timeout=MAAS_SUBSCRIPTION_PHASE_TIMEOUT, require_model_statuses=False):
        """Wait for MaaSSubscription to reach a specific phase.
    
        Args:
            name: Name of the MaaSSubscription
            expected_phase: Phase to wait for (default: "Active")
            namespace: Namespace (defaults to _ns())
            timeout: Maximum wait time in seconds (default: 60)
            require_model_statuses: If True, also requires modelRefStatuses to be populated
                                    (default: False). Set to True for status reporting tests.
    
        Returns:
            The subscription CR dict when the expected phase is reached
    
        Raises:
            TimeoutError: If MaaSSubscription doesn't reach expected phase within timeout
        """
        namespace = namespace or _ns()
        deadline = time.time() + timeout
        log.info(f"Waiting for MaaSSubscription {name} to reach phase '{expected_phase}' (timeout: {timeout}s)...")
    
        while time.time() &lt; deadline:
            cr = _get_cr("maassubscription", name, namespace)
            if cr:
                status = cr.get("status", {})
                phase = status.get("phase")
                model_statuses = status.get("modelRefStatuses", [])
    
                if phase == expected_phase:
                    if require_model_statuses:
                        expected_count = len(cr.get("spec", {}).get("modelRefs", []))
                        if len(model_statuses) &gt;= expected_count:
                            log.info(f"MaaSSubscription {name} reached phase '{expected_phase}' with {len(model_statuses)}/{expected_count} modelRefStatuses")
                            return cr
                    else:
                        log.info(f"MaaSSubscription {name} reached phase '{expected_phase}'")
                        return cr
                log.debug(f"MaaSSubscription {name}: phase={phase}, modelRefStatuses={len(model_statuses)}")
            time.sleep(2)
    
        # Timeout - return current state for debugging
        cr = _get_cr("maassubscription", name, namespace)
        status = cr.get("status", {}) if cr else {}
&gt;       raise TimeoutError(
            f"MaaSSubscription {name} did not reach phase '{expected_phase}' within {timeout}s "
            f"(current: phase={status.get('phase')}, modelRefStatuses={len(status.get('modelRefStatuses', []))})"
        )
E       TimeoutError: MaaSSubscription e2e-models-exempt-test-subscription did not reach phase 'Active' within 90s (current: phase=None, modelRefStatuses=0)

test/e2e/tests/test_helper.py:1334: TimeoutError</failure></testcase><testcase classname="tests.test_subscription.TestMultipleSubscriptionsPerModel" name="test_user_in_one_of_two_subscriptions_gets_200" time="0.461" /><testcase classname="tests.test_subscription.TestMultipleAuthPoliciesPerModel" name="test_delete_one_auth_policy_other_still_works" time="0.833" /><testcase classname="tests.test_subscription.TestCascadeDeletion" name="test_delete_subscription_rebuilds_trlp" time="91.198"><failure message="TimeoutError: MaaSSubscription e2e-temp-sub did not reach phase 'Active' within 90s (current: phase=None, modelRefStatuses=0)">self = &lt;test_subscription.TestCascadeDeletion object at 0x7ffa2be839a0&gt;

    @pytest.mark.serial
    def test_delete_subscription_rebuilds_trlp(self):
        """Add a 2nd subscription, delete it -&gt; TRLP rebuilt with only the original."""
        ns = _ns()
        try:
            _apply_cr({
                "apiVersion": "maas.opendatahub.io/v1alpha1",
                "kind": "MaaSSubscription",
                "metadata": {"name": "e2e-temp-sub", "namespace": ns},
                "spec": {
                    "owner": {"groups": [{"name": "system:authenticated"}]},
                    "modelRefs": [{"name": MODEL_REF, "namespace": MODEL_NAMESPACE, "tokenRateLimits": [{"limit": 50, "window": "1m"}]}],
                },
            })
&gt;           _wait_for_maas_subscription_phase("e2e-temp-sub")

test/e2e/tests/test_subscription.py:916: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

name = 'e2e-temp-sub', expected_phase = 'Active'
namespace = 'models-as-a-service', timeout = 90, require_model_statuses = False

    def _wait_for_maas_subscription_phase(name, expected_phase="Active", namespace=None, timeout=MAAS_SUBSCRIPTION_PHASE_TIMEOUT, require_model_statuses=False):
        """Wait for MaaSSubscription to reach a specific phase.
    
        Args:
            name: Name of the MaaSSubscription
            expected_phase: Phase to wait for (default: "Active")
            namespace: Namespace (defaults to _ns())
            timeout: Maximum wait time in seconds (default: 60)
            require_model_statuses: If True, also requires modelRefStatuses to be populated
                                    (default: False). Set to True for status reporting tests.
    
        Returns:
            The subscription CR dict when the expected phase is reached
    
        Raises:
            TimeoutError: If MaaSSubscription doesn't reach expected phase within timeout
        """
        namespace = namespace or _ns()
        deadline = time.time() + timeout
        log.info(f"Waiting for MaaSSubscription {name} to reach phase '{expected_phase}' (timeout: {timeout}s)...")
    
        while time.time() &lt; deadline:
            cr = _get_cr("maassubscription", name, namespace)
            if cr:
                status = cr.get("status", {})
                phase = status.get("phase")
                model_statuses = status.get("modelRefStatuses", [])
    
                if phase == expected_phase:
                    if require_model_statuses:
                        expected_count = len(cr.get("spec", {}).get("modelRefs", []))
                        if len(model_statuses) &gt;= expected_count:
                            log.info(f"MaaSSubscription {name} reached phase '{expected_phase}' with {len(model_statuses)}/{expected_count} modelRefStatuses")
                            return cr
                    else:
                        log.info(f"MaaSSubscription {name} reached phase '{expected_phase}'")
                        return cr
                log.debug(f"MaaSSubscription {name}: phase={phase}, modelRefStatuses={len(model_statuses)}")
            time.sleep(2)
    
        # Timeout - return current state for debugging
        cr = _get_cr("maassubscription", name, namespace)
        status = cr.get("status", {}) if cr else {}
&gt;       raise TimeoutError(
            f"MaaSSubscription {name} did not reach phase '{expected_phase}' within {timeout}s "
            f"(current: phase={status.get('phase')}, modelRefStatuses={len(status.get('modelRefStatuses', []))})"
        )
E       TimeoutError: MaaSSubscription e2e-temp-sub did not reach phase 'Active' within 90s (current: phase=None, modelRefStatuses=0)

test/e2e/tests/test_helper.py:1334: TimeoutError</failure></testcase><testcase classname="tests.test_subscription.TestCascadeDeletion" name="test_trlp_persists_during_multi_subscription_deletion" time="0.865"><failure message="subprocess.CalledProcessError: Command '['oc', 'apply', '-f', '-']' returned non-zero exit status 1.">self = &lt;test_subscription.TestCascadeDeletion object at 0x7ffa2be83be0&gt;

    @pytest.mark.serial
    def test_trlp_persists_during_multi_subscription_deletion(self):
        """Validate CWE-693/CWE-400 fix: TRLP rebuilt in-place during deletion.
    
        Tests the fix for the security vulnerability where deleting one subscription
        would delete the entire TokenRateLimitPolicy, disabling rate limiting for
        ALL subscriptions to that model and creating a window for unthrottled requests.
    
        The fix ensures:
        1. TRLP is rebuilt in-place when a subscription is deleted (not deleted entirely)
        2. TRLP contains only remaining subscriptions after deletion
        3. TRLP is deleted only when no subscriptions remain
    
        This prevents the rate-limit protection gap (CWE-693: Protection Mechanism
        Failure, CWE-400: Uncontrolled Resource Consumption).
        """
        ns = _ns()
        trlp_ns = MODEL_NAMESPACE
        trlp_name = TRLP_NAME
    
        # Snapshot original subscription for restoration
        original_sub = _snapshot_cr("maassubscription", SIMULATOR_SUBSCRIPTION, ns)
        assert original_sub, f"Pre-existing {SIMULATOR_SUBSCRIPTION} not found"
    
        try:
            # Step 1: Create a second subscription for the same model
            log.info("Creating second subscription for the same model...")
&gt;           _apply_cr({
                "apiVersion": "maas.opendatahub.io/v1alpha1",
                "kind": "MaaSSubscription",
                "metadata": {"name": "e2e-second-sub", "namespace": ns},
                "spec": {
                    "owner": {"groups": [{"name": "system:authenticated"}]},
                    "modelRefs": [{
                        "name": MODEL_REF,
                        "namespace": MODEL_NAMESPACE,
                        "tokenRateLimits": [{"limit": 75, "window": "1m"}]
                    }],
                },
            })

test/e2e/tests/test_subscription.py:952: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
test/e2e/tests/test_helper.py:620: in _apply_cr
    subprocess.run(["oc", "apply", "-f", "-"], input=json.dumps(cr_dict), capture_output=True, text=True, check=True)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

input = '{"apiVersion": "maas.opendatahub.io/v1alpha1", "kind": "MaaSSubscription", "metadata": {"name": "e2e-second-sub", "na...": [{"name": "facebook-opt-125m-simulated", "namespace": "llm", "tokenRateLimits": [{"limit": 75, "window": "1m"}]}]}}'
capture_output = True, timeout = None, check = True
popenargs = (['oc', 'apply', '-f', '-'],)
kwargs = {'stderr': -1, 'stdin': -1, 'stdout': -1, 'text': True}
process = &lt;Popen: returncode: 1 args: ['oc', 'apply', '-f', '-']&gt;, stdout = ''
stderr = 'Error from server (InternalError): error when creating "STDIN": Internal error occurred: failed calling webhook "vmaa...ahub-io-v1alpha1-maassubscription?timeout=10s": no endpoints available for service "maas-controller-webhook-service"\n'
retcode = 1

    def run(*popenargs,
            input=None, capture_output=False, timeout=None, check=False, **kwargs):
        """Run command with arguments and return a CompletedProcess instance.
    
        The returned instance will have attributes args, returncode, stdout and
        stderr. By default, stdout and stderr are not captured, and those attributes
        will be None. Pass stdout=PIPE and/or stderr=PIPE in order to capture them.
    
        If check is True and the exit code was non-zero, it raises a
        CalledProcessError. The CalledProcessError object will have the return code
        in the returncode attribute, and output &amp; stderr attributes if those streams
        were captured.
    
        If timeout is given, and the process takes too long, a TimeoutExpired
        exception will be raised.
    
        There is an optional argument "input", allowing you to
        pass bytes or a string to the subprocess's stdin.  If you use this argument
        you may not also use the Popen constructor's "stdin" argument, as
        it will be used internally.
    
        By default, all communication is in bytes, and therefore any "input" should
        be bytes, and the stdout and stderr will be bytes. If in text mode, any
        "input" should be a string, and stdout and stderr will be strings decoded
        according to locale encoding, or by "encoding" if set. Text mode is
        triggered by setting any of text, encoding, errors or universal_newlines.
    
        The other arguments are the same as for the Popen constructor.
        """
        if input is not None:
            if kwargs.get('stdin') is not None:
                raise ValueError('stdin and input arguments may not both be used.')
            kwargs['stdin'] = PIPE
    
        if capture_output:
            if kwargs.get('stdout') is not None or kwargs.get('stderr') is not None:
                raise ValueError('stdout and stderr arguments may not be used '
                                 'with capture_output.')
            kwargs['stdout'] = PIPE
            kwargs['stderr'] = PIPE
    
        with Popen(*popenargs, **kwargs) as process:
            try:
                stdout, stderr = process.communicate(input, timeout=timeout)
            except TimeoutExpired as exc:
                process.kill()
                if _mswindows:
                    # Windows accumulates the output in a single blocking
                    # read() call run on child threads, with the timeout
                    # being done in a join() on those threads.  communicate()
                    # _after_ kill() is required to collect that and add it
                    # to the exception.
                    exc.stdout, exc.stderr = process.communicate()
                else:
                    # POSIX _communicate already populated the output so
                    # far into the TimeoutExpired exception.
                    process.wait()
                raise
            except:  # Including KeyboardInterrupt, communicate handled that.
                process.kill()
                # We don't call process.wait() as .__exit__ does that for us.
                raise
            retcode = process.poll()
            if check and retcode:
&gt;               raise CalledProcessError(retcode, process.args,
                                         output=stdout, stderr=stderr)
E               subprocess.CalledProcessError: Command '['oc', 'apply', '-f', '-']' returned non-zero exit status 1.

/usr/lib64/python3.9/subprocess.py:528: CalledProcessError</failure></testcase></testsuite></testsuites>