This is an automated email from the ASF dual-hosted git repository.
pradeepagrawal8184 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/master by this push:
new 469708a8d RANGER-5844: Add Docker system tests to verify Kafka publish
authorization via Ranger (#1317)
469708a8d is described below
commit 469708a8d95d17f84ef42360dd92139f97e7fef6
Author: Chinmay Hegde <[email protected]>
AuthorDate: Fri Oct 16 17:59:44 2026 +0530
RANGER-5844: Add Docker system tests to verify Kafka publish authorization
via Ranger (#1317)
---
functional-tests/kafka/test_kafka_01.py | 139 ++++++++++++
functional-tests/pytest.ini | 1 +
functional-tests/ranger_test_utils/kafka_utils.py | 260 ++++++++++++++++++++++
functional-tests/readme.md | 55 ++---
functional-tests/run-tests.sh | 2 +-
5 files changed, 420 insertions(+), 37 deletions(-)
diff --git a/functional-tests/kafka/test_kafka_01.py
b/functional-tests/kafka/test_kafka_01.py
new file mode 100644
index 000000000..33e8f24e5
--- /dev/null
+++ b/functional-tests/kafka/test_kafka_01.py
@@ -0,0 +1,139 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+"""
+Authorization is asserted via Kafka console producer output only. Access-audit
asserts are
+deferred until Kafka plugin audits are reliably indexed in Docker — use
+ranger_test_utils.access_audit_utils when ready.
+
+AUDIT_FILTERS (future):
+ test_01_01 positive: accessType=publish, accessResult=1,
aclEnforcer=ranger-acl,
+ resourceName=<topic>, repoName=dev_kafka,
requestUser=hrt_21
+ test_01_01 negative: accessType=publish, accessResult=0,
aclEnforcer=ranger-acl,
+ resourceName=<topic>, repoName=dev_kafka,
requestUser=hrt_22
+"""
+
+import pytest
+
+from ranger_test_utils.hive_utils import delete_policies_by_id
+from ranger_test_utils.kafka_utils import (
+ KAFKA_ALLOWED_GROUP,
+ KAFKA_ALLOWED_USER,
+ KAFKA_AUTHZ_DENIED_SUBSTR,
+ KAFKA_DENIED_USER,
+ create_topic_allow_policy,
+ ensure_kerberos_user,
+ ensure_os_group_membership,
+ preflight_kafka_stack,
+ restore_missing_policies,
+ run_kafka_producer,
+ snapshot_policies,
+ wait_for_kafka_policy_propagation,
+)
+from ranger_test_utils.utils import (
+ configure_test_logging,
+ create_ranger_admin_session,
+ ensure_group_exists,
+ get_test_logger,
+ log_testcase_begin_for_pytest,
+ unique_suffix,
+)
+
+pytestmark = pytest.mark.kafka
+
+logger = get_test_logger(__name__)
+
+MESSAGE = "Hello Ranger Kafka"
+
+_admin_session = None
+_policy_id = None
+_original_policies = []
+_topic = None
+
+
+def _restore_dev_kafka_policies():
+ try:
+ if _policy_id is not None:
+ delete_policies_by_id(_admin_session, [_policy_id])
+ finally:
+ restore_missing_policies(_admin_session, _original_policies)
+
+
+def setup_module():
+ configure_test_logging(module_name=__name__)
+ logger.info("=== setup_module: start ===")
+ preflight_kafka_stack()
+
+ global _admin_session, _policy_id, _original_policies, _topic
+ _admin_session = create_ranger_admin_session()
+ _topic = "kafkaranger_test" + unique_suffix(6)
+ logger.info("Test topic: %s", _topic)
+
+ ensure_group_exists(_admin_session, KAFKA_ALLOWED_GROUP)
+ ensure_os_group_membership(KAFKA_ALLOWED_USER, KAFKA_ALLOWED_GROUP)
+ ensure_kerberos_user(KAFKA_ALLOWED_USER)
+ ensure_kerberos_user(KAFKA_DENIED_USER)
+
+ _original_policies = snapshot_policies(_admin_session)
+ try:
+ delete_policies_by_id(_admin_session, [policy["id"] for policy in
_original_policies])
+ _policy_id = create_topic_allow_policy(_admin_session, _topic,
KAFKA_ALLOWED_GROUP)
+ logger.info("Created topic allow policy id=%s for group %s",
_policy_id, KAFKA_ALLOWED_GROUP)
+ wait_for_kafka_policy_propagation()
+ except BaseException:
+ # pytest does not run teardown_module when setup_module raises, so
restore here.
+ _restore_dev_kafka_policies()
+ raise
+ logger.info("=== setup_module: done ===")
+
+
+def teardown_module():
+ if _admin_session is None:
+ return
+
+ logger.info("=== teardown_module: start ===")
+ _restore_dev_kafka_policies()
+ logger.info("=== teardown_module: done ===")
+
+
+def test_01_01_kafka_publish_topic_test_positive(request):
+ """hrt_21 is in the finance group, which the topic allow policy lets
publish (creating the topic)."""
+ log_testcase_begin_for_pytest(logger, request.node.name, suite="KAFKA")
+ exit_code, output = run_kafka_producer(KAFKA_ALLOWED_USER, _topic, MESSAGE)
+ assert "TOPIC_AUTHORIZATION_FAILED" not in output and
KAFKA_AUTHZ_DENIED_SUBSTR not in output, (
+ "Unexpected authorization failure publishing to "
+ + _topic
+ + "; exit_code="
+ + str(exit_code)
+ + " output="
+ + output
+ )
+ assert exit_code == 0, (
+ "Kafka producer failed; exit_code=" + str(exit_code) + " output=" +
output
+ )
+
+
+def test_01_01_kafka_publish_topic_test_negative(request):
+ """hrt_22 has no policy on the topic, so publishing is denied."""
+ log_testcase_begin_for_pytest(logger, request.node.name, suite="KAFKA")
+ exit_code, output = run_kafka_producer(KAFKA_DENIED_USER, _topic, MESSAGE)
+ assert KAFKA_AUTHZ_DENIED_SUBSTR in output, (
+ "Expected "
+ + KAFKA_AUTHZ_DENIED_SUBSTR
+ + " in Kafka producer output; exit_code="
+ + str(exit_code)
+ + " output="
+ + output
+ )
diff --git a/functional-tests/pytest.ini b/functional-tests/pytest.ini
index 1cc7adb7e..d17dbeb90 100644
--- a/functional-tests/pytest.ini
+++ b/functional-tests/pytest.ini
@@ -31,6 +31,7 @@ markers =
hive: Hive plugin enforcement (Docker HS2)
hbase: HBase plugin enforcement (Docker HBase shell)
hdfs_policy: HDFS Ranger plugin path policy enforcement (Docker dfs)
+ kafka: Kafka plugin enforcement (Docker Kafka console producer)
pythonpath = .
# No console logging during pytest; logger.info -> logs/<test_module_name>.log
(see conftest.py).
log_cli = false
diff --git a/functional-tests/ranger_test_utils/kafka_utils.py
b/functional-tests/ranger_test_utils/kafka_utils.py
new file mode 100644
index 000000000..064511fe6
--- /dev/null
+++ b/functional-tests/ranger_test_utils/kafka_utils.py
@@ -0,0 +1,260 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+"""Kafka Docker helpers for Ranger functional-tests (console producer + plugin
enforcement)."""
+
+import json
+import shlex
+import time
+
+import pytest
+
+from ranger_test_utils.hive_utils import (
+ ADMIN_CONTAINER,
+ KDC_CONTAINER,
+ KERBEROS_REALM,
+ get_docker_client,
+ list_policies_for_service,
+)
+from ranger_test_utils.utils import (
+ LOGS_DIR,
+ POLICY_URL,
+ assert_http_ok,
+ ensure_logs_directory,
+ get_test_logger,
+ unique_suffix,
+)
+
+logger = get_test_logger(__name__)
+
+KAFKA_SERVICE_NAME = "dev_kafka"
+KAFKA_CONTAINER = "ranger-kafka"
+KAFKA_BOOTSTRAP_SERVER = "ranger-kafka.rangernw:9092"
+
+KAFKA_ALLOWED_USER = "hrt_21"
+KAFKA_DENIED_USER = "hrt_22"
+KAFKA_ALLOWED_GROUP = "finance"
+
+KAFKA_AUTHZ_DENIED_SUBSTR = "TopicAuthorizationException"
+
+# publish to a not-yet-existing topic: broker auto-creates it, which needs
create + describe
+KAFKA_TOPIC_POLICY_ACCESS_TYPES = ["publish", "create", "describe"]
+
+# Kafka plugin polls Ranger for policy changes every 30s by default.
+KAFKA_POLICY_PROPAGATION_SLEEP_SEC = 40
+
+
+def _container_running(name):
+ try:
+ container = get_docker_client().containers.get(name)
+ except Exception:
+ return False
+ container.reload()
+ return container.status == "running"
+
+
+def preflight_kafka_stack():
+ """Skip tests when required Docker services are unavailable."""
+ required = [ADMIN_CONTAINER, KDC_CONTAINER, KAFKA_CONTAINER]
+ missing = [name for name in required if not _container_running(name)]
+ if missing:
+ pytest.skip(
+ "Kafka functional-tests need running containers: "
+ + ", ".join(missing)
+ + ". Run ./run-tests.sh postgres kafka from functional-tests/."
+ )
+
+
+def _user_principal(username):
+ return username + "@" + KERBEROS_REALM
+
+
+def _user_keytab_in_kafka_container(username):
+ return "/etc/keytabs/" + username + ".keytab"
+
+
+def ensure_kerberos_user(username):
+ """Create a user Kerberos principal and keytab on KDC, visible in
ranger-kafka."""
+ kdc = get_docker_client().containers.get(KDC_CONTAINER)
+ principal = _user_principal(username)
+ keytab_in_kdc = "/etc/keytabs/ranger-kafka/" + username + ".keytab"
+
+ logger.info("Kerberos setup on %s: principal=%s keytab=%s", KDC_CONTAINER,
principal, keytab_in_kdc)
+ kdc.exec_run('kadmin.local -q "addprinc -randkey ' + principal + '"',
user="root")
+ kdc.exec_run("rm -f " + keytab_in_kdc, user="root")
+ # -norandkey keeps the key version, so keytabs this shared user already
has in the
+ # hive/hbase containers stay valid.
+ exit_code, output = kdc.exec_run(
+ 'kadmin.local -q "ktadd -norandkey -k ' + keytab_in_kdc + " " +
principal + '"',
+ user="root",
+ demux=True,
+ )
+ if exit_code != 0:
+ raise RuntimeError(
+ "ktadd failed for " + principal + ": " + ((output[0] or b"") +
(output[1] or b"")).decode()
+ )
+ kdc.exec_run("chmod 444 " + keytab_in_kdc, user="root")
+
+
+def ensure_os_group_membership(username, group):
+ """
+ Make username a member of group on the ranger-kafka OS.
+
+ The Kafka plugin resolves a user's groups from the broker's OS (Hadoop
group
+ mapping), not from Ranger, so group-based policies need the membership
here.
+ """
+ kafka = get_docker_client().containers.get(KAFKA_CONTAINER)
+ shell = (
+ "getent group " + group + " > /dev/null || groupadd " + group + "; "
+ "id " + username + " > /dev/null 2>&1 || useradd -M -s /sbin/nologin "
+ username + "; "
+ "usermod -aG " + group + " " + username
+ )
+ exit_code, output = kafka.exec_run(["bash", "-c", shell], user="root",
demux=True)
+ if exit_code != 0:
+ raise RuntimeError(
+ "Failed to add "
+ + username
+ + " to OS group "
+ + group
+ + " in "
+ + KAFKA_CONTAINER
+ + ": "
+ + ((output[0] or b"") + (output[1] or b"")).decode()
+ )
+ logger.info("OS group membership in %s: %s -> %s", KAFKA_CONTAINER,
username, group)
+
+
+def run_kafka_producer(username, topic, message):
+ """
+ Publish one message to topic with kafka-console-producer.sh inside
ranger-kafka,
+ authenticating as username from its keytab.
+
+ Returns (exit_code, combined_output).
+ """
+ kafka = get_docker_client().containers.get(KAFKA_CONTAINER)
+ props_path = "/tmp/kafka-producer-" + username + "-" + unique_suffix(6) +
".properties"
+ properties = "\n".join(
+ [
+ "security.protocol=SASL_PLAINTEXT",
+ "sasl.mechanism=GSSAPI",
+ "sasl.kerberos.service.name=kafka",
+ "sasl.jaas.config=com.sun.security.auth.module.Krb5LoginModule
required"
+ " useKeyTab=true storeKey=true doNotPrompt=true"
+ ' keyTab="' + _user_keytab_in_kafka_container(username) + '"'
+ ' principal="' + _user_principal(username) + '";',
+ # Ranger's Kafka authorizer denies the idempotent producer's
cluster-level
+ # idempotent_write check, so publish with a plain producer.
+ "enable.idempotence=false",
+ ]
+ )
+ producer_cmd = (
+ "/opt/kafka/bin/kafka-console-producer.sh --bootstrap-server "
+ + KAFKA_BOOTSTRAP_SERVER
+ + " --topic "
+ + shlex.quote(topic)
+ + " --producer.config "
+ + props_path
+ )
+ shell = (
+ "printf '%s\\n' "
+ + shlex.quote(properties)
+ + " > "
+ + props_path
+ + "; printf '%s\\n' "
+ + shlex.quote(message)
+ + " | "
+ + producer_cmd
+ + '; rc=$?; rm -f '
+ + props_path
+ + '; exit "$rc"'
+ )
+ logger.info("Kafka producer: container=%s ranger_user=%s",
KAFKA_CONTAINER, username)
+ logger.info("RUNNING: %s", producer_cmd)
+
+ exit_code, output = kafka.exec_run(["bash", "-c", shell], user="kafka",
demux=True)
+ combined = (output[0] or b"").decode() + (output[1] or b"").decode()
+ logger.info("Kafka producer exit_code=%s output:\n%s", exit_code, combined)
+ return exit_code, combined
+
+
+def create_topic_allow_policy(session, topic, group,
service_name=KAFKA_SERVICE_NAME):
+ """Create a policy allowing group to publish to topic. Returns the policy
id."""
+ payload = {
+ "service": service_name,
+ "name": "allow publish on kafka topic " + topic + " - " +
unique_suffix(),
+ "description": "Kafka topic allow policy for functional-tests",
+ "isEnabled": True,
+ "isAuditEnabled": True,
+ "resources": {
+ "topic": {"values": [topic], "isExcludes": False, "isRecursive":
False},
+ },
+ "policyItems": [
+ {
+ "users": [],
+ "groups": [group],
+ "accesses": [
+ {"type": perm, "isAllowed": True}
+ for perm in KAFKA_TOPIC_POLICY_ACCESS_TYPES
+ ],
+ "delegateAdmin": False,
+ }
+ ],
+ }
+ response = session.post(POLICY_URL, json=payload)
+ assert_http_ok(response, (200, 201), "Create kafka topic allow policy",
POLICY_URL)
+ policy_id = response.json().get("id")
+ assert policy_id is not None, "Create policy response missing id: " +
response.text
+ return policy_id
+
+
+def snapshot_policies(session, service_name=KAFKA_SERVICE_NAME):
+ """Return every policy in the service and save them to logs/ before
anything changes."""
+ policies = list_policies_for_service(session, service_name)
+ ensure_logs_directory()
+ path = LOGS_DIR / ("kafka_policies_snapshot_" + unique_suffix() + ".json")
+ path.write_text(json.dumps(policies, indent=2))
+ logger.info("Saved %s policies from %s to %s", len(policies),
service_name, path)
+ return policies
+
+
+def restore_missing_policies(session, original_policies,
service_name=KAFKA_SERVICE_NAME):
+ """
+ Re-create each original policy whose name is not currently in the service.
+
+ Safe to call after a partial setup or teardown: policies still present are
skipped,
+ and a failed restore does not stop the others. Raises at the end if any
failed.
+ """
+ current_names = {policy["name"] for policy in
list_policies_for_service(session, service_name)}
+ restored = 0
+ failures = []
+ for policy in original_policies:
+ if policy["name"] in current_names:
+ continue
+ body = {key: value for key, value in policy.items() if key not in
("id", "guid", "version")}
+ response = session.post(POLICY_URL, json=body)
+ if response.status_code in (200, 201):
+ restored += 1
+ else:
+ failures.append(policy["name"] + ": HTTP " +
str(response.status_code) + " " + response.text)
+ logger.info("Restored %s policies in %s", restored, service_name)
+ if failures:
+ raise AssertionError(
+ "Failed to restore policies in " + service_name + ":\n" +
"\n".join(failures)
+ )
+
+
+def
wait_for_kafka_policy_propagation(seconds=KAFKA_POLICY_PROPAGATION_SLEEP_SEC):
+ logger.info("Waiting %ss for Kafka plugin policy propagation", seconds)
+ time.sleep(seconds)
diff --git a/functional-tests/readme.md b/functional-tests/readme.md
index c46bee8ec..c9f9e25af 100644
--- a/functional-tests/readme.md
+++ b/functional-tests/readme.md
@@ -31,35 +31,26 @@ A Docker-based test execution framework that validates
Apache Ranger service int
| **servicerest** | Test cases for Ranger Admin Service REST APIs |
| **tagrest** | Test cases for Ranger Admin Tag REST APIs |
| **api** | Public API v2 service and policy CRUD (admin Docker only) |
+| **api_tests** | Public API v2 user REST calls by role: admin, keyadmin,
auditor, user role, and negative cases (admin Docker only) |
| **hive** | Hive Ranger plugin enforcement via HS2/beeline (requires
`ranger-hadoop` + `ranger-hive`) |
| **hbase** | HBase Ranger plugin enforcement via HBase shell (requires
`ranger-hadoop` + `ranger-hbase`) |
+| **kafka** | Kafka Ranger plugin enforcement via console producer (requires
`ranger-kafka`) |
### Directory Structure
```text
functional-tests/
-├── multiple modules containing test cases
-├── ...
-├── ranger_test_utils/utils.py # Shared logging, HTTP asserts, Public API v2
helpers
-├── hdfs/ # Tests on HDFS encryption cycle
-├── kms/ # Tests on KMS REST API
-├── xuserrest/ # Tests on Ranger User/Group/Role REST APIs
-├── rolerest/ # Tests on Ranger Role REST APIs
-├── servicerest/ # Tests on Ranger Service REST APIs
-├── tagrest/ # Tests on Ranger Tag REST APIs
-├── api/ # Public API v2 (service / policy) tests
-├── hive/ # Hive plugin enforcement tests
-├── hbase/ # HBase plugin enforcement tests
-├── ranger_test_utils/hive_utils.py
-├── ranger_test_utils/hbase_utils.py
-├── ranger_test_utils/hdfs_utils.py
-├── ranger_test_utils/access_audit_utils.py # For future access-audit asserts
-│
-├── ranger_test_utils/utils.py # Shared logging, HTTP asserts, Public API v2
helpers
+├── <component> test modules # i.e hive,hdfs,kms etc
+├── REST API modules # xuserrest,servicerest,api,tagrest etc
+|
├── pytest.ini # Registers custom pytest markers
├── run-tests.sh # Script to automate setup and test execution
├── requirements.txt # Python dependencies
└── readme.md # This documentation
+├── ranger_test_utils/utils.py # Shared logging, HTTP asserts, Public API v2
helpers
+├── ranger_test_utils/<service>_utils.py
+├── ranger_test_utils/access_audit_utils.py # For future access-audit asserts
+
```
> **Note:** A Python virtual environment folder named `myenv` is created
> automatically
@@ -102,7 +93,7 @@ Enter DB type (press Enter to default to postgres): postgres
Available audit stores: opensearch, solr, none
Enter audit store (press Enter to default to opensearch): opensearch
-Available test suites: rolerest xuserrest servicerest tagrest hdfs kms api hive
+Available test suites: rolerest xuserrest servicerest tagrest hdfs kms api
hive ...
Enter test suites space-separated (press Enter to run ALL): hdfs kms
Available containers : hadoop hive hbase knox trino ozone kafka
@@ -222,32 +213,24 @@ Brought up based on what you enter at the containers
prompt (or `SELECTED_CONTAI
---
-### Hive suite notes
-
-- Service repo name in Docker is `dev_hive` (not QE `cm_hive`).
-- Tests currently assert **beeline authorization output only**. Hive access
audits helpers live in `ranger_test_utils/access_audit_utils.py` for a
follow-up once auditing works.
-- Example: `./run-tests.sh postgres hive`
-
-### HBase suite notes
+### Note on hive, hbase, kafka suites
+- Each suite uses its own Docker service repo: `dev_hive`, `dev_hbase`,
`dev_kafka`.
+- Tests assert on client output only (beeline, HBase shell, Kafka console
producer). Access-audit checks are deferred; helpers are in
`ranger_test_utils/access_audit_utils.py`.
+- `hive` and `hbase` start `ranger-hadoop` automatically. `kafka` starts
`ranger-kafka`.
+- Example: `SELECTED_CONTAINERS="hive hbase kafka" ./run-tests.sh postgres
hive hbase kafka`
-- Service repo name in Docker is `dev_hbase` (not QE `hbasedev`).
-- Tests currently assert **HBase shell authorization output only**.
Access-audit helpers live in `ranger_test_utils/access_audit_utils.py` for a
follow-up once auditing works.
-- Example: `./run-tests.sh postgres hbase`
-### HDFS suite notes (encryption + `hdfs_policy`)
+### HDFS suite notes
+- Service repo is `dev_hdfs`.
+- `./run-tests.sh postgres hdfs` does not start `ranger-hadoop`. Export
`SELECTED_CONTAINERS=hadoop` (or `all`) first.
+- The whole `hdfs/` folder runs. For plugin tests only: `pytest
hdfs/test_hdfs_group_policy_03.py -m hdfs_policy` (still needs
`ranger-hadoop`and admin running).
-- Service repo for plugin tests is `dev_hdfs` (seeded by ranger-docker).
-- **`./run-tests.sh postgres hdfs` does not start `ranger-hadoop` by itself.**
Export `SELECTED_CONTAINERS=hadoop` or `SELECTED_CONTAINERS=all` before running
(or choose `hadoop` / `all` at the containers prompt).
-- `./run-tests.sh` runs the whole `hdfs/` folder (no marker filter). Use `-m
hdfs_policy` only for selective local runs.
- Example:
```text
export AUDIT_INDEX_STORE=none
export SELECTED_CONTAINERS=hadoop
./run-tests.sh postgres hdfs
```
-- Plugin tests only (skips encryption/KMS session setup in `hdfs/conftest.py`):
- `pytest hdfs/test_hdfs_group_policy_03.py -m hdfs_policy`
- Still requires `ranger-hadoop` (and Ranger Admin) running.
## Test Reports
diff --git a/functional-tests/run-tests.sh b/functional-tests/run-tests.sh
index e2d797a6f..5048f410e 100755
--- a/functional-tests/run-tests.sh
+++ b/functional-tests/run-tests.sh
@@ -17,7 +17,7 @@
# All available test suites (pytest folders)
-ALL_TEST_SUITES=(rolerest xuserrest servicerest tagrest hdfs kms api api_tests
hive hbase)
+ALL_TEST_SUITES=(rolerest xuserrest servicerest tagrest hdfs kms api api_tests
hive hbase kafka)
# Test-only suites (no docker-compose file needed)
TEST_ONLY_SUITES=(rolerest xuserrest servicerest tagrest api api_tests)