Пример #1
0
def test_url_for_a_list_of_namespaced_custom_subresources_clusterwide():
    resource = Resource('group', 'version', 'plural', namespaced=True)
    with pytest.raises(ValueError) as err:
        resource.get_url(namespace=None, subresource='status')
    assert str(
        err.value
    ) == "Subresources can be used only with specific resources by their name."
Пример #2
0
def test_url_for_a_list_of_namespaced_corev1_subresources_in_a_namespace():
    resource = Resource('', 'v1', 'plural', namespaced=True)
    with pytest.raises(ValueError) as err:
        resource.get_url(namespace='ns-a.b', subresource='status')
    assert str(
        err.value
    ) == "Subresources can be used only with specific resources by their name."
Пример #3
0
def test_url_for_a_list_of_clusterscoped_corev1_subresources_clusterwide():
    resource = Resource('', 'v1', 'plural', namespaced=False)
    with pytest.raises(ValueError) as err:
        resource.get_url(namespace=None, subresource='status')
    assert str(
        err.value
    ) == "Subresources can be used only with specific resources by their name."
Пример #4
0
def test_url_for_a_specific_clusterscoped_corev1_subresource_in_a_namespace():
    resource = Resource('', 'v1', 'plural', namespaced=False)
    with pytest.raises(ValueError) as err:
        resource.get_url(namespace='ns', name='name-a.b', subresource='status')
    assert str(
        err.value
    ) == "Specific namespaces are not supported for cluster-scoped resources."
Пример #5
0
def test_url_for_a_specific_namespaced_corev1_subresource_clusterwide():
    resource = Resource('', 'v1', 'plural', namespaced=True)
    with pytest.raises(ValueError) as err:
        resource.get_url(namespace=None, name='name-a.b', subresource='status')
    assert str(
        err.value
    ) == "Specific namespaces are required for specific namespaced resources."
Пример #6
0
def test_url_for_a_specific_namespaced_custom_resource_clusterwide():
    resource = Resource('group', 'version', 'plural', namespaced=True)
    with pytest.raises(ValueError) as err:
        resource.get_url(namespace=None, name='name-a.b')
    assert str(
        err.value
    ) == "Specific namespaces are required for specific namespaced resources."
Пример #7
0
def test_url_for_a_specific_clusterscoped_custom_resource_in_a_namespace():
    resource = Resource('group', 'version', 'plural', namespaced=False)
    with pytest.raises(ValueError) as err:
        resource.get_url(namespace='ns', name='name-a.b')
    assert str(
        err.value
    ) == "Specific namespaces are not supported for cluster-scoped resources."
Пример #8
0
def test_url_for_a_list_of_clusterscoped_corev1_subresources_in_a_namespace():
    resource = Resource('', 'v1', 'plural', namespaced=False)
    with pytest.raises(ValueError) as err:
        resource.get_url(namespace='ns', subresource='status')
    assert str(err.value) in {
        "Specific namespaces are not supported for cluster-scoped resources.",
        "Subresources can be used only with specific resources by their name.",
    }
Пример #9
0
async def create_obj(
        *,
        resource: references.Resource,
        namespace: references.Namespace = None,
        name: Optional[str] = None,
        body: Optional[bodies.RawBody] = None,
        context: Optional[auth.APIContext] = None,  # injected by the decorator
) -> Optional[bodies.RawBody]:
    """
    Create a resource.
    """
    if context is None:
        raise RuntimeError("API instance is not injected by the decorator.")

    body = body if body is not None else {}
    if namespace is not None:
        body.setdefault('metadata', {}).setdefault('namespace', namespace)
    if name is not None:
        body.setdefault('metadata', {}).setdefault('name', name)

    namespace = cast(references.Namespace,
                     body.get('metadata', {}).get('namespace'))
    response = await context.session.post(
        url=resource.get_url(server=context.server, namespace=namespace),
        json=body,
    )
    created_body: bodies.RawBody = await errors.parse_response(response)
    return created_body
Пример #10
0
async def watch_objs(
    *,
    settings: configuration.OperatorSettings,
    resource: references.Resource,
    namespace: references.Namespace,
    timeout: Optional[float] = None,
    since: Optional[str] = None,
    context: Optional[auth.APIContext] = None,  # injected by the decorator
    freeze_waiter: aiotasks.Future,
) -> AsyncIterator[bodies.RawInput]:
    """
    Watch objects of a specific resource type.

    The cluster-scoped call is used in two cases:

    * The resource itself is cluster-scoped, and namespacing makes not sense.
    * The operator serves all namespaces for the namespaced custom resource.

    Otherwise, the namespace-scoped call is used:

    * The resource is namespace-scoped AND operator is namespaced-restricted.
    """
    if context is None:
        raise RuntimeError("API instance is not injected by the decorator.")

    params: Dict[str, str] = {}
    params['watch'] = 'true'
    if since is not None:
        params['resourceVersion'] = since
    if timeout is not None:
        params['timeoutSeconds'] = str(timeout)

    # Stream the parsed events from the response until it is closed server-side,
    # or until it is closed client-side by the freeze-waiting future's callbacks.
    try:
        response = await context.session.get(
            url=resource.get_url(server=context.server,
                                 namespace=namespace,
                                 params=params),
            timeout=aiohttp.ClientTimeout(
                total=settings.watching.client_timeout,
                sock_connect=settings.watching.connect_timeout,
            ),
        )
        await errors.check_response(response)

        response_close_callback = lambda _: response.close()
        freeze_waiter.add_done_callback(response_close_callback)
        try:
            async with response:
                async for line in _iter_jsonlines(response.content):
                    raw_input = cast(bodies.RawInput,
                                     json.loads(line.decode("utf-8")))
                    yield raw_input
        finally:
            freeze_waiter.remove_done_callback(response_close_callback)

    except (aiohttp.ClientConnectionError, aiohttp.ClientPayloadError,
            asyncio.TimeoutError):
        pass
Пример #11
0
async def list_objs_rv(
        *,
        resource: references.Resource,
        namespace: references.Namespace,
        context: Optional[auth.APIContext] = None,  # injected by the decorator
) -> Tuple[Collection[bodies.RawBody], str]:
    """
    List the objects of specific resource type.

    The cluster-scoped call is used in two cases:

    * The resource itself is cluster-scoped, and namespacing makes not sense.
    * The operator serves all namespaces for the namespaced custom resource.

    Otherwise, the namespace-scoped call is used:

    * The resource is namespace-scoped AND operator is namespaced-restricted.
    """
    if context is None:
        raise RuntimeError("API instance is not injected by the decorator.")

    url = resource.get_url(server=context.server, namespace=namespace)
    rsp = await errors.parse_response(await context.session.get(url))

    items: List[bodies.RawBody] = []
    resource_version = rsp.get('metadata', {}).get('resourceVersion', None)
    for item in rsp['items']:
        if 'kind' in rsp:
            item.setdefault('kind', rsp['kind'][:-4] if rsp['kind'][-4:] == 'List' else rsp['kind'])
        if 'apiVersion' in rsp:
            item.setdefault('apiVersion', rsp['apiVersion'])
        items.append(item)

    return items, resource_version
Пример #12
0
def test_url_of_builtin_resource_list_namespaced_with_subresource():
    resource = Resource('', 'v1', 'plural')
    with pytest.raises(ValueError):
        resource.get_url(namespace='ns-a.b', subresource='status')
Пример #13
0
def test_url_for_a_specific_clusterscoped_corev1_subresource_clusterwide():
    resource = Resource('', 'v1', 'plural', namespaced=False)
    url = resource.get_url(namespace=None,
                           name='name-a.b',
                           subresource='status')
    assert url == '/api/v1/plural/name-a.b/status'
Пример #14
0
def test_url_of_custom_resource_list_cluster_scoped():
    resource = Resource('group', 'version', 'plural')
    url = resource.get_url()
    assert url == '/apis/group/version/plural'
Пример #15
0
def test_url_of_custom_resource_item_cluster_scoped():
    resource = Resource('group', 'version', 'plural')
    url = resource.get_url(name='name-a.b')
    assert url == '/apis/group/version/plural/name-a.b'
Пример #16
0
def test_url_for_a_specific_clusterscoped_custom_resource_clusterwide():
    resource = Resource('group', 'version', 'plural', namespaced=False)
    url = resource.get_url(namespace=None, name='name-a.b')
    assert url == '/apis/group/version/plural/name-a.b'
Пример #17
0
def test_url_for_a_list_of_namespaced_custom_resources_clusterwide():
    resource = Resource('group', 'version', 'plural', namespaced=True)
    url = resource.get_url(namespace=None)
    assert url == '/apis/group/version/plural'
Пример #18
0
def test_url_with_arbitrary_params():
    resource = Resource('group', 'version', 'plural')
    url = resource.get_url(
        params=dict(watch='true', resourceVersion='abc%def xyz'))
    assert url == '/apis/group/version/plural?watch=true&resourceVersion=abc%25def+xyz'
Пример #19
0
def test_url_of_custom_resource_list_namespaced_with_subresource():
    resource = Resource('group', 'version', 'plural')
    with pytest.raises(ValueError):
        resource.get_url(namespace='ns-a.b', subresource='status')
Пример #20
0
def test_url_for_a_list_of_namespaced_corev1_resources_clusterwide():
    resource = Resource('', 'v1', 'plural', namespaced=True)
    url = resource.get_url(namespace=None)
    assert url == '/api/v1/plural'
Пример #21
0
def test_url_of_builtin_resource_item_cluster_scoped():
    resource = Resource('', 'v1', 'plural')
    url = resource.get_url(name='name-a.b')
    assert url == '/api/v1/plural/name-a.b'
Пример #22
0
async def patch_obj(
        *,
        resource: references.Resource,
        namespace: references.Namespace,
        name: Optional[str],
        patch: patches.Patch,
        context: Optional[auth.APIContext] = None,  # injected by the decorator
) -> Optional[bodies.RawBody]:
    """
    Patch a resource of specific kind.

    Unlike the object listing, the namespaced call is always
    used for the namespaced resources, even if the operator serves
    the whole cluster (i.e. is not namespace-restricted).

    Returns the patched body. The patched body can be partial (status-only,
    no-status, or empty) -- depending on whether there were fields in the body
    or in the status to patch; if neither had fields for patching, the result
    is an empty body. The result should only be used to check against the patch:
    if there was nothing to patch, it does not matter if the fields are absent.

    Returns ``None`` if the underlying object is absent, as detected by trying
    to patch it and failing with HTTP 404. This can happen if the object was
    deleted in the operator's handlers or externally during the processing,
    so that the framework was unaware of these changes until the last moment.
    """
    if context is None:
        raise RuntimeError("API instance is not injected by the decorator.")

    as_subresource = 'status' in resource.subresources
    body_patch = dict(
        patch)  # shallow: for mutation of the top-level keys below.
    status_patch = body_patch.pop('status', None) if as_subresource else None

    # Patch & reconstruct the actual body as reported by the server. The reconstructed body can be
    # partial or empty -- if the body/status patches are empty. This is fine: it is only used
    # to verify that the patched fields are matching the patch. No patch? No mismatch!
    try:
        patched_body = bodies.RawBody()

        if body_patch:
            response = await context.session.patch(
                url=resource.get_url(server=context.server,
                                     namespace=namespace,
                                     name=name),
                headers={'Content-Type': 'application/merge-patch+json'},
                json=body_patch,
            )
            patched_body = await errors.parse_response(response)

        if status_patch:
            response = await context.session.patch(
                url=resource.get_url(
                    server=context.server,
                    namespace=namespace,
                    name=name,
                    subresource='status' if as_subresource else None),
                headers={'Content-Type': 'application/merge-patch+json'},
                json={'status': status_patch},
            )
            patched_body['status'] = (
                await errors.parse_response(response)).get('status')

        return patched_body

    except errors.APINotFoundError:
        return None
Пример #23
0
def test_url_of_builtin_resource_list_cluster_scoped_with_subresource():
    resource = Resource('', 'v1', 'plural')
    with pytest.raises(ValueError):
        resource.get_url(subresource='status')
Пример #24
0
def test_url_of_builtin_resource_list_cluster_scoped():
    resource = Resource('', 'v1', 'plural')
    url = resource.get_url()
    assert url == '/api/v1/plural'
Пример #25
0
def test_url_for_a_specific_namespaced_corev1_subresource_in_a_namespace():
    resource = Resource('', 'v1', 'plural', namespaced=True)
    url = resource.get_url(namespace='ns-a.b',
                           name='name-a.b',
                           subresource='status')
    assert url == '/api/v1/namespaces/ns-a.b/plural/name-a.b/status'
Пример #26
0
def test_url_for_a_specific_namespaced_custom_subresource_in_a_namespace():
    resource = Resource('group', 'version', 'plural', namespaced=True)
    url = resource.get_url(namespace='ns-a.b',
                           name='name-a.b',
                           subresource='status')
    assert url == '/apis/group/version/namespaces/ns-a.b/plural/name-a.b/status'
Пример #27
0
def test_url_for_a_list_of_namespaced_custom_resources_in_a_namespace():
    resource = Resource('group', 'version', 'plural', namespaced=True)
    url = resource.get_url(namespace='ns-a.b')
    assert url == '/apis/group/version/namespaces/ns-a.b/plural'
Пример #28
0
def test_url_of_custom_resource_item_namespaced():
    resource = Resource('group', 'version', 'plural')
    url = resource.get_url(namespace='ns-a.b', name='name-a.b')
    assert url == '/apis/group/version/namespaces/ns-a.b/plural/name-a.b'
Пример #29
0
def test_url_for_a_list_of_namespaced_corev1_resources_in_a_namespace():
    resource = Resource('', 'v1', 'plural', namespaced=True)
    url = resource.get_url(namespace='ns-a.b')
    assert url == '/api/v1/namespaces/ns-a.b/plural'
Пример #30
0
def test_url_of_builtin_resource_item_namespaced():
    resource = Resource('', 'v1', 'plural')
    url = resource.get_url(namespace='ns-a.b', name='name-a.b')
    assert url == '/api/v1/namespaces/ns-a.b/plural/name-a.b'