Mostly integration test work: 54/2454/16
authorTommy Carpenter <tc677g@att.com>
Fri, 7 Feb 2020 19:06:20 +0000 (14:06 -0500)
committerTommy Carpenter <tc677g@att.com>
Thu, 13 Feb 2020 15:07:56 +0000 (10:07 -0500)
    * Switches A1's three test receivers (integration tests) over to golang; this was mostly done to learn the go xapp framework and they are identical in functionality.
    * Upgrades the version of rmr in A1 and all integration receivers to 1.13.*
    * Uses a much fancier Docker build to reduce the size of a1's image. The python:3.7-alpine image itself is 98MB and A1 is now only ~116MB, so we're done optimizing A1's container size.

Issue-ID: RICAPP-61
Change-Id: Ic2004a5b457c55c730575aa8326e61b01e8546da
Signed-off-by: Tommy Carpenter <tc677g@att.com>
23 files changed:
.gitignore
Dockerfile
container-tag.yaml
docs/developer-guide.rst
docs/release-notes.rst
integration_tests/a1mediator/Chart.yaml
integration_tests/getlogs.sh
integration_tests/query_tester.py [deleted file]
integration_tests/receiver.py [deleted file]
integration_tests/testreceiver/templates/config.yaml
integration_tests/testreceiver/templates/deployment.yaml
integration_tests/testreceiver/values.yaml
integration_tests/testxappcode/Dockerfile-delay-receiver [moved from integration_tests/Dockerfile-test-delay-receiver with 52% similarity]
integration_tests/testxappcode/Dockerfile-query-receiver [moved from integration_tests/Dockerfile-query-receiver with 52% similarity]
integration_tests/testxappcode/Dockerfile-test-receiver [new file with mode: 0644]
integration_tests/testxappcode/delay-config-file.yaml [new file with mode: 0755]
integration_tests/testxappcode/go.mod [new file with mode: 0644]
integration_tests/testxappcode/query-config-file.yaml [new file with mode: 0755]
integration_tests/testxappcode/receiver.go [new file with mode: 0755]
integration_tests/testxappcode/test-config-file.yaml [new file with mode: 0755]
rmr-version.yaml
setup.py
tox-integration.ini

index fb1708b..92ce839 100644 (file)
@@ -1,10 +1,14 @@
 # misc cruft
 *.log
+log.txt
 integration_tests/log.txt
 NOTES.txt
 rmr/*
 docs_and_diagrams/
 
+# go
+go.sum
+
 # documentation
 .tox
 docs/_build/
index 2faaca5..8872e4e 100644 (file)
@@ -1,6 +1,6 @@
 # ==================================================================================
-#       Copyright (c) 2019 Nokia
-#       Copyright (c) 2018-2019 AT&T Intellectual Property.
+#       Copyright (c) 2019-2020 Nokia
+#       Copyright (c) 2018-2020 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.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 # ==================================================================================
-# install a well known working rmr
-FROM python:3.7-alpine
-RUN apk update && apk add autoconf automake build-base cmake libtool ninja pkgconfig git
-RUN git clone --branch 1.10.2 https://gerrit.o-ran-sc.org/r/ric-plt/lib/rmr \
-    && cd rmr \
-    && mkdir build \
-    && cd build \
-    && cmake .. -DPACK_EXTERNALS=1 \
-    && make install
 
-# a1 stage 2
+# This container uses a 2 stage build!
+# Tips and tricks were learned from: https://pythonspeed.com/articles/multi-stage-docker-python/
+FROM python:3.7-alpine AS compile-image
+# Gevent needs gcc
+RUN apk update && apk add gcc musl-dev
+# do the install of a1
+
+# Switch to a non-root user for security reasons
+# This is only really needed in stage 2 however this makes the copying easier and straitforward! --user doesn't do the same thing if run as root!
+RUN addgroup -S a1user && adduser -S -G a1user a1user
+USER a1user
+
+# Speed hack; we install gevent FIRST because when building repeatedly (eg during dev) and only changing a1 code, we do not need to keep compiling gevent which takes forever
+RUN pip install --upgrade pip && pip install --user gevent
+COPY setup.py /home/a1user/
+COPY a1/ /home/a1user/a1
+RUN pip install --user /home/a1user
+
+###########
+# 2nd stage
 FROM python:3.7-alpine
-
 # dir that rmr routing file temp goes into
 RUN mkdir -p /opt/route/
-
-# Gevent needs gcc
-RUN apk update && apk add bash gcc musl-dev
-
-# Speed hack; we install gevent here because when building repeatedly (eg during dev) and only changing a1 code,
-# we do not need to keep compiling gevent which takes forever
-RUN pip install --upgrade pip && pip install gevent
-
-# copies
-COPY --from=0 /usr/local/lib64/libnng.so /usr/local/lib64/libnng.so
-COPY --from=0 /usr/local/lib64/librmr_nng.so /usr/local/lib64/librmr_nng.so
-COPY a1/ /tmp/a1
-COPY setup.py tox.ini /tmp/
-WORKDIR /tmp
-
-# do the actual install; this writes into /usr/local, need root
-RUN pip install .
-
-# Switch to a non-root user for security reasons.
-# a1 does not currently write into any dirs so no chowns are needed at this time.
-ENV A1USER a1user
-RUN addgroup -S $A1USER && adduser -S -G $A1USER $A1USER
-USER $A1USER
-
+# python copy; this basically makes the 2 stage python build work
+COPY --from=compile-image /home/a1user/.local /home/a1user/.local
+# copy rmr .sos from the builder image
+COPY --from=nexus3.o-ran-sc.org:10004/bldr-alpine3-go:1-rmr1.13.1 /usr/local/lib64/libnng.so /usr/local/lib64/libnng.so
+COPY --from=nexus3.o-ran-sc.org:10004/bldr-alpine3-go:1-rmr1.13.1 /usr/local/lib64/librmr_nng.so /usr/local/lib64/librmr_nng.so
+# Switch to a non-root user for security reasons. a1 does not currently write into any dirs so no chowns are needed at this time.
+RUN addgroup -S a1user && adduser -S -G a1user a1user
+USER a1user
 # misc setups
 EXPOSE 10000
 ENV LD_LIBRARY_PATH /usr/local/lib/:/usr/local/lib64
 ENV RMR_SEED_RT /opt/route/local.rt
-# dont buffer logging
 ENV PYTHONUNBUFFERED 1
+# This step is critical
+ENV PATH=/home/a1user/.local/bin:$PATH
 
+# Run!
 CMD run.py
index 5c4cc74..65db8b3 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: 2.1.2
+tag: 2.1.3
index e3e47c5..b37b2c4 100644 (file)
@@ -35,19 +35,13 @@ This project follows semver. When changes are made, the versions are in:
 
 
 Version bumping rmr
---------------------
-rmr is a critical dependency of A1. Bumping the rmr version dependency requires changes in:
-
-1) ``Dockerfile``
-
-2) ``Dockerfile-Unit-Test``
-
-3) ``integration_tests/Dockerfile-test-delay-receiver``
-
-4) ``integration_tests/Dockerfile-query-receiver``
-
-5) ``rmr-version.yaml``
+-------------------
+As of 2020/02/13, A1 and all three integration test receivers use a base image from o-ran-sc.
+The rmr version is in that base image.
+However, the one item in this repo that must be kept in sync is ``rmr-version.yaml``. This controls what rmr gets installed for unit testing.
 
+Version bumping pyrmr
+---------------------
 rmr-python is the python binding to rmr . Installing rmr per the above does not install it.
 Bumping the rmr python version dependency requires changes in:
 
index fe3c1c9..e39ec3a 100644 (file)
@@ -14,6 +14,15 @@ and this project adheres to `Semantic Versioning <http://semver.org/>`__.
    :depth: 3
    :local:
 
+[2.1.3] - 2/13/2020
+-------------------
+::
+
+    * This is a pretty big amount of work/changes, however no APIs were changed hence the semver patch
+    * Switches A1's three test receivers (integration tests) over to golang; this was mostly done to learn the go xapp framework and they are identical in functionality.
+    * Upgrades the version of rmr in A1 and all integration receivers to 1.13.*
+    * Uses a much fancier Docker build to reduce the size of a1's image. The python:3.7-alpine image itself is 98MB and A1 is now only ~116MB, so we're done optimizing A1's container size.
+
 [2.1.2] - 1/22/2020
 -------------------
 
index e1ed636..3072609 100644 (file)
@@ -1,4 +1,4 @@
 apiVersion: v1
 description: A1 Helm chart for Kubernetes
 name: a1mediator
-version: 2.1.2
+version: 2.1.3
index 22bbd4b..fbf2f1b 100755 (executable)
@@ -1,14 +1,19 @@
 #!/bin/sh
 echo "\n\n a1" > log.txt
+kubectl get pods --namespace=default | awk '{ print $1 }' | egrep '^a1-a1mediator-' | xargs kubectl logs -p >> log.txt 2>&1
 kubectl get pods --namespace=default | awk '{ print $1 }' | egrep '^a1-a1mediator-' | xargs kubectl logs  >> log.txt 2>&1
 
 echo "\n\n test receiver" >> log.txt
+# the -p gets the "previous logs" in case the prior container crashed.. very useful for debugging a new receiver.
+kubectl get pods --namespace=default | awk '{ print $1 }' | egrep '^testreceiver-' | xargs -I X kubectl logs -p X testreceiver  >> log.txt 2>&1
 kubectl get pods --namespace=default | awk '{ print $1 }' | egrep '^testreceiver-' | xargs -I X kubectl logs X testreceiver  >> log.txt 2>&1
 
 echo "\n\n delay" >> log.txt
+kubectl get pods --namespace=default | awk '{ print $1 }' | egrep '^testreceiver-' | xargs -I X kubectl logs -p X delayreceiver  >> log.txt 2>&1
 kubectl get pods --namespace=default | awk '{ print $1 }' | egrep '^testreceiver-' | xargs -I X kubectl logs X delayreceiver  >> log.txt 2>&1
 
 echo "\n\n query" >> log.txt
+kubectl get pods --namespace=default | awk '{ print $1 }' | egrep '^testreceiver-' | xargs -I X kubectl logs -p X queryreceiver  >> log.txt 2>&1
 kubectl get pods --namespace=default | awk '{ print $1 }' | egrep '^testreceiver-' | xargs -I X kubectl logs X queryreceiver  >> log.txt 2>&1
 
 echo "\n\n sdl-redis" >> log.txt
diff --git a/integration_tests/query_tester.py b/integration_tests/query_tester.py
deleted file mode 100644 (file)
index d8cf3ba..0000000
+++ /dev/null
@@ -1,84 +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.
-# ==================================================================================
-"""
-Test receiver
-"""
-
-import time
-import json
-from rmr import rmr
-
-PORT = "4564"
-
-mrc = rmr.rmr_init(PORT.encode("utf-8"), rmr.RMR_MAX_RCV_BYTES, rmr.RMRFL_MTCALL)
-test_type = 1006001
-
-while rmr.rmr_ready(mrc) == 0:
-    time.sleep(1)
-    print("not yet ready")
-
-print("listening ON {}".format(PORT))
-
-# loop
-while True:
-
-    # do query
-    pay = {"policy_type_id": test_type}
-    sbuf_send = rmr.rmr_alloc_msg(mrc, 4096, payload=json.dumps(pay).encode("utf-8"), gen_transaction_id=True, mtype=20012)
-    sbuf_send = rmr.rmr_send_msg(mrc, sbuf_send)
-    post_send_summary = rmr.message_summary(sbuf_send)
-
-    if not (post_send_summary["message state"] == 0 and post_send_summary["message status"] == "RMR_OK"):
-        print("was unable to send query to a1!")
-        time.sleep(1)
-    else:
-        # query worked, wait 2 seconds, then receive everything we have
-        time.sleep(1)
-        print("reading messages")
-
-        # this is a hacked up version of rmr_rcvall_msgs in the rmr package
-        # we need the actual messages, not the summaries, to use rts
-        sbuf_rcv = rmr.rmr_alloc_msg(mrc, 4096)  # allocate buffer to have something for a return status
-        while True:
-            sbuf_rcv = rmr.rmr_torcv_msg(mrc, sbuf_rcv, 0)  # set the timeout to 0 so this doesn't block!!
-
-            summary = rmr.message_summary(sbuf_rcv)
-            if summary["message status"] != "RMR_OK":  # ok indicates msg received, stop on all other states
-                print("no more instances received. will try again in 1s")
-                break
-
-            print("Received: {0}".format(summary))
-
-            received_payload = json.loads(summary["payload"])
-            assert received_payload["policy_type_id"] == test_type
-            assert summary["message type"] == 20010
-
-            payload = {
-                "policy_type_id": received_payload["policy_type_id"],
-                "policy_instance_id": received_payload["policy_instance_id"],
-                "handler_id": "query_tester",
-                "status": "OK",
-            }
-            val = json.dumps(payload).encode("utf-8")
-            rmr.set_payload_and_length(val, sbuf_rcv)  # TODO: extend rmr-python to allow rts to accept this param
-            sbuf_rcv.contents.mtype = 20011  # TODO: extend rmr-python to allow rts to accept this param
-            print("Pre reply summary: {}".format(rmr.message_summary(sbuf_rcv)))
-
-            # send ack
-            sbuf_rcv = rmr.rmr_rts_msg(mrc, sbuf_rcv)
-            post_reply_summary = rmr.message_summary(sbuf_rcv)
-            print("Post reply summary: {}".format(post_reply_summary))
diff --git a/integration_tests/receiver.py b/integration_tests/receiver.py
deleted file mode 100644 (file)
index 5c3ceaf..0000000
+++ /dev/null
@@ -1,77 +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.
-# ==================================================================================
-"""
-Test receiver
-"""
-
-import time
-import json
-import os
-from rmr import rmr
-
-PORT = os.environ.get("TEST_RCV_PORT", "4560")
-DELAY = int(os.environ.get("TEST_RCV_SEC_DELAY", 0))
-HANDLER_ID = os.environ.get("HANDLER_ID", "test_receiver")
-
-mrc = rmr.rmr_init(PORT.encode("utf-8"), rmr.RMR_MAX_RCV_BYTES, rmr.RMRFL_MTCALL)
-
-while rmr.rmr_ready(mrc) == 0:
-    time.sleep(1)
-    print("not yet ready")
-
-print("listening ON {}".format(PORT))
-while True:
-    sbuf = rmr.rmr_alloc_msg(mrc, 10)
-    sbuf = rmr.rmr_torcv_msg(mrc, sbuf, 1000)
-    summary = rmr.message_summary(sbuf)
-    if summary["message state"] == 12 and summary["message status"] == "RMR_ERR_TIMEOUT":
-        # print("Nothing received yet")
-        time.sleep(1)
-    else:
-        print("Message received!: {}".format(summary))
-
-        received_payload = json.loads(summary["payload"])
-
-        op = received_payload["operation"]
-        send_payload_status = "ERROR"
-        if op == "CREATE":
-            send_payload_status = "OK"
-        elif op == "DELETE":
-            send_payload_status = "DELETED"
-
-        payload = {
-            "policy_type_id": received_payload["policy_type_id"],
-            "policy_instance_id": received_payload["policy_instance_id"],
-            "handler_id": HANDLER_ID,
-            "status": send_payload_status,
-        }
-
-        val = json.dumps(payload).encode("utf-8")
-        rmr.set_payload_and_length(val, sbuf)
-        sbuf.contents.mtype = 20011
-        print("Pre reply summary: {}".format(rmr.message_summary(sbuf)))
-        time.sleep(DELAY)
-
-        # try up to 5 times to send back the ack
-        for _ in range(5):
-            sbuf = rmr.rmr_rts_msg(mrc, sbuf)
-            post_reply_summary = rmr.message_summary(sbuf)
-            print("Post reply summary: {}".format(post_reply_summary))
-            if post_reply_summary["message state"] == 10 and post_reply_summary["message status"] == "RMR_ERR_RETRY":
-                time.sleep(1)
-            else:
-                break
index e6f4801..dd4772b 100644 (file)
@@ -1,3 +1,5 @@
+#note: the xapp frame calls rmrready, which requires a route table, even if the app only uses rts. So we can never fully delete these.
+#
 apiVersion: v1
 kind: ConfigMap
 metadata:
@@ -5,7 +7,7 @@ metadata:
 data:
   local.rt: |
     newrt|start
-    # we actaully use rts so i dont even think this is used
+    # right now the test receivers in go cannot use rts so we need this. See the comment in the receiver xapp
     rte|20011|a1rmrservice:4562
     newrt|end
 
@@ -18,7 +20,7 @@ metadata:
 data:
   local.rt: |
     newrt|start
-    # we actaully use rts so i dont even think this is used
+    # right now the test receivers in go cannot use rts so we need this. See the comment in the receiver xapp
     rte|20011|a1rmrservice:4562
     newrt|end
 
@@ -31,6 +33,8 @@ metadata:
 data:
   local.rt: |
     newrt|start
-    # this query is initiated in the query receiver so this is certainly needed
+    # this query is initiated in the query receiver
     rte|20012|a1rmrservice:4562
+    # right now the test receivers in go cannot use rts so we need this. See the comment in the receiver xapp
+    rte|20011|a1rmrservice:4562
     newrt|end
index 0a08fa3..af17968 100644 (file)
@@ -28,9 +28,22 @@ spec:
               mountPath: /opt/route/local.rt
               subPath: local.rt
           env:
+            # tells the test xapp to do a query
+            - name: DO_QUERY
+              value: "YES"
             # this sets the source field in messages from a1 to point back to a1s service name, rather than it's random pod name
             - name: RMR_SRC_ID
               value: {{ .Values.queryrmrservice.name }}
+            - name: HANDLER_ID
+              value: "query_tester"
+            # the xapp framework requires this to work, even if SDL isn't used.
+            # it does an SDL healthcheck before it starts up properly
+            # moreover, the db config section doesn't appear to be honored; with that set, but not this, it doesn't find SDL
+            # so we need this here for the test receiver which uses the xapp framework to work
+            - name: DBAAS_SERVICE_HOST
+              value: "dbaas"
+            - name: DBAAS_SERVICE_PORT
+              value: "6379"
 
         # test receiver
         - name: testreceiver
@@ -42,10 +55,15 @@ spec:
             - name: testreceiverconf
               mountPath: /opt/route/local.rt
               subPath: local.rt
+          env:
+            - name: DBAAS_SERVICE_HOST
+              value: "dbaas"
+            - name: DBAAS_SERVICE_PORT
+              value: "6379"
 
         # test receiver that delays until sending
         - name: delayreceiver
-          image: testreceiver:latest
+          image: delayreceiver:latest
           imagePullPolicy: Never
           resources:
             {{- toYaml .Values.resources | nindent 12 }}
@@ -54,12 +72,14 @@ spec:
               mountPath: /opt/route/local.rt
               subPath: local.rt
           env:
-          - name: TEST_RCV_PORT
-            value: "{{ .Values.delayrmrservice.port }}"
-          - name: TEST_RCV_SEC_DELAY
-            value: "5"
-          - name: HANDLER_ID
-            value: "delay_receiver"
+            - name: TEST_RCV_SEC_DELAY
+              value: "5"
+            - name: HANDLER_ID
+              value: "delay_receiver"
+            - name: DBAAS_SERVICE_HOST
+              value: "dbaas"
+            - name: DBAAS_SERVICE_PORT
+              value: "6379"
 
       volumes:
         - name: "testreceiverconf"
index d53b7c7..f045b63 100644 (file)
@@ -1,7 +1,3 @@
-# Default values for testreceiver.
-# This is a YAML-formatted file.
-# Declare variables to be passed into your templates.
-
 replicaCount: 1
 
 testrmrservice:
@@ -1,6 +1,6 @@
 # ==================================================================================
-#       Copyright (c) 2019 Nokia
-#       Copyright (c) 2018-2019 AT&T Intellectual Property.
+#       Copyright (c) 2020 Nokia
+#       Copyright (c) 2020 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.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 # ==================================================================================
-# install a well known working rmr
-FROM python:3.7-alpine
-RUN apk update && apk add autoconf automake build-base cmake libtool ninja pkgconfig git
-RUN git clone --branch 1.10.2 https://gerrit.o-ran-sc.org/r/ric-plt/lib/rmr \
-    && cd rmr \
-    && mkdir build \
-    && cd build \
-    && cmake .. -DPACK_EXTERNALS=1 \
-    && make install
 
-# stage2
-FROM python:3.7-alpine
+# This Dockerfile uses a two stage Docker build
 
-# copies
-COPY --from=0 /usr/local/lib64/libnng.so /usr/local/lib64/libnng.so
-COPY --from=0 /usr/local/lib64/librmr_nng.so /usr/local/lib64/librmr_nng.so
-COPY receiver.py /
+FROM nexus3.o-ran-sc.org:10004/bldr-alpine3-go:1-rmr1.13.1
 
-# Install RMr python bindings
-RUN pip install --upgrade pip
-RUN pip install rmr==2.2.0
+# go will complain if there is a go.mod at the root of the GOPATH so we can't.
+RUN mkdir myxapp
+COPY receiver.go myxapp/receiver.go
+COPY go.mod myxapp/go.mod
 
-# rmr setups
+# do the build
+WORKDIR myxapp
+ENV GO111MODULE on
+ENV GO_ENABLED 0
+ENV GOOS linux
+RUN go build -a -installsuffix cgo -o receiver receiver.go
+
+# 2nd stage
+FROM alpine:3.11
+COPY --from=0 /usr/local/lib64/libnng.so* /usr/local/lib64/
+COPY --from=0 /usr/local/lib64/librmr_nng* /usr/local/lib64/
+COPY --from=0 /go/myxapp/receiver .
+COPY delay-config-file.yaml .
+
+# rmr setup
 RUN mkdir -p /opt/route/
 ENV LD_LIBRARY_PATH /usr/local/lib:/usr/local/lib64
 ENV RMR_SEED_RT /opt/route/local.rt
 
-WORKDIR /
-CMD ["python","-u","receiver.py"]
+CMD ["./receiver", "-f", "delay-config-file.yaml"]
@@ -1,6 +1,6 @@
 # ==================================================================================
-#       Copyright (c) 2019 Nokia
-#       Copyright (c) 2018-2019 AT&T Intellectual Property.
+#       Copyright (c) 2020 Nokia
+#       Copyright (c) 2020 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.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 # ==================================================================================
-# install a well known working rmr
-FROM python:3.7-alpine
-RUN apk update && apk add autoconf automake build-base cmake libtool ninja pkgconfig git
-RUN git clone --branch 1.10.2 https://gerrit.o-ran-sc.org/r/ric-plt/lib/rmr \
-    && cd rmr \
-    && mkdir build \
-    && cd build \
-    && cmake .. -DPACK_EXTERNALS=1 \
-    && make install
 
-# stage2
-FROM python:3.7-alpine
+# This Dockerfile uses a two stage Docker build
 
-# copies
-COPY --from=0 /usr/local/lib64/libnng.so /usr/local/lib64/libnng.so
-COPY --from=0 /usr/local/lib64/librmr_nng.so /usr/local/lib64/librmr_nng.so
-COPY query_tester.py /
+FROM nexus3.o-ran-sc.org:10004/bldr-alpine3-go:1-rmr1.13.1
 
-# Install RMr python bindings
-RUN pip install --upgrade pip
-RUN pip install rmr==2.2.0
+# go will complain if there is a go.mod at the root of the GOPATH so we can't.
+RUN mkdir myxapp
+COPY receiver.go myxapp/receiver.go
+COPY go.mod myxapp/go.mod
 
-# rmr setups
+# do the build
+WORKDIR myxapp
+ENV GO111MODULE on
+ENV GO_ENABLED 0
+ENV GOOS linux
+RUN go build -a -installsuffix cgo -o receiver receiver.go
+
+# 2nd stage
+FROM alpine:3.11
+COPY --from=0 /usr/local/lib64/libnng.so* /usr/local/lib64/
+COPY --from=0 /usr/local/lib64/librmr_nng* /usr/local/lib64/
+COPY --from=0 /go/myxapp/receiver .
+COPY query-config-file.yaml .
+
+# rmr setup
 RUN mkdir -p /opt/route/
 ENV LD_LIBRARY_PATH /usr/local/lib:/usr/local/lib64
 ENV RMR_SEED_RT /opt/route/local.rt
 
-WORKDIR /
-CMD ["python","-u","query_tester.py"]
+CMD ["./receiver", "-f", "query-config-file.yaml"]
diff --git a/integration_tests/testxappcode/Dockerfile-test-receiver b/integration_tests/testxappcode/Dockerfile-test-receiver
new file mode 100644 (file)
index 0000000..174af0e
--- /dev/null
@@ -0,0 +1,47 @@
+# ==================================================================================
+#       Copyright (c) 2020 Nokia
+#       Copyright (c) 2020 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.
+# ==================================================================================
+
+# This Dockerfile uses a two stage Docker build
+
+# The first stage is defined here: https://gerrit.o-ran-sc.org/r/gitweb?p=ci-management.git;a=blob;f=docker/bldr-alpine3-go/Dockerfile;h=a1e31f07e6113d4a02202793ace6ebc780d71583;hb=3711ffcbfe06f6c872bf4a0871eb5f2a2fcd83ae
+FROM nexus3.o-ran-sc.org:10004/bldr-alpine3-go:1-rmr1.13.1
+
+# go will complain if there is a go.mod at the root of the GOPATH so we can't.
+RUN mkdir myxapp
+COPY receiver.go myxapp/receiver.go
+COPY go.mod myxapp/go.mod
+
+# do the build
+WORKDIR myxapp
+ENV GO111MODULE on
+ENV GO_ENABLED 0
+ENV GOOS linux
+RUN go build -a -installsuffix cgo -o receiver receiver.go
+
+# 2nd stage
+FROM alpine:3.11
+COPY --from=0 /usr/local/lib64/libnng.so* /usr/local/lib64/
+COPY --from=0 /usr/local/lib64/librmr_nng* /usr/local/lib64/
+COPY --from=0 /go/myxapp/receiver .
+COPY test-config-file.yaml .
+
+# rmr setup
+RUN mkdir -p /opt/route/
+ENV LD_LIBRARY_PATH /usr/local/lib:/usr/local/lib64
+ENV RMR_SEED_RT /opt/route/local.rt
+
+CMD ["./receiver", "-f", "test-config-file.yaml"]
diff --git a/integration_tests/testxappcode/delay-config-file.yaml b/integration_tests/testxappcode/delay-config-file.yaml
new file mode 100755 (executable)
index 0000000..8db59f7
--- /dev/null
@@ -0,0 +1,38 @@
+#   Copyright (c) 2019-2020 AT&T Intellectual Property.
+#   Copyright (c) 2019-2020 Nokia.
+#
+#   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.
+
+"local":
+  "host": ":8080"
+"logger":
+  "level": 4
+"rmr":
+  "protPort": "tcp:4563"
+  "maxSize": 4096
+  "numWorkers": 1
+"db":
+  "host": "dbaas"
+  "port": 6379
+  "namespaces": ["sdl", "rnib"]
+"test":
+  "mode": "forwarder"
+  "mtype": 10004
+  "subId": 1111
+  "size": 100
+  "rate": 10
+  "amount": 10
+  "rounds": 1
+  "store": 0
+  "waitForAck": 0
+
diff --git a/integration_tests/testxappcode/go.mod b/integration_tests/testxappcode/go.mod
new file mode 100644 (file)
index 0000000..9a5691f
--- /dev/null
@@ -0,0 +1,20 @@
+go 1.13
+
+module gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/example-xapp
+
+require (
+       gerrit.o-ran-sc.org/r/ric-plt/xapp-frame v0.0.24
+       github.com/go-openapi/errors v0.19.3 // indirect
+       github.com/go-openapi/runtime v0.19.11 // indirect
+       github.com/go-openapi/spec v0.19.6 // indirect
+       github.com/go-openapi/strfmt v0.19.4 // indirect
+       github.com/go-openapi/swag v0.19.7 // indirect
+       github.com/go-openapi/validate v0.19.6 // indirect
+       github.com/jessevdk/go-flags v1.4.0 // indirect
+)
+
+replace gerrit.o-ran-sc.org/r/ric-plt/xapp-frame => gerrit.o-ran-sc.org/r/ric-plt/xapp-frame.git v0.0.24
+
+replace gerrit.o-ran-sc.org/r/ric-plt/sdlgo => gerrit.o-ran-sc.org/r/ric-plt/sdlgo.git v0.5.0
+
+replace gerrit.o-ran-sc.org/r/com/golog => gerrit.o-ran-sc.org/r/com/golog.git v0.0.1
diff --git a/integration_tests/testxappcode/query-config-file.yaml b/integration_tests/testxappcode/query-config-file.yaml
new file mode 100755 (executable)
index 0000000..871d166
--- /dev/null
@@ -0,0 +1,38 @@
+#   Copyright (c) 2019-2020 AT&T Intellectual Property.
+#   Copyright (c) 2019-2020 Nokia.
+#
+#   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.
+
+"local":
+  "host": ":8080"
+"logger":
+  "level": 4
+"rmr":
+  "protPort": "tcp:4564"
+  "maxSize": 4096
+  "numWorkers": 1
+"db":
+  "host": "dbaas"
+  "port": 6379
+  "namespaces": ["sdl", "rnib"]
+"test":
+  "mode": "forwarder"
+  "mtype": 10004
+  "subId": 1111
+  "size": 100
+  "rate": 10
+  "amount": 10
+  "rounds": 1
+  "store": 0
+  "waitForAck": 0
+
diff --git a/integration_tests/testxappcode/receiver.go b/integration_tests/testxappcode/receiver.go
new file mode 100755 (executable)
index 0000000..9012094
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+==================================================================================
+  Copyright (c) 2020 AT&T Intellectual Property.
+  Copyright (c) 2020 Nokia
+
+   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.
+==================================================================================
+*/
+package main
+
+import (
+       "encoding/json"
+       "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
+       "os"
+       "strconv"
+       "time"
+)
+
+var delay int        // used for the delay receiver
+var handlerID string // used for the delay receiver too
+var doQuery bool     // used for the query receiver
+
+type a1Receiver struct {
+       msgChan  chan *xapp.RMRParams
+       appReady bool
+       rmrReady bool
+}
+
+type policyRequest struct {
+       Operation        string      `json:"operation"`
+       PolicyTypeID     int         `json:"policy_type_id"`
+       PolicyInstanceID string      `json:"policy_instance_id"`
+       Pay              interface{} `json:"payload"`
+}
+
+type policyRequestResponse struct {
+       PolicyTypeID     int    `json:"policy_type_id"`
+       PolicyInstanceID string `json:"policy_instance_id"`
+       HandlerID        string `json:"handler_id"`
+       Status           string `json:"status"`
+}
+
+type policyQuery struct {
+       PolicyTypeID int `json:"policy_type_id"`
+}
+
+func (e *a1Receiver) sendMsgRetry(params *xapp.RMRParams) {
+       // helper for rmr that handles retries and sleep
+       retries := 0
+       for { // just keep trying until it works
+               if e.rmrReady { // we must wait for ready, else SendMsg will blow with a nullptr
+                       if ok := xapp.Rmr.SendMsg(params); ok {
+                               xapp.Logger.Info("Msg successfully sent after %d retries!", retries)
+                               return
+                       }
+                       retries++
+                       //xapp.Logger.Info("Query failed to send...")
+               } else {
+                       xapp.Logger.Info("rmr not ready...")
+                       time.Sleep(time.Duration(1) * time.Second)
+               }
+       }
+}
+
+func (e *a1Receiver) handlePolicyReq(msg *xapp.RMRParams) {
+
+       // unmarshal the request
+       var dat policyRequest
+       if err := json.Unmarshal(msg.Payload, &dat); err != nil {
+               panic(err)
+       }
+
+       var status string
+       switch dat.Operation {
+       case "CREATE":
+               status = "OK"
+       case "DELETE":
+               status = "DELETED"
+       }
+
+       // form the response
+       res := &policyRequestResponse{
+               dat.PolicyTypeID,
+               dat.PolicyInstanceID,
+               "test_receiver",
+               status,
+       }
+
+       outgoing, err := json.Marshal(res)
+       if err != nil {
+               panic(err)
+       }
+
+       /*
+               WARNING:
+               we want to use rts here. However, the current go xapp framework rts is broken.
+       */
+       params := &xapp.RMRParams{
+               Mtype:   20011,
+               Payload: outgoing,
+       }
+
+       if delay > 0 {
+               xapp.Logger.Info("Xapp is sleeping...")
+               time.Sleep(time.Duration(delay) * time.Second) // so much work to replicate python's time.sleep(5)...
+       }
+
+       e.sendMsgRetry(params)
+
+       xapp.Logger.Info("Policy response sent!")
+}
+
+func (e *a1Receiver) sendQuery() {
+       // form the query
+       res := &policyQuery{
+               1006001,
+       }
+       outgoing, err := json.Marshal(res)
+       if err != nil {
+               panic(err)
+       }
+       params := &xapp.RMRParams{
+               Mtype:   20012,
+               Payload: outgoing,
+       }
+
+       for {
+               /* We do this in a loop here, because even when the query first works, it could be the case that
+                  a1 does not even have the type yet, or there are no instances yet. In this integration test,
+                  we just keep pounding away so that eventually a1 returns the list this int test is looking for.
+                  A real xapp would NOT call the query in a loop like this.
+               */
+               e.sendMsgRetry(params)
+               xapp.Logger.Info("Query sent successfully")
+               time.Sleep(time.Duration(1) * time.Second)
+       }
+}
+
+func (e *a1Receiver) messageLoop() {
+       for {
+               xapp.Logger.Info("Waiting for message..")
+
+               msg := <-e.msgChan
+
+               xapp.Logger.Info("Message received!")
+               defer xapp.Rmr.Free(msg.Mbuf)
+
+               switch msg.Mtype {
+               case 20010:
+                       e.handlePolicyReq(msg)
+               default:
+                       panic("Unexpected message type!")
+               }
+       }
+}
+
+// Consume: This named function is a required callback for e to use the xapp interface. it is called on all received rmr messages.
+func (e *a1Receiver) Consume(rp *xapp.RMRParams) (err error) {
+       e.msgChan <- rp
+       return
+}
+
+func (e *a1Receiver) Run() {
+       // Set MDC (read: name visible in the logs)
+       xapp.Logger.SetMdc(handlerID, "0.1.0")
+
+       /* from reading the xapp frame code...
+                  this SetReadyCB sets off a chain of events..
+              it sets readycb and readycbparams at the module level in xapp.go
+                  nothing happens yet..
+                  when the xapp is ran with` xapp.Run, this callback actually gets passed into the Rmr client which is not exposed in the xapp
+                      Rmr.SetReadyCB(xappReadyCb, nil)
+                  This "primes" the rmr client with it's own readycb, which is now set to this callback function
+                  When the rmr client is ready, it invokes the callback
+                  so basically, when rmr is ready, this function is invoked
+                  I think the xapp frame code could have been greatly simplified by just passing this into the invocation of Run() and then just passing that into the rmr client init!
+       */
+       xapp.SetReadyCB(func(d interface{}) { e.rmrReady = true }, true)
+
+       // start message loop. We cannot wait for e.rmrReady here since that doesn't get populated until Run() runs.
+       go e.messageLoop()
+
+       if doQuery {
+               // we are in the query tester; kick off a loop that does that until it works
+               go e.sendQuery()
+       }
+
+       xapp.Run(e)
+}
+
+func newA1Receiver(appReady, rmrReady bool) *a1Receiver {
+       return &a1Receiver{
+               msgChan:  make(chan *xapp.RMRParams),
+               rmrReady: rmrReady,
+               appReady: appReady,
+       }
+}
+
+func main() {
+
+       delay = 0
+       if d, ok := os.LookupEnv("TEST_RCV_SEC_DELAY"); ok {
+               delay, _ = strconv.Atoi(d)
+       }
+
+       handlerID = "test_receiver"
+       if hid, ok := os.LookupEnv("HANDLER_ID"); ok {
+               handlerID = hid
+       }
+
+       doQuery = false
+       if _, ok := os.LookupEnv("DO_QUERY"); ok {
+               doQuery = true
+       }
+
+       newA1Receiver(true, false).Run()
+}
diff --git a/integration_tests/testxappcode/test-config-file.yaml b/integration_tests/testxappcode/test-config-file.yaml
new file mode 100755 (executable)
index 0000000..cd59770
--- /dev/null
@@ -0,0 +1,38 @@
+#   Copyright (c) 2019-2020 AT&T Intellectual Property.
+#   Copyright (c) 2019-2020 Nokia.
+#
+#   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.
+
+"local":
+  "host": ":8080"
+"logger":
+  "level": 4
+"rmr":
+  "protPort": "tcp:4560"
+  "maxSize": 4096
+  "numWorkers": 1
+"db":
+  "host": "dbaas"
+  "port": 6379
+  "namespaces": ["sdl", "rnib"]
+"test":
+  "mode": "forwarder"
+  "mtype": 10004
+  "subId": 1111
+  "size": 100
+  "rate": 10
+  "amount": 10
+  "rounds": 1
+  "store": 0
+  "waitForAck": 0
+
index 0d9c301..5808bc4 100644 (file)
@@ -1,3 +1,3 @@
 # CI script installs RMR from PackageCloud using this version
 ---
-version: 1.10.2
+version: 1.13.1
index 4401c10..db13d40 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -18,7 +18,7 @@ from setuptools import setup, find_packages
 
 setup(
     name="a1",
-    version="2.1.2",
+    version="2.1.3",
     packages=find_packages(exclude=["tests.*", "tests"]),
     author="Tommy Carpenter",
     description="RIC A1 Mediator for policy/intent changes",
index 62670ef..428d14b 100644 (file)
@@ -32,8 +32,21 @@ deps =
 changedir=integration_tests
 commands_pre=
     echo "WARNING: make sure you're running with latest docker builds!"
+# Fish function that builds and tags the 3 receivers. Run from integration_tests/testxappcode/
+#function a1intbuild
+#    docker build  -t a1:latest .
+#    cd integration_tests/testxappcode
+#    docker build -t testreceiver:latest . -f Dockerfile-test-receiver
+#    docker build -t delayreceiver:latest . -f Dockerfile-delay-receiver
+#    docker build -t queryreceiver:latest . -f Dockerfile-query-receiver
+#    cd ../..
+#end
     sleep 5
 # helm v3 is helm install [name] [chart]
+    echo "linting"
+    helm lint a1mediator
+    helm lint testreceiver
+    helm lint dbaas-service
     helm install --devel testreceiver testreceiver
     helm install --devel a1 a1mediator
     helm install --devel dbaas dbaas-service
@@ -42,10 +55,6 @@ commands_pre=
     ./portforward.sh
     sleep 2
 commands=
-    echo "linting"
-    helm lint a1mediator
-    helm lint testreceiver
-    helm lint dbaas-service
     echo "running tavern"
 # run tavern
     pytest --tavern-beta-new-traceback test_a1.tavern.yaml