Towards A1 v1.0.0 72/972/6
authorTommy Carpenter <tc677g@att.com>
Wed, 18 Sep 2019 14:45:50 +0000 (10:45 -0400)
committerTommy Carpenter <tc677g@att.com>
Thu, 19 Sep 2019 14:53:38 +0000 (10:53 -0400)
- Implement type PUT
- Implement type GET
- Remove RIC manifest
- Read type GET to get schema for instance PUT
- Remove Utils (no longer needed)
- lots more tests

Change-Id: I8facd9fd9d388a9087ba856bf781397397ac2ad3
Signed-off-by: Tommy Carpenter <tc677g@att.com>
19 files changed:
a1/controller.py
a1/data.py
a1/exceptions.py
a1/openapi.yaml
a1/run.py
a1/utils.py [deleted file]
container-tag.yaml
docs/release-notes.rst
integration_tests/a1mediator/Chart.yaml
integration_tests/a1mediator/templates/config.yaml
integration_tests/a1mediator/templates/deployment.yaml
integration_tests/test_a1.tavern.yaml
setup.py
tests/conftest.py [new file with mode: 0644]
tests/fixtures/ricmanifest.json [deleted file]
tests/test_controller.py
tests/test_utils.py [deleted file]
tests/testing_helpers.py [deleted file]
tox-integration.ini

index 92ff278..9fd36d5 100644 (file)
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 # ==================================================================================
+import json
 from flask import Response
+from jsonschema import validate
 import connexion
-import json
 from jsonschema.exceptions import ValidationError
 from a1 import get_module_logger
-from a1 import a1rmr, exceptions, utils, data
+from a1 import a1rmr, exceptions, data
 
 
 logger = get_module_logger(__name__)
 
 
-def _get_policy_definition(policy_type_id):
-    # Currently we read the manifest on each call, which would seem to allow updating A1 in place. Revisit this?
-    manifest = utils.get_ric_manifest()
-    for m in manifest["controls"]:
-        if m["name"] == policy_type_id:
-            return m
-
-    raise exceptions.PolicyTypeNotFound()
-
-
-def _get_policy_schema(policy_type_id):
-    """
-    Get the needed info for a policy
-    """
-    m = _get_policy_definition(policy_type_id)
-    return m["message_receives_payload_schema"] if "message_receives_payload_schema" in m else None
-
-
 def _try_func_return(func):
     """
     generic caller that returns the apporp http response if exceptions are raised
@@ -52,6 +35,9 @@ def _try_func_return(func):
     except ValidationError as exc:
         logger.exception(exc)
         return "", 400
+    except exceptions.PolicyTypeAlreadyExists as exc:
+        logger.exception(exc)
+        return "", 400
     except exceptions.PolicyTypeNotFound as exc:
         logger.exception(exc)
         return "", 404
@@ -70,70 +56,6 @@ def _try_func_return(func):
         return Response(status=500)
 
 
-def _put_handler(policy_type_id, policy_instance_id, instance):
-    """
-    Handles policy put
-
-    For now, policy_type_id is used as the message type
-    """
-    # check for 404
-    data.type_is_valid(policy_type_id)
-
-    # validate the PUT against the schema, or if there is no shema, make sure the pUT is empty
-    schema = _get_policy_schema(policy_type_id)
-    if schema:
-        utils.validate_json(instance, schema)
-    elif instance != {}:
-        return "BODY SUPPLIED BUT POLICY HAS NO EXPECTED BODY", 400
-
-    # store the instance
-    data.store_policy_instance(policy_type_id, policy_instance_id, instance)
-
-    body = {
-        "operation": "CREATE",
-        "policy_type_id": policy_type_id,
-        "policy_instance_id": policy_instance_id,
-        "payload": instance,
-    }
-
-    # send rmr (best effort)
-    a1rmr.send(json.dumps(body), message_type=policy_type_id)
-
-    return "", 201
-
-
-def _get_status_handler(policy_type_id, policy_instance_id):
-    """
-    Pop trough A1s mailbox, insert the latest status updates into the database, and then return the status vector
-
-    NOTE: this is done lazily. Meaning, when someone performs a GET on this API, we pop through a1s mailbox.
-    THis may not work in the future if there are "thousands" of policy acknowledgements that hit a1 before this is called,
-    because the rmr mailbox may fill. However, in the near term, we do not expect this to happen.
-    """
-    # check validity to 404 first:
-    data.type_is_valid(policy_type_id)
-    data.instance_is_valid(policy_type_id, policy_instance_id)
-
-    # pop a1s mailbox, looking for policy notifications
-    new_messages = a1rmr.dequeue_all_waiting_messages(21024)
-
-    # try to parse the messages as responses. Drop those that are malformed
-    for msg in new_messages:
-        # note, we don't use the parameters "policy_type_id, policy_instance" from above here,
-        # because we are popping the whole mailbox, which might include other statuses
-        pay = json.loads(msg["payload"])
-        if "policy_type_id" in pay and "policy_instance_id" in pay and "handler_id" in pay and "status" in pay:
-            data.set_policy_instance_status(
-                pay["policy_type_id"], pay["policy_instance_id"], pay["handler_id"], pay["status"]
-            )
-        else:
-            logger.debug("Dropping message")
-            logger.debug(pay)
-
-    # return the status vector
-    return data.get_policy_instance_statuses(policy_type_id, policy_instance_id)
-
-
 # Healthcheck
 
 
@@ -159,14 +81,20 @@ def create_policy_type(policy_type_id):
     """
     Handles PUT /a1-p/policytypes/policy_type_id
     """
-    return "", 501
+
+    def _put_type_handler(policy_type_id, body):
+        data.store_policy_type(policy_type_id, body)
+        return "", 201
+
+    body = connexion.request.json
+    return _try_func_return(lambda: _put_type_handler(policy_type_id, body))
 
 
 def get_policy_type(policy_type_id):
     """
     Handles GET /a1-p/policytypes/policy_type_id
     """
-    return "", 501
+    return _try_func_return(lambda: data.get_policy_type(policy_type_id))
 
 
 def delete_policy_type(policy_type_id):
@@ -190,6 +118,7 @@ def get_policy_instance(policy_type_id, policy_instance_id):
     """
     Handles GET /a1-p/policytypes/polidyid/policies/policy_instance_id
     """
+    # 200 is automatic here
     return _try_func_return(lambda: data.get_policy_instance(policy_type_id, policy_instance_id))
 
 
@@ -197,6 +126,38 @@ def get_policy_instance_status(policy_type_id, policy_instance_id):
     """
     Handles GET /a1-p/policytypes/polidyid/policies/policy_instance_id/status
     """
+
+    def _get_status_handler(policy_type_id, policy_instance_id):
+        """
+        Pop trough A1s mailbox, insert the latest status updates into the database, and then return the status vector
+
+        NOTE: this is done lazily. Meaning, when someone performs a GET on this API, we pop through a1s mailbox.
+        THis may not work in the future if there are "thousands" of policy acknowledgements that hit a1 before this is called,
+        because the rmr mailbox may fill. However, in the near term, we do not expect this to happen.
+        """
+        # check validity to 404 first:
+        data.type_is_valid(policy_type_id)
+        data.instance_is_valid(policy_type_id, policy_instance_id)
+
+        # pop a1s mailbox, looking for policy notifications
+        new_messages = a1rmr.dequeue_all_waiting_messages(21024)
+
+        # try to parse the messages as responses. Drop those that are malformed
+        for msg in new_messages:
+            # note, we don't use the parameters "policy_type_id, policy_instance" from above here,
+            # because we are popping the whole mailbox, which might include other statuses
+            pay = json.loads(msg["payload"])
+            if "policy_type_id" in pay and "policy_instance_id" in pay and "handler_id" in pay and "status" in pay:
+                data.set_policy_instance_status(
+                    pay["policy_type_id"], pay["policy_instance_id"], pay["handler_id"], pay["status"]
+                )
+            else:
+                logger.debug("Dropping message")
+                logger.debug(pay)
+
+        # return the status vector
+        return data.get_policy_instance_statuses(policy_type_id, policy_instance_id)
+
     return _try_func_return(lambda: _get_status_handler(policy_type_id, policy_instance_id))
 
 
@@ -204,8 +165,34 @@ def create_or_replace_policy_instance(policy_type_id, policy_instance_id):
     """
     Handles PUT /a1-p/policytypes/polidyid/policies/policy_instance_id
     """
+
+    def _put_instance_handler(policy_type_id, policy_instance_id, instance):
+        """
+        Handles policy instance put
+
+        For now, policy_type_id is used as the message type
+        """
+        #  validate the PUT against the schema
+        schema = data.get_policy_type(policy_type_id)["create_schema"]
+        validate(instance=instance, schema=schema)
+
+        # store the instance
+        data.store_policy_instance(policy_type_id, policy_instance_id, instance)
+
+        body = {
+            "operation": "CREATE",
+            "policy_type_id": policy_type_id,
+            "policy_instance_id": policy_instance_id,
+            "payload": instance,
+        }
+
+        # send rmr (best effort)
+        a1rmr.send(json.dumps(body), message_type=policy_type_id)
+
+        return "", 201
+
     instance = connexion.request.json
-    return _try_func_return(lambda: _put_handler(policy_type_id, policy_instance_id, instance))
+    return _try_func_return(lambda: _put_instance_handler(policy_type_id, policy_instance_id, instance))
 
 
 def delete_policy_instance(policy_type_id, policy_instance_id):
index 17ccf4a..6fa8b3f 100644 (file)
@@ -23,7 +23,7 @@ Hopefully, the access functions are a good api so nothing else has to change whe
 For now, the database is in memory.
 We use dict data structures (KV) with the expectation of having to move this into Redis
 """
-from a1.exceptions import PolicyTypeNotFound, PolicyInstanceNotFound
+from a1.exceptions import PolicyTypeNotFound, PolicyInstanceNotFound, PolicyTypeAlreadyExists
 from a1 import get_module_logger
 
 logger = get_module_logger(__name__)
@@ -35,14 +35,7 @@ I = "instances"
 H = "handlers"
 D = "data"
 
-
-# TODO: REMOVE THIS!! (should be done via PUT)
-POLICY_DATA[20000] = {}
-POLICY_DATA[20000][D] = {"foo": "bar"}
-POLICY_DATA[20000][I] = {}
-POLICY_DATA[20001] = {}
-POLICY_DATA[20001][D] = {"foo": "bar"}
-POLICY_DATA[20001][I] = {}
+# Types
 
 
 def type_is_valid(policy_type_id):
@@ -54,6 +47,29 @@ def type_is_valid(policy_type_id):
         raise PolicyTypeNotFound()
 
 
+def store_policy_type(policy_type_id, body):
+    """
+    store a policy type if it doesn't already exist
+    """
+    if policy_type_id in POLICY_DATA:
+        raise PolicyTypeAlreadyExists()
+
+    POLICY_DATA[policy_type_id] = {}
+    POLICY_DATA[policy_type_id][D] = body
+    POLICY_DATA[policy_type_id][I] = {}
+
+
+def get_policy_type(policy_type_id):
+    """
+    retrieve a type
+    """
+    type_is_valid(policy_type_id)
+    return POLICY_DATA[policy_type_id][D]
+
+
+# Instances
+
+
 def instance_is_valid(policy_type_id, policy_instance_id):
     """
     check that an instance is valid
index b924292..1362b2a 100644 (file)
@@ -27,6 +27,10 @@ class PolicyTypeNotFound(BaseException):
     """a policy type instance cannot be found"""
 
 
+class PolicyTypeAlreadyExists(BaseException):
+    """a policy type already exists and replace not supported at this time"""
+
+
 class MissingRmrString(BaseException):
     pass
 
index 5a380bc..9dbe02b 100644 (file)
@@ -318,7 +318,7 @@ components:
         represents a policy type identifier. Currently this is restricted to an integer range.
       type: integer
       minimum: 20000
-      maximum: 21024
+      maximum: 21023
 
     policy_instance_id:
       description: >
index 903fe85..9eb59f5 100644 (file)
--- a/a1/run.py
+++ b/a1/run.py
@@ -16,9 +16,7 @@
 # ==================================================================================
 from gevent.pywsgi import WSGIServer
 from a1 import get_module_logger, app
-from a1 import utils, exceptions
 from a1.a1rmr import init_rmr
-import sys
 
 
 logger = get_module_logger(__name__)
@@ -26,15 +24,8 @@ logger = get_module_logger(__name__)
 
 def main():
     """Entrypoint"""
-    # Fail fast if we don't have a manifest
-    try:
-        utils.get_ric_manifest()
-    except exceptions.MissingManifest:
-        logger.error("Failing fast: no A1 manifest found!")
-        sys.exit(1)
-
     logger.debug("Initializing rmr")
     init_rmr()
     logger.debug("Starting gevent server")
-    http_server = WSGIServer(('', 10000), app)
+    http_server = WSGIServer(("", 10000), app)
     http_server.serve_forever()
diff --git a/a1/utils.py b/a1/utils.py
deleted file mode 100644 (file)
index 0193b1d..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-# ==================================================================================
-#       Copyright (c) 2019 Nokia
-#       Copyright (c) 2018-2019 AT&T Intellectual Property.
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#          http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-# ==================================================================================
-import json
-from jsonschema import validate
-from a1 import get_module_logger
-from a1 import exceptions
-
-
-logger = get_module_logger(__name__)
-
-
-# Public
-
-
-def validate_json(instance, schema):
-    """
-    validate incoming policy payloads
-    """
-    validate(instance=instance, schema=schema)
-
-
-def get_ric_manifest():
-    """
-    Get the ric level manifest
-    """
-    try:
-        with open("/opt/ricmanifest.json", "r") as f:
-            content = f.read()
-            manifest = json.loads(content)
-            return manifest
-    except FileNotFoundError:
-        logger.error("Missing A1 Manifest!")
-        raise exceptions.MissingManifest
index fdbd454..bdcf339 100644 (file)
@@ -1,4 +1,4 @@
 # The Jenkins job uses this string for the tag in the image name
 # for example nexus3.o-ran-sc.org:10004/my-image-name:my-tag
 ---
-tag: 0.11.0-NOT_FOR_USE_YET
+tag: 0.12.0-NOT_FOR_USE_YET
index d087051..7a1894f 100644 (file)
@@ -30,6 +30,16 @@ and this project adheres to `Semantic Versioning <http://semver.org/>`__.
     * Release 1.0.0 will be the Release A version of A1
 
 
+[0.12.0] - 9/19/2019
+::
+
+    * Implement type PUT
+    * Implement type GET
+    * Remove RIC manifest
+    * Read type GET to get schema for instance PUT
+    * Remove Utils (no longer needed)
+    * lots more tests (unit and integration)
+
 [0.11.0] - 9/17/2019
 
 ::
index 916aadc..b4f509e 100644 (file)
@@ -1,4 +1,4 @@
 apiVersion: v1
 description: A1 Helm chart for Kubernetes
 name: a1mediator
-version: 0.11.0
+version: 0.12.0
index bf4134c..bd0dae3 100644 (file)
@@ -9,4 +9,3 @@ data:
     rte|20001|delayreceiverrmrservice:4563
     rte|21024|{{ .Values.rmrservice.name }}:{{ .Values.rmrservice.port }}
     newrt|end
-  ricmanifest.json: {{tpl (.Files.Get "files/ricmanifest.json") . | quote}}
index a2963fb..a30c21c 100644 (file)
@@ -21,9 +21,6 @@ spec:
       containers:
         - name: {{ .Chart.Name }}
           volumeMounts:
-          - name: a1conf
-            mountPath: /opt/ricmanifest.json
-            subPath: ricmanifest.json
           - name: a1conf
             mountPath: /opt/route/local.rt
             subPath: local.rt
index d9de88e..25d06be 100644 (file)
@@ -15,6 +15,59 @@ stages:
 test_name: test admission control
 
 stages:
+  - name: type not there yet
+    request:
+      url: http://localhost:10000/a1-p/policytypes/20000
+      method: GET
+    response:
+      status_code: 404
+
+  - name: put the type
+    request:
+      url: http://localhost:10000/a1-p/policytypes/20000
+      method: PUT
+      json:
+        name: Admission Control
+        description: various parameters to control admission of dual connection
+        policy_type_id: 20000
+        create_schema:
+          "$schema": http://json-schema.org/draft-07/schema#
+          type: object
+          properties:
+            enforce:
+              type: boolean
+              default: true
+            window_length:
+              type: integer
+              default: 1
+              minimum: 1
+              maximum: 60
+              description: Sliding window length (in minutes)
+            blocking_rate:
+              type: number
+              default: 10
+              minimum: 1
+              maximum: 100
+              description: "% Connections to block"
+            trigger_threshold:
+              type: integer
+              default: 10
+              minimum: 1
+              description: Minimum number of events in window to trigger blocking
+          required:
+            - enforce
+            - blocking_rate
+            - trigger_threshold
+            - window_length
+          additionalProperties: false
+
+  - name: type there now
+    request:
+      url: http://localhost:10000/a1-p/policytypes/20000
+      method: GET
+    response:
+      status_code: 200
+
   - name: test the admission control policy get not there yet
     request:
       url: http://localhost:10000/a1-p/policytypes/20000/policies/admission_control_policy
@@ -29,7 +82,7 @@ stages:
     response:
       status_code: 404
 
-  - name: test the admission control policy
+  - name: put the admission control policy
     request:
       url: http://localhost:10000/a1-p/policytypes/20000/policies/admission_control_policy
       method: PUT
@@ -71,7 +124,53 @@ stages:
 test_name: test the delay receiver
 
 stages:
-  - name: test the delay policy get not there yet
+
+  - name: test the delay policy type not there yet
+    request:
+      url: http://localhost:10000/a1-p/policytypes/20001
+      method: GET
+    response:
+      status_code: 404
+
+  - name: put the type
+    request:
+      url: http://localhost:10000/a1-p/policytypes/20001
+      method: PUT
+      json:
+        name: test policy
+        description: just for testing
+        policy_type_id: 20001
+        create_schema:
+          "$schema": http://json-schema.org/draft-07/schema#
+          type: object
+          properties:
+            test:
+              type: string
+          required:
+            - test
+          additionalProperties: false
+
+  - name: type there now
+    request:
+      url: http://localhost:10000/a1-p/policytypes/20001
+      method: GET
+    response:
+      status_code: 200
+      body:
+        name: test policy
+        description: just for testing
+        policy_type_id: 20001
+        create_schema:
+          "$schema": http://json-schema.org/draft-07/schema#
+          type: object
+          properties:
+            test:
+              type: string
+          required:
+            - test
+          additionalProperties: false
+
+  - name: test the delay policy instance get not there yet
     request:
       url: http://localhost:10000/a1-p/policytypes/20001/policies/delaytest
       method: GET
@@ -89,7 +188,8 @@ stages:
     request:
       url: http://localhost:10000/a1-p/policytypes/20001/policies/delaytest
       method: PUT
-      json: {}
+      json:
+        test: foo
       headers:
         content-type: application/json
     response:
@@ -101,7 +201,8 @@ stages:
       method: GET
     response:
       status_code: 200
-      body: {}
+      body:
+        test: foo
 
   - name: test the admission control policy status get
     delay_before: 8  # give it a few seconds for rmr ; delay reciever sleeps for 5 seconds by default
@@ -115,14 +216,58 @@ stages:
           status: OK
 
 
-
-
 ---
 
 test_name: bad_requests
 
 stages:
 
+  - name: bad type get
+    request:
+      url: http://localhost:10000/a1-p/policytypes/20002
+      method: GET
+    response:
+      status_code: 404
+
+
+  - name: bad instance get
+    request:
+      url: http://localhost:10000/a1-p/policytypes/20000/policies/darkness
+      method: GET
+    response:
+      status_code: 404
+
+  - name: bad int range 1
+    request:
+      url: http://localhost:10000/a1-p/policytypes/19999
+      method: PUT
+      json:
+        name: test policy
+        description: just for testing
+        policy_type_id: 19999
+        create_schema:
+          "$schema": http://json-schema.org/draft-07/schema#
+          type: object
+    response:
+      status_code: 400
+
+  - name: bad int range 2
+    request:
+      url: http://localhost:10000/a1-p/policytypes/21024
+      method: PUT
+      json:
+        name: test policy
+        description: just for testing
+        policy_type_id: 21024
+        create_schema:
+          "$schema": http://json-schema.org/draft-07/schema#
+          type: object
+    response:
+      status_code: 400
+
+
+
+
   - name: bad body for admission control policy
     request:
       url: http://localhost:10000/a1-p/policytypes/20000/policies/admission_control_policy
@@ -142,9 +287,9 @@ stages:
     response:
       status_code: 415
 
-  - name: bad body for test policy
+  - name: bad body for delaytest
     request:
-      url: http://localhost:10000/a1-p/policytypes/20001/policies/test_policy
+      url: http://localhost:10000/a1-p/policytypes/20001/policies/delaytest
       method: PUT
       json:
         not: "welcome"
index e885438..441c054 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -18,7 +18,7 @@ from setuptools import setup, find_packages
 
 setup(
     name="a1",
-    version="0.11.0",
+    version="0.12.0",
     packages=find_packages(exclude=["tests.*", "tests"]),
     author="Tommy Carpenter",
     description="RIC A1 Mediator for policy/intent changes",
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644 (file)
index 0000000..c39b962
--- /dev/null
@@ -0,0 +1,44 @@
+import pytest
+
+
+@pytest.fixture
+def adm_type_good():
+    return {
+        "name": "Admission Control",
+        "description": "various parameters to control admission of dual connection",
+        "policy_type_id": 20000,
+        "create_schema": {
+            "$schema": "http://json-schema.org/draft-07/schema#",
+            "type": "object",
+            "properties": {
+                "enforce": {"type": "boolean", "default": True},
+                "window_length": {
+                    "type": "integer",
+                    "default": 1,
+                    "minimum": 1,
+                    "maximum": 60,
+                    "description": "Sliding window length (in minutes)",
+                },
+                "blocking_rate": {
+                    "type": "number",
+                    "default": 10,
+                    "minimum": 1,
+                    "maximum": 100,
+                    "description": "% Connections to block",
+                },
+                "trigger_threshold": {
+                    "type": "integer",
+                    "default": 10,
+                    "minimum": 1,
+                    "description": "Minimum number of events in window to trigger blocking",
+                },
+            },
+            "required": ["enforce", "blocking_rate", "trigger_threshold", "window_length"],
+            "additionalProperties": False,
+        },
+    }
+
+
+@pytest.fixture
+def adm_instance_good():
+    return {"enforce": True, "window_length": 10, "blocking_rate": 20, "trigger_threshold": 10}
diff --git a/tests/fixtures/ricmanifest.json b/tests/fixtures/ricmanifest.json
deleted file mode 100644 (file)
index 7883c8a..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-{
-  "controls":[
-    {
-      "name":20000,
-      "description":"various parameters to control admission of dual connection",
-      "control_state_request_rmr_type":"DC_ADM_GET_POLICY",
-      "control_state_request_reply_rmr_type":"DC_ADM_GET_POLICY_ACK",
-      "message_receives_rmr_type":"DC_ADM_INT_CONTROL",
-      "message_receives_payload_schema":{
-        "$schema":"http://json-schema.org/draft-07/schema#",
-        "type":"object",
-        "properties":{
-          "enforce":{
-            "type":"boolean",
-            "default":true
-          },
-          "window_length":{
-            "type":"integer",
-            "default":1,
-            "minimum":1,
-            "maximum":60,
-            "description":"Sliding window length (in minutes)"
-          },
-          "blocking_rate":{
-            "type":"number",
-            "default":10,
-            "minimum":1,
-            "maximum":100,
-            "description":"% Connections to block"
-          },
-          "trigger_threshold":{
-            "type":"integer",
-            "default":10,
-            "minimum":1,
-            "description":"Minimum number of events in window to trigger blocking"
-          }
-        },
-        "required":[
-          "enforce",
-          "blocking_rate",
-          "trigger_threshold",
-          "window_length"
-        ],
-        "additionalProperties":false
-      },
-      "message_sends_rmr_type":"DC_ADM_INT_CONTROL_ACK",
-      "message_sends_payload_schema":{
-        "$schema":"http://json-schema.org/draft-07/schema#",
-        "type":"object",
-        "properties":{
-          "status":{
-            "type":"string",
-            "enum":[
-              "SUCCESS",
-              "FAIL"
-            ]
-          },
-          "message":{
-            "type":"string"
-          }
-        },
-        "required":[
-          "status"
-        ],
-        "additionalProperties":false
-      }
-    },
-    {
-      "name":20001,
-      "description":"for the purposes of testing",
-      "message_receives_rmr_type":"TEST_REQ",
-      "message_sends_rmr_type":"TEST_ACK",
-      "message_sends_payload_schema":{
-        "$schema":"http://json-schema.org/draft-07/schema#",
-        "type":"object",
-        "properties":{
-          "status":{
-            "type":"string",
-            "enum":[
-              "SUCCESS",
-              "FAIL"
-            ]
-          }
-        }
-      }
-    }
-  ]
-}
index 32f6d87..5bd3a08 100644 (file)
@@ -19,13 +19,14 @@ import os
 
 from rmr.rmr_mocks import rmr_mocks
 from a1 import app
-import testing_helpers
 import pytest
 
 
 ADM_CTRL = "admission_control_policy"
 ADM_CTRL_INSTANCE = "/a1-p/policytypes/20000/policies/" + ADM_CTRL
 ADM_CTRL_INSTANCE_STATUS = ADM_CTRL_INSTANCE + "/status"
+ADM_CTRL_TYPE = "/a1-p/policytypes/20000"
+TEST_TYPE = "/a1-p/policytypes/20001"
 
 
 # http://flask.pocoo.org/docs/1.0/testing/
@@ -53,7 +54,7 @@ def _fake_dequeue(_filter_type):
 
 
 def _test_put_patch(monkeypatch):
-    testing_helpers.patch_all(monkeypatch)
+    rmr_mocks.patch_rmr(monkeypatch)
     monkeypatch.setattr("rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(0))  # good sends for this whole batch
 
     # we need to repatch alloc (already patched in patch_rmr) to fix the transactionid, alloc is called in send and recieve
@@ -74,34 +75,23 @@ def _test_put_patch(monkeypatch):
 # Actual Tests
 
 
-# def test_policy_get(client, monkeypatch):
-#     """
-#     test policy GET
-#     """
-#     _test_put_patch(monkeypatch)
-#     monkeypatch.setattr(
-#         "a1.a1rmr.dequeue_all_waiting_messages",
-#         _fake_dequeue(monkeypatch, msg_payload={"GET ack": "pretend policy is here"}, msg_type=20003),
-#     )
-#     res = client.get("/a1-p/policies/admission_control_policy")
-#     assert res.status_code == 200
-#     assert res.json == {"GET ack": "pretend policy is here"}
-#
-#
-# def test_policy_get_unsupported(client, monkeypatch):
-#     """
-#     test policy GET
-#     """
-#     testing_helpers.patch_all(monkeypatch, nofetch=True)
-#     res = client.get("/a1-p/policies/admission_control_policy")
-#     assert res.status_code == 400
-#     assert res.data == b'"POLICY DOES NOT SUPPORT FETCHING"\n'
-#
-#
-def test_xapp_put_good(client, monkeypatch):
+def test_xapp_put_good(client, monkeypatch, adm_type_good, adm_instance_good):
     """ test policy put good"""
 
-    # nothing there yet
+    # no type there yet
+    res = client.get(ADM_CTRL_TYPE)
+    assert res.status_code == 404
+
+    # put the type
+    res = client.put(ADM_CTRL_TYPE, json=adm_type_good)
+    assert res.status_code == 201
+
+    # there now
+    res = client.get(ADM_CTRL_TYPE)
+    assert res.status_code == 200
+    assert res.json == adm_type_good
+
+    # no instance there yet
     res = client.get(ADM_CTRL_INSTANCE)
     assert res.status_code == 404
     res = client.get(ADM_CTRL_INSTANCE_STATUS)
@@ -109,13 +99,13 @@ def test_xapp_put_good(client, monkeypatch):
 
     # create a good instance
     _test_put_patch(monkeypatch)
-    res = client.put(ADM_CTRL_INSTANCE, json=testing_helpers.good_payload())
+    res = client.put(ADM_CTRL_INSTANCE, json=adm_instance_good)
     assert res.status_code == 201
 
     # get the instance
     res = client.get(ADM_CTRL_INSTANCE)
     assert res.status_code == 200
-    assert res.json == testing_helpers.good_payload()
+    assert res.json == adm_instance_good
 
     # get the instance status
     monkeypatch.setattr("a1.a1rmr.dequeue_all_waiting_messages", _fake_dequeue)
@@ -125,16 +115,14 @@ def test_xapp_put_good(client, monkeypatch):
 
     # assert that rmr bad states don't cause problems
     monkeypatch.setattr("rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(10))
-    res = client.put(ADM_CTRL_INSTANCE, json=testing_helpers.good_payload())
+    res = client.put(ADM_CTRL_INSTANCE, json=adm_instance_good)
     assert res.status_code == 201
 
     monkeypatch.setattr("rmr.rmr.rmr_send_msg", rmr_mocks.send_mock_generator(5))
-    res = client.put(ADM_CTRL_INSTANCE, json=testing_helpers.good_payload())
+    res = client.put(ADM_CTRL_INSTANCE, json=adm_instance_good)
     assert res.status_code == 201
 
 
-#
-#
 # def test_xapp_put_bad(client, monkeypatch):
 #     """Test policy put fails"""
 #     _test_put_patch(monkeypatch)
@@ -174,13 +162,26 @@ def test_xapp_put_good(client, monkeypatch):
 #     res = client.put("/a1-p/policies/admission_control_policy", json=testing_helpers.good_payload())
 #     assert res.status_code == 504
 #     assert res.data == b"\"A1 was expecting an ACK back but it didn't receive one or didn't recieve the expected ACK\"\n"
-#
-#
-def test_bad_requests(client, monkeypatch):
+
+
+def test_bad_instances(client, monkeypatch, adm_type_good):
     """
     Test bad send failures
     """
-    testing_helpers.patch_all(monkeypatch)
+    rmr_mocks.patch_rmr(monkeypatch)
+
+    # TODO: reenable this after delete!
+    # put the type
+    # res = client.put(ADM_CTRL_TYPE, json=adm_type_good)
+    # assert res.status_code == 201
+
+    # illegal type range
+    res = client.put("/a1-p/policytypes/19999", json=adm_type_good)
+    assert res.status_code == 400
+    res = client.put("/a1-p/policytypes/21024", json=adm_type_good)
+    assert res.status_code == 400
+
+    # bad body
     res = client.put(ADM_CTRL_INSTANCE, json={"not": "expected"})
     assert res.status_code == 400
 
@@ -188,37 +189,6 @@ def test_bad_requests(client, monkeypatch):
     res = client.put(ADM_CTRL_INSTANCE, data="notajson")
     assert res.status_code == 415
 
-    # test a PUT body against a poliucy not expecting one
-    res = client.put("/a1-p/policytypes/20001/policies/test_policy", json=testing_helpers.good_payload())
-    assert res.status_code == 400
-    assert res.data == b'"BODY SUPPLIED BUT POLICY HAS NO EXPECTED BODY"\n'
-
-
-# def test_bad_requests(client, monkeypatch):
-#     """Test bad requests"""
-#     testing_helpers.patch_all(monkeypatch)
-#
-#     # test a 404
-#     res = client.put("/a1-p/policies/noexist", json=testing_helpers.good_payload())
-#     assert res.status_code == 404
-
-
-# def test_missing_manifest(client, monkeypatch):
-#     """
-#     test that we get a 500 with an approrpiate message on a missing manifest
-#     """
-#
-#     def f():
-#         raise exceptions.MissingManifest()
-#
-#     monkeypatch.setattr("a1.utils.get_ric_manifest", f)
-#
-#     res = client.put("/a1-p/policies/admission_control_policy", json=testing_helpers.good_payload())
-#     assert res.status_code == 500
-#     assert res.data == b'"A1 was unable to find the required RIC manifest. report this!"\n'
-#
-#
-
 
 def test_healthcheck(client):
     """
diff --git a/tests/test_utils.py b/tests/test_utils.py
deleted file mode 100644 (file)
index 2895632..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-# ==================================================================================
-#       Copyright (c) 2019 Nokia
-#       Copyright (c) 2018-2019 AT&T Intellectual Property.
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#          http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-# ==================================================================================
-import pytest
-import jsonschema
-from a1 import utils, exceptions
-import testing_helpers
-
-
-def test_bad_get_ric_manifest(monkeypatch):
-    """
-    testing missing manifest
-    """
-
-    def badopen(filename, mode):
-        raise FileNotFoundError()
-
-    monkeypatch.setattr("builtins.open", badopen)
-    with pytest.raises(exceptions.MissingManifest):
-        utils.get_ric_manifest()
-
-
-def test_good_get_ric_manifest(monkeypatch):
-    """
-    test get_ric_manifest
-    """
-    testing_helpers.patch_all(monkeypatch)
-    utils.get_ric_manifest()
-
-
-def test_validate(monkeypatch):
-    """
-    test json validation wrapper
-    """
-    testing_helpers.patch_all(monkeypatch)
-    ricmanifest = utils.get_ric_manifest()
-    schema = ricmanifest["controls"][0]["message_receives_payload_schema"]
-    utils.validate_json(testing_helpers.good_payload(), schema)
-    with pytest.raises(jsonschema.exceptions.ValidationError):
-        utils.validate_json({"dc_admission_start_time": "10:00:00", "dc_admission_end_time": "nevergonnagiveyouup"}, schema)
diff --git a/tests/testing_helpers.py b/tests/testing_helpers.py
deleted file mode 100644 (file)
index c59426b..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-# ==================================================================================
-#       Copyright (c) 2019 Nokia
-#       Copyright (c) 2018-2019 AT&T Intellectual Property.
-#
-#   Licensed under the Apache License, Version 2.0 (the "License");
-#   you may not use this file except in compliance with the License.
-#   You may obtain a copy of the License at
-#
-#          http://www.apache.org/licenses/LICENSE-2.0
-#
-#   Unless required by applicable law or agreed to in writing, software
-#   distributed under the License is distributed on an "AS IS" BASIS,
-#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#   See the License for the specific language governing permissions and
-#   limitations under the License.
-# ==================================================================================
-import json
-import os
-from rmr.rmr_mocks import rmr_mocks
-
-
-def _get_fixture_path(name):
-    cur_dir = os.path.dirname(os.path.realpath(__file__))
-    return "{0}/fixtures/{1}".format(cur_dir, name)
-
-
-def patch_all(monkeypatch, nonexisting_rmr=False, nofetch=False):
-    rmr_mocks.patch_rmr(monkeypatch)
-
-    # patch manifest
-    man = json.loads(open(_get_fixture_path("ricmanifest.json"), "r").read())
-    if nonexisting_rmr:
-        man["controls"][0]["message_receives_rmr_type"] = "DARKNESS"
-
-    if nofetch:
-        del man["controls"][0]["control_state_request_rmr_type"]
-
-    monkeypatch.setattr("a1.utils.get_ric_manifest", lambda: man)
-
-
-def good_payload():
-    return {"enforce": True, "window_length": 10, "blocking_rate": 20, "trigger_threshold": 10}
index 08f5482..f6191e0 100644 (file)
@@ -24,22 +24,30 @@ whitelist_externals=
     ab
     echo
     pkill
+    kubectl
 passenv = *
 deps =
     tavern
 changedir=integration_tests
 commands_pre=
+    echo "WARNING: make sure you're running with latest docker builds!"
+    sleep 5
     helm install --devel testreceiver -n testreceiver
-    helm install --devel a1mediator/ -n a1
+    helm install --devel a1mediator -n a1
+# wait for helm charts
     sleep 20
+    kubectl get pods --all-namespaces
     ./portforward.sh
     sleep 2
 commands=
     echo "linting"
-    helm lint a1mediator/
+    helm lint a1mediator
+    helm lint testreceiver
     echo "running tavern"
-    pytest
+# run tavern
+    pytest --tavern-beta-new-traceback
     echo "running ab"
+# run apache bench
     ab -n 100 -c 10 -u putdata -T application/json http://localhost:10000/a1-p/policytypes/20000/policies/admission_control_policy
 commands_post=
     helm delete testreceiver