예제 #1
0
    def test_patch_many(self, patch_many_dict, bg_patch, bg_patch2, kwargs):
        """Parametrize for the 'many' kwarg because the parser should ignore it"""
        patches = SchemaParser.parse_patch(patch_many_dict, **kwargs)
        sorted_patches = sorted(patches, key=lambda x: x.operation)

        for index, patch in enumerate([bg_patch, bg_patch2]):
            assert_patch_equal(patch, sorted_patches[index])
예제 #2
0
    async def patch(self):
        """
        ---
        summary: Initiate administrative actions
        description: |
          The body of the request needs to contain a set of instructions
          detailing the operations to perform.

          Currently the supported operations are `rescan`:
          ```JSON
          [
            { "operation": "rescan" }
          ]
          ```
          * Will remove from the registry and database any currently stopped
            plugins who's directory has been removed.
          * Will add and start any new plugin directories.

          And reloading the plugin logging configuration:
          ```JSON
          [
            {
              "operation": "reload",
              "path": "/config/logging/plugin"
            }
          ]
          ```
        parameters:
          - name: patch
            in: body
            required: true
            description: Instructions for operations
            schema:
              $ref: '#/definitions/Patch'
        responses:
          204:
            description: Operation successfully initiated
          50x:
            $ref: '#/definitions/50xError'
        tags:
          - Admin
        """
        self.verify_user_permission_for_object(GARDEN_UPDATE, local_garden())

        operations = SchemaParser.parse_patch(
            self.request.decoded_body, many=True, from_string=True
        )

        for op in operations:
            if op.operation == "rescan":
                await self.client(Operation(operation_type="RUNNER_RESCAN"))
            elif op.operation == "reload":
                if op.path == "/config/logging/plugin":
                    await self.client(Operation(operation_type="PLUGIN_LOG_RELOAD"))
                else:
                    raise ModelValidationError(f"Unsupported path '{op.path}'")
            else:
                raise ModelValidationError(f"Unsupported operation '{op.operation}'")

        self.set_status(204)
예제 #3
0
    async def patch(self):
        """
        ---
        summary: Partially update a Garden
        description: |
          The body of the request needs to contain a set of instructions detailing the
          updates to apply. Currently the only operations are:

          * sync

          ```JSON
          [
            { "operation": "" }
          ]
          ```
        parameters:
          - name: garden_name
            in: path
            required: true
            description: Garden to use
            type: string
          - name: patch
            in: body
            required: true
            description: Instructions for how to update the Garden
            schema:
              $ref: '#/definitions/Patch'
        responses:
          200:
            description: Execute Patch action against Gardens
            schema:
              $ref: '#/definitions/Garden'
          400:
            $ref: '#/definitions/400Error'
          404:
            $ref: '#/definitions/404Error'
          50x:
            $ref: '#/definitions/50xError'
        tags:
          - Garden
        """

        patch = SchemaParser.parse_patch(self.request.decoded_body, from_string=True)

        for op in patch:
            operation = op.operation.lower()

            if operation == "sync":
                response = await self.client(
                    Operation(
                        operation_type="GARDEN_SYNC",
                    )
                )

            else:
                raise ModelValidationError(f"Unsupported operation '{op.operation}'")

        self.set_header("Content-Type", "application/json; charset=UTF-8")
        self.write(response)
예제 #4
0
    def test_patch_serialized_start(self, patch_dict_no_envelop):
        """Patches are always parsed into a list, so they need a tweak to test"""
        serialized = SchemaParser.serialize(
            SchemaParser.parse_patch(patch_dict_no_envelop, from_string=False),
            to_string=False,
        )

        assert len(serialized) == 1
        assert serialized[0] == patch_dict_no_envelop
예제 #5
0
    async def patch(self):
        """
        ---
        summary: Update runners
        description: |
          The body of the request needs to contain a set of instructions detailing the
          updates to apply. Currently the only operations are:

          * reload

          ```JSON
          [
            { "operation": "reload", "path": "echo-3.0.0" }
          ]
          ```
        parameters:
          - name: patch
            in: body
            required: true
            description: Instructions for how to update the Runner
            schema:
              $ref: '#/definitions/Patch'
        responses:
          200:
            description: Reloaded Runners
            schema:
              $ref: '#/definitions/Runner'
          400:
            $ref: '#/definitions/400Error'
          404:
            $ref: '#/definitions/404Error'
          50x:
            $ref: '#/definitions/50xError'
        tags:
          - Runners
        """
        patch = SchemaParser.parse_patch(self.request.decoded_body,
                                         from_string=True)

        for op in patch:
            operation = op.operation.lower()

            if operation == "reload":
                response = await self.client(
                    Operation(operation_type="RUNNER_RELOAD",
                              kwargs={"path": op.path}))

            else:
                raise ModelValidationError(
                    f"Unsupported operation '{op.operation}'")

        self.set_header("Content-Type", "application/json; charset=UTF-8")
        self.write(response)
예제 #6
0
    async def patch(self):
        """
        ---
        summary: Reload the plugin logging configuration
        deprecated: true
        description: |
          The body of the request needs to contain a set of instructions detailing the
          operation to make. Currently supported operations are below:
          ```JSON
          { "operation": "reload" }
          ```
        parameters:
          - name: patch
            in: body
            required: true
            description: Operation to perform
            schema:
              $ref: '#/definitions/Patch'
        responses:
          200:
            description: Updated plugin logging configuration
            schema:
              $ref: '#/definitions/LoggingConfig'
          50x:
            $ref: '#/definitions/50xError'
        tags:
          - Deprecated
        """
        self.verify_user_permission_for_object(GARDEN_UPDATE, local_garden())

        patch = SchemaParser.parse_patch(self.request.decoded_body,
                                         many=True,
                                         from_string=True)

        response = None
        for op in patch:
            if op.operation == "reload":
                response = await self.client(
                    Operation(operation_type="PLUGIN_LOG_RELOAD"))
            else:
                raise ModelValidationError(
                    f"Unsupported operation '{op.operation}'")

        self.set_header("Content-Type", "application/json; charset=UTF-8")
        self.write(response)
예제 #7
0
    async def patch(self, instance_id):
        """
        ---
        summary: Partially update an Instance
        description: |
          The body of the request needs to contain a set of instructions detailing the
          updates to apply. Currently the only operations are:

          * initialize
          * start
          * stop
          * heartbeat
          * replace

          ```JSON
          [
            { "operation": "" }
          ]
          ```
        parameters:
          - name: instance_id
            in: path
            required: true
            description: The ID of the Instance
            type: string
          - name: patch
            in: body
            required: true
            description: Instructions for how to update the Instance
            schema:
              $ref: '#/definitions/Patch'
        responses:
          200:
            description: Instance with the given ID
            schema:
              $ref: '#/definitions/Instance'
          400:
            $ref: '#/definitions/400Error'
          404:
            $ref: '#/definitions/404Error'
          50x:
            $ref: '#/definitions/50xError'
        tags:
          - Instances
        """
        _ = self.get_or_raise(System, SYSTEM_UPDATE, instances__id=instance_id)

        patch = SchemaParser.parse_patch(self.request.decoded_body,
                                         from_string=True)

        for op in patch:
            operation = op.operation.lower()

            if operation == "initialize":
                runner_id = None
                if op.value:
                    runner_id = op.value.get("runner_id")

                response = await self.client(
                    Operation(
                        operation_type="INSTANCE_INITIALIZE",
                        args=[instance_id],
                        kwargs={"runner_id": runner_id},
                    ))

            elif operation == "start":
                response = await self.client(
                    Operation(operation_type="INSTANCE_START",
                              args=[instance_id]))

            elif operation == "stop":
                response = await self.client(
                    Operation(operation_type="INSTANCE_STOP",
                              args=[instance_id]))

            elif operation == "heartbeat":
                response = await self.client(
                    Operation(operation_type="INSTANCE_HEARTBEAT",
                              args=[instance_id]))

            elif operation == "replace":
                if op.path.lower() == "/status":

                    response = await self.client(
                        Operation(
                            operation_type="INSTANCE_UPDATE",
                            args=[instance_id],
                            kwargs={"new_status": op.value},
                        ))
                else:
                    raise ModelValidationError(f"Unsupported path '{op.path}'")

            elif operation == "update":
                if op.path.lower() == "/metadata":
                    response = await self.client(
                        Operation(
                            operation_type="INSTANCE_UPDATE",
                            args=[instance_id],
                            kwargs={"metadata": op.value},
                        ))
                else:
                    raise ModelValidationError(f"Unsupported path '{op.path}'")

            else:
                raise ModelValidationError(
                    f"Unsupported operation '{op.operation}'")

        self.set_header("Content-Type", "application/json; charset=UTF-8")
        self.write(response)
예제 #8
0
    async def patch(self, system_id):
        """
        ---
        summary: Partially update a System
        description: |
          The body of the request needs to contain a set of instructions detailing the
          updates to apply.
          Currently supported operations are below:
          ```JSON
          [
            { "operation": "add", "path": "/instance", "value": "" },
            { "operation": "replace", "path": "/commands", "value": "" },
            { "operation": "replace", "path": "/description", "value": "new description"},
            { "operation": "replace", "path": "/display_name", "value": "new display name"},
            { "operation": "replace", "path": "/icon_name", "value": "new icon name"},
            { "operation": "update", "path": "/metadata", "value": {"foo": "bar"}}
          ]
          ```
          Where `value` is a list of new Commands.
        parameters:
          - name: system_id
            in: path
            required: true
            description: The ID of the System
            type: string
          - name: patch
            in: body
            required: true
            description: Instructions for how to update the System
            schema:
              $ref: '#/definitions/Patch'
        responses:
          200:
            description: System with the given ID
            schema:
              $ref: '#/definitions/System'
          400:
            $ref: '#/definitions/400Error'
          404:
            $ref: '#/definitions/404Error'
          50x:
            $ref: '#/definitions/50xError'
        tags:
          - Systems
        """
        _ = self.get_or_raise(System, SYSTEM_UPDATE, id=system_id)

        kwargs = {}
        do_reload = False

        response = ""

        for op in SchemaParser.parse_patch(self.request.decoded_body,
                                           from_string=True):
            if op.operation == "replace":
                if op.path == "/commands":
                    kwargs["new_commands"] = SchemaParser.parse_command(
                        op.value, many=True)
                elif op.path in [
                        "/description",
                        "/icon_name",
                        "/display_name",
                        "/template",
                ]:
                    kwargs[op.path.strip("/")] = op.value
                else:
                    raise ModelValidationError(
                        f"Unsupported path for replace '{op.path}'")

            elif op.operation == "add":
                if op.path == "/instance":
                    if not kwargs.get("add_instances"):
                        kwargs["add_instances"] = []

                    kwargs["add_instances"].append(
                        SchemaParser.parse_instance(op.value))
                else:
                    raise ModelValidationError(
                        f"Unsupported path for add '{op.path}'")

            elif op.operation == "update":
                if op.path == "/metadata":
                    kwargs["metadata"] = op.value
                else:
                    raise ModelValidationError(
                        f"Unsupported path for update '{op.path}'")

            elif op.operation == "reload":
                do_reload = True

            else:
                raise ModelValidationError(
                    f"Unsupported operation '{op.operation}'")

        if kwargs:
            response = await self.client(
                Operation(operation_type="SYSTEM_UPDATE",
                          args=[system_id],
                          kwargs=kwargs))

        if do_reload:
            await self.client(
                Operation(operation_type="SYSTEM_RELOAD", args=[system_id]))

        self.set_header("Content-Type", "application/json; charset=UTF-8")
        self.write(response)
예제 #9
0
    async def patch(self, request_id):
        """
        ---
        summary: Partially update a Request
        description: |
          The body of the request needs to contain a set of instructions detailing the
          updates to apply. Currently the only operation supported is `replace`, with
          paths `/status`, `/output`, and `/error_class`:
          ```JSON
          [
            { "operation": "replace", "path": "/status", "value": "" },
            { "operation": "replace", "path": "/output", "value": "" },
            { "operation": "replace", "path": "/error_class", "value": "" }
          ]
          ```
        parameters:
          - name: request_id
            in: path
            required: true
            description: The ID of the Request
            type: string
          - name: patch
            in: body
            required: true
            description: Instructions for how to update the Request
            schema:
              $ref: '#/definitions/Patch'
        responses:
          200:
            description: Request with the given ID
            schema:
              $ref: '#/definitions/Request'
          400:
            $ref: '#/definitions/400Error'
          404:
            $ref: '#/definitions/404Error'
          50x:
            $ref: '#/definitions/50xError'
        tags:
          - Requests
        """
        _ = self.get_or_raise(Request, REQUEST_UPDATE, id=request_id)

        operation = Operation(args=[request_id])
        patch = SchemaParser.parse_patch(self.request.decoded_body, from_string=True)

        for op in patch:
            if op.operation == "replace":
                if op.path == "/status":

                    # If we get a start just assume there's no other op in patch
                    if op.value.upper() == "IN_PROGRESS":
                        operation.operation_type = "REQUEST_START"
                        operation.kwargs = {}
                        break

                    elif op.value.upper() in BrewtilsRequest.COMPLETED_STATUSES:
                        operation.operation_type = "REQUEST_COMPLETE"
                        operation.kwargs["status"] = op.value

                    else:
                        raise ModelValidationError(
                            f"Unsupported status value '{op.value}'"
                        )

                elif op.path == "/output":
                    operation.kwargs["output"] = op.value

                elif op.path == "/error_class":
                    operation.kwargs["error_class"] = op.value

                else:
                    raise ModelValidationError(f"Unsupported path '{op.path}'")
            else:
                raise ModelValidationError(f"Unsupported operation '{op.operation}'")

        response = await self.client(operation)

        self.set_header("Content-Type", "application/json; charset=UTF-8")
        self.write(response)
예제 #10
0
    async def patch(self, garden_name):
        """
        ---
        summary: Partially update a Garden
        description: |
          The body of the request needs to contain a set of instructions detailing the
          updates to apply. Currently the only operations are:

          * initializing
          * running
          * stopped
          * block
          * update

          ```JSON
          [
            { "operation": "" }
          ]
          ```
        parameters:
          - name: garden_name
            in: path
            required: true
            description: Garden to use
            type: string
          - name: patch
            in: body
            required: true
            description: Instructions for how to update the Garden
            schema:
              $ref: '#/definitions/Patch'
        responses:
          200:
            description: Garden with the given garden_name
            schema:
              $ref: '#/definitions/Garden'
          400:
            $ref: '#/definitions/400Error'
          404:
            $ref: '#/definitions/404Error'
          50x:
            $ref: '#/definitions/50xError'
        tags:
          - Garden
        """

        patch = SchemaParser.parse_patch(self.request.decoded_body, from_string=True)

        for op in patch:
            operation = op.operation.lower()

            if operation in ["initializing", "running", "stopped", "block"]:
                response = await self.client(
                    Operation(
                        operation_type="GARDEN_UPDATE_STATUS",
                        args=[garden_name, operation.upper()],
                    )
                )
            elif operation == "heartbeat":
                response = await self.client(
                    Operation(
                        operation_type="GARDEN_UPDATE_STATUS",
                        args=[garden_name, "RUNNING"],
                    )
                )
            elif operation == "config":
                response = await self.client(
                    Operation(
                        operation_type="GARDEN_UPDATE_CONFIG",
                        args=[SchemaParser.parse_garden(op.value, from_string=False)],
                    )
                )
            elif operation == "sync":
                response = await self.client(
                    Operation(
                        operation_type="GARDEN_SYNC",
                        kwargs={"sync_target": garden_name},
                    )
                )

            else:
                raise ModelValidationError(f"Unsupported operation '{op.operation}'")

        self.set_header("Content-Type", "application/json; charset=UTF-8")
        self.write(response)
예제 #11
0
 def test_patch(self, bg_patch, data, kwargs):
     """Parametrize for the 'many' kwarg because the parser should ignore it"""
     assert_patch_equal(
         SchemaParser.parse_patch(data, **kwargs)[0], bg_patch)
예제 #12
0
def test_parse_patch_many(patch_many_dict, bg_patch1, bg_patch2):
    parser = SchemaParser()
    patches = sorted(parser.parse_patch(patch_many_dict, many=True),
                     key=lambda x: x.operation)
    for index, patch in enumerate([bg_patch1, bg_patch2]):
        assert_patch_equal(patch, patches[index])
예제 #13
0
def test_parse_patch(bg_patch1, data, kwargs):
    parser = SchemaParser()
    actual = parser.parse_patch(data, **kwargs)[0]
    assert_patch_equal(actual, bg_patch1)
예제 #14
0
    async def patch(self, job_id):
        """
        ---
        summary: Pause/Resume a job
        description: |
          The body of the request needs to contain a set of instructions
          detailing the actions to take. Currently the only operation
          supported is `update` with `path` of `/status`.


          You can pause a job with:
          ```JSON
          { "operation": "update", "path": "/status", "value": "PAUSED" }
          ```

          And resume it with:
          ```JSON
          { "operation": "update", "path": "/status", "value": "RUNNING" }
          ```
        parameters:
          - name: job_id
            in: path
            required: true
            description: The ID of the Job
            type: string
          - name: patch
            in: body
            required: true
            description: Instructions for the actions to take
            schema:
              $ref: '#/definitions/Patch'
        responses:
          200:
            description: Job with the given ID
            schema:
              $ref: '#/definitions/Job'
          400:
            $ref: '#/definitions/400Error'
          404:
            $ref: '#/definitions/404Error'
          50x:
            $ref: '#/definitions/50xError'
        tags:
          - Jobs
        """

        patch = SchemaParser.parse_patch(self.request.decoded_body,
                                         from_string=True)

        for op in patch:
            if op.operation == "update":
                if op.path == "/status":
                    if str(op.value).upper() == "PAUSED":
                        response = await self.client(
                            Operation(operation_type="JOB_PAUSE",
                                      args=[job_id]))
                    elif str(op.value).upper() == "RUNNING":
                        response = await self.client(
                            Operation(operation_type="JOB_RESUME",
                                      args=[job_id]))
                    else:
                        raise ModelValidationError(
                            f"Unsupported status value '{op.value}'")
                elif op.path == "/job":
                    response = await self.client(
                        Operation(
                            operation_type="JOB_UPDATE",
                            args=[SchemaParser.parse_job(op.value)],
                        ))
                else:
                    raise ModelValidationError(
                        f"Unsupported path value '{op.path}'")
            else:
                raise ModelValidationError(
                    f"Unsupported operation '{op.operation}'")

        self.set_header("Content-Type", "application/json; charset=UTF-8")
        self.write(response)
예제 #15
0
    async def patch(self):
        """
        ---
        summary: Partially update a Garden
        description: |
          The body of the request needs to contain a set of instructions detailing the
          updates to apply. Currently the only operations are:

          * sync
          * sync_users

          ```JSON
          [
            { "operation": "" }
          ]
          ```
        parameters:
          - name: garden_name
            in: path
            required: true
            description: Garden to use
            type: string
          - name: patch
            in: body
            required: true
            description: Instructions for how to update the Garden
            schema:
              $ref: '#/definitions/Patch'
        responses:
          204:
            description: Patch operation has been successfully forwarded
          400:
            $ref: '#/definitions/400Error'
          404:
            $ref: '#/definitions/404Error'
          50x:
            $ref: '#/definitions/50xError'
        tags:
          - Garden
        """
        self.verify_user_permission_for_object(GARDEN_UPDATE, local_garden())

        patch = SchemaParser.parse_patch(self.request.decoded_body,
                                         from_string=True)

        for op in patch:
            operation = op.operation.lower()

            if operation == "sync":
                await self.client(Operation(operation_type="GARDEN_SYNC", ))
            elif operation == "sync_users":
                # requires GARDEN_UPDATE for all gardens
                for garden in Garden.objects.all():
                    self.verify_user_permission_for_object(
                        GARDEN_UPDATE, garden)

                initiate_user_sync()
            else:
                raise ModelValidationError(
                    f"Unsupported operation '{op.operation}'")

        self.set_status(204)
예제 #16
0
    async def patch(self, runner_id):
        """
        ---
        summary: Partially update a Runner
        description: |
          The body of the request needs to contain a set of instructions detailing the
          updates to apply. Currently the only operations are:

          * start
          * stop

          ```JSON
          [
            { "operation": "" }
          ]
          ```
        parameters:
          - name: runner_id
            in: path
            required: true
            description: The ID of the Instance
            type: string
          - name: patch
            in: body
            required: true
            description: Instructions for how to update the Runner
            schema:
              $ref: '#/definitions/Patch'
        responses:
          200:
            description: Runner with the given ID
            schema:
              $ref: '#/definitions/Runner'
          400:
            $ref: '#/definitions/400Error'
          404:
            $ref: '#/definitions/404Error'
          50x:
            $ref: '#/definitions/50xError'
        tags:
          - Runners
        """
        patch = SchemaParser.parse_patch(self.request.decoded_body,
                                         from_string=True)

        for op in patch:
            operation = op.operation.lower()

            if operation == "start":
                response = await self.client(
                    Operation(operation_type="RUNNER_START",
                              kwargs={"runner_id": runner_id}))

            elif operation == "stop":
                response = await self.client(
                    Operation(
                        operation_type="RUNNER_STOP",
                        kwargs={"runner_id": runner_id},
                    ))

            else:
                raise ModelValidationError(
                    f"Unsupported operation '{op.operation}'")

        self.set_header("Content-Type", "application/json; charset=UTF-8")
        self.write(response)