示例#1
0
 def test_extract_oauth_key_from_auth_header_no_whitespace_rtns_key(self):
     token = factory.make_string(18)
     auth_header = factory.make_oauth_header(oauth_token=token)
     auth_header = auth_header.replace(", ", ",")
     self.assertEqual(
         token, extract_oauth_key_from_auth_header(auth_header)
     )
示例#2
0
 def test_extract_oauth_key_from_auth_header_returns_key(self):
     token = factory.make_string(18)
     self.assertEqual(
         token,
         extract_oauth_key_from_auth_header(
             factory.make_oauth_header(oauth_token=token)),
     )
示例#3
0
 def test_extract_oauth_key_from_auth_header_returns_None_if_missing(self):
     self.assertIs(None, extract_oauth_key_from_auth_header(""))
示例#4
0
    def render_POST(self, request):
        """Receive and process a status message from a node, usually cloud-init

        A node can call this to report progress of its booting or deployment.

        Calling this from a node that is not Allocated, Commissioning, Ready,
        or Failed Tests will update the substatus_message node attribute.
        Signaling completion more than once is not an error; all but the first
        successful call are ignored.

        This method accepts a single JSON-encoded object payload, described as
        follows.

        {
            "event_type": "finish",
            "origin": "curtin",
            "description": "Finished XYZ",
            "name": "cmd-install",
            "result": "SUCCESS",
            "files": [
                {
                    "name": "logs.tgz",
                    "encoding": "base64",
                    "content": "QXVnIDI1IDA3OjE3OjAxIG1hYXMtZGV2...
                },
                {
                    "name": "results.log",
                    "compression": "bzip2"
                    "encoding": "base64",
                    "content": "AAAAAAAAAAAAAAAAAAAAAAA...
                }
            ]
        }

        `event_type` can be "start", "progress" or "finish".

        `origin` tells us the program that originated the call.

        `description` is a human-readable, operator-friendly string that
        conveys what is being done to the node and that can be presented on the
        web UI.

        `name` is the name of the activity that's being executed. It's
        meaningful to the calling program and is a slash-separated path. We are
        mainly concerned with top-level events (no slashes), which are used to
        change the status of the node.

        `result` can be "SUCCESS" or "FAILURE" indicating whether the activity
        was successful or not.

        `files`, when present, contains one or more files. The attribute `path`
        tells us the name of the file, `compression` tells the compression we
        used before applying the `encoding` and content is the encoded data
        from the file. If the file being sent is the result of the execution of
        a script, the `result` key will hold its value. If `result` is not
        sent, it is interpreted as zero.

        `script_result_id`, when present, MAAS will search for an existing
        ScriptResult with the given id to store files present.

        """
        # Extract the authorization from request. This only does a basic
        # check that its provided. The status worker will do the authorization,
        # the negative to this is that the calling client will no know. To
        # them the message was accepted. This overall is okay since they are
        # just status messages.
        authorization = request.getHeader(b"authorization")
        if not authorization:
            request.setResponseCode(401)
            return b""
        authorization = extract_oauth_key_from_auth_header(
            authorization.decode("utf-8")
        )
        if authorization is None:
            request.setResponseCode(401)
            return b""

        # Load the content to ensure that its atleast correct before placing
        # it into the status worker.
        payload = request.content.read()
        try:
            payload = payload.decode("ascii")
        except UnicodeDecodeError as error:
            request.setResponseCode(400)
            error_msg = "Status payload must be ASCII-only: %s" % error
            logger.error(error_msg)
            return error_msg.encode("ascii")

        try:
            message = json.loads(payload)
        except ValueError:
            request.setResponseCode(400)
            error_msg = "Status payload is not valid JSON:\n%s\n\n" % payload
            logger.error(error_msg)
            return error_msg.encode("ascii")

        # Ensure the other required keys exist.
        missing_keys = [
            key for key in self.requiredMessageKeys if key not in message
        ]
        if len(missing_keys) > 0:
            request.setResponseCode(400)
            error_msg = (
                "Missing parameter(s) %s in "
                "status message." % ", ".join(missing_keys)
            )
            logger.error(error_msg)
            return error_msg.encode("ascii")

        # Queue the message with its authorization in the status worker.
        d = self.worker.queueMessage(authorization, message)

        # Finish the request after defer finishes.
        def _finish(result, request):
            request.setResponseCode(204)
            request.finish()

        d.addCallback(_finish, request)
        return NOT_DONE_YET