From: Tommy Carpenter Date: Fri, 7 Feb 2020 19:06:20 +0000 (-0500) Subject: Mostly integration test work: X-Git-Tag: 2.1.4~2 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?p=ric-plt%2Fa1.git;a=commitdiff_plain;h=78ba273b279a7e7af6dba811a29746b881a53a8e Mostly integration test work: * 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 --- diff --git a/.gitignore b/.gitignore index fb1708b..92ce839 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ diff --git a/Dockerfile b/Dockerfile index 2faaca5..8872e4e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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. @@ -14,50 +14,45 @@ # 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 diff --git a/container-tag.yaml b/container-tag.yaml index 5c4cc74..65db8b3 100644 --- a/container-tag.yaml +++ b/container-tag.yaml @@ -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 diff --git a/docs/developer-guide.rst b/docs/developer-guide.rst index e3e47c5..b37b2c4 100644 --- a/docs/developer-guide.rst +++ b/docs/developer-guide.rst @@ -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: diff --git a/docs/release-notes.rst b/docs/release-notes.rst index fe3c1c9..e39ec3a 100644 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -14,6 +14,15 @@ and this project adheres to `Semantic Versioning `__. :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 ------------------- diff --git a/integration_tests/a1mediator/Chart.yaml b/integration_tests/a1mediator/Chart.yaml index e1ed636..3072609 100644 --- a/integration_tests/a1mediator/Chart.yaml +++ b/integration_tests/a1mediator/Chart.yaml @@ -1,4 +1,4 @@ apiVersion: v1 description: A1 Helm chart for Kubernetes name: a1mediator -version: 2.1.2 +version: 2.1.3 diff --git a/integration_tests/getlogs.sh b/integration_tests/getlogs.sh index 22bbd4b..fbf2f1b 100755 --- a/integration_tests/getlogs.sh +++ b/integration_tests/getlogs.sh @@ -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 index d8cf3ba..0000000 --- a/integration_tests/query_tester.py +++ /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 index 5c3ceaf..0000000 --- a/integration_tests/receiver.py +++ /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 diff --git a/integration_tests/testreceiver/templates/config.yaml b/integration_tests/testreceiver/templates/config.yaml index e6f4801..dd4772b 100644 --- a/integration_tests/testreceiver/templates/config.yaml +++ b/integration_tests/testreceiver/templates/config.yaml @@ -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 diff --git a/integration_tests/testreceiver/templates/deployment.yaml b/integration_tests/testreceiver/templates/deployment.yaml index 0a08fa3..af17968 100644 --- a/integration_tests/testreceiver/templates/deployment.yaml +++ b/integration_tests/testreceiver/templates/deployment.yaml @@ -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" diff --git a/integration_tests/testreceiver/values.yaml b/integration_tests/testreceiver/values.yaml index d53b7c7..f045b63 100644 --- a/integration_tests/testreceiver/values.yaml +++ b/integration_tests/testreceiver/values.yaml @@ -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: diff --git a/integration_tests/Dockerfile-test-delay-receiver b/integration_tests/testxappcode/Dockerfile-delay-receiver similarity index 52% rename from integration_tests/Dockerfile-test-delay-receiver rename to integration_tests/testxappcode/Dockerfile-delay-receiver index 4d09d1b..15c6f28 100644 --- a/integration_tests/Dockerfile-test-delay-receiver +++ b/integration_tests/testxappcode/Dockerfile-delay-receiver @@ -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. @@ -14,32 +14,33 @@ # 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"] diff --git a/integration_tests/Dockerfile-query-receiver b/integration_tests/testxappcode/Dockerfile-query-receiver similarity index 52% rename from integration_tests/Dockerfile-query-receiver rename to integration_tests/testxappcode/Dockerfile-query-receiver index 3c5ac28..20fb082 100644 --- a/integration_tests/Dockerfile-query-receiver +++ b/integration_tests/testxappcode/Dockerfile-query-receiver @@ -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. @@ -14,32 +14,33 @@ # 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 index 0000000..174af0e --- /dev/null +++ b/integration_tests/testxappcode/Dockerfile-test-receiver @@ -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 index 0000000..8db59f7 --- /dev/null +++ b/integration_tests/testxappcode/delay-config-file.yaml @@ -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 index 0000000..9a5691f --- /dev/null +++ b/integration_tests/testxappcode/go.mod @@ -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 index 0000000..871d166 --- /dev/null +++ b/integration_tests/testxappcode/query-config-file.yaml @@ -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 index 0000000..9012094 --- /dev/null +++ b/integration_tests/testxappcode/receiver.go @@ -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 index 0000000..cd59770 --- /dev/null +++ b/integration_tests/testxappcode/test-config-file.yaml @@ -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 + diff --git a/rmr-version.yaml b/rmr-version.yaml index 0d9c301..5808bc4 100644 --- a/rmr-version.yaml +++ b/rmr-version.yaml @@ -1,3 +1,3 @@ # CI script installs RMR from PackageCloud using this version --- -version: 1.10.2 +version: 1.13.1 diff --git a/setup.py b/setup.py index 4401c10..db13d40 100644 --- 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", diff --git a/tox-integration.ini b/tox-integration.ini index 62670ef..428d14b 100644 --- a/tox-integration.ini +++ b/tox-integration.ini @@ -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