1+ #! /bin/bash
2+ #
3+ # Licensed to the Apache Software Foundation (ASF) under one
4+ # or more contributor license agreements. See the NOTICE file
5+ # distributed with this work for additional information
6+ # regarding copyright ownership. The ASF licenses this file
7+ # to you under the Apache License, Version 2.0 (the
8+ # "License"); you may not use this file except in compliance
9+ # with the License. You may obtain a copy of the License at
10+ #
11+ # http://www.apache.org/licenses/LICENSE-2.0
12+ #
13+ # Unless required by applicable law or agreed to in writing,
14+ # software distributed under the License is distributed on an
15+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+ # KIND, either express or implied. See the License for the
17+ # specific language governing permissions and limitations
18+ # under the License.
19+ #
20+
21+ # This test creates an INTERNAL catalog and an EXTERNAL catalog with passthrough facade
22+ # to demonstrate true catalog federation.
23+
24+ set -e
25+
26+
27+ SPARK_BEARER_TOKEN=" ${REGTEST_ROOT_BEARER_TOKEN} "
28+
29+ echo " === Setting up Catalog Federation Test ==="
30+
31+ # Step 1: Create a new principal
32+ echo " Creating new principal..."
33+ PRINCIPAL_RESPONSE=$( curl -s -X POST -H " Authorization: Bearer ${SPARK_BEARER_TOKEN} " -H ' Content-Type: application/json' \
34+ http://${POLARIS_HOST:- localhost} :8181/api/management/v1/principals \
35+ -d ' {
36+ "principal": {
37+ "name": "new-user"
38+ }
39+ }' )
40+
41+ NEW_CLIENT_ID=$( echo " $PRINCIPAL_RESPONSE " | jq -r ' .credentials.clientId' )
42+ NEW_CLIENT_SECRET=$( echo " $PRINCIPAL_RESPONSE " | jq -r ' .credentials.clientSecret' )
43+
44+ # Step 2: Create local catalog
45+ echo " Creating local catalog..."
46+ RESPONSE_CODE=$( curl -s -X POST -H " Authorization: Bearer ${SPARK_BEARER_TOKEN} " -H ' Content-Type: application/json' \
47+ http://${POLARIS_HOST:- localhost} :8181/api/management/v1/catalogs \
48+ -d ' {
49+ "type": "INTERNAL",
50+ "name": "test-catalog-local",
51+ "properties": {
52+ "default-base-location": "file:///tmp/warehouse"
53+ },
54+ "storageConfigInfo": {
55+ "storageType": "FILE",
56+ "allowedLocations": ["file:///tmp/warehouse"]
57+ }
58+ }' \
59+ --write-out " %{http_code}" )
60+ echo " Create local catalog response code: $RESPONSE_CODE "
61+
62+
63+
64+ # Step 3: Grant permissions
65+ echo " Setting up permissions..."
66+
67+ # Grant TABLE_WRITE_DATA privilege to catalog_admin for local catalog
68+ RESPONSE_CODE=$( curl -s -X PUT -H " Authorization: Bearer ${SPARK_BEARER_TOKEN} " -H ' Accept: application/json' -H ' Content-Type: application/json' \
69+ http://${POLARIS_HOST:- localhost} :8181/api/management/v1/catalogs/test-catalog-local/catalog-roles/catalog_admin/grants \
70+ -d ' {"type": "catalog", "privilege": "TABLE_WRITE_DATA"}' \
71+ --write-out " %{http_code}" )
72+ echo " Grant TABLE_WRITE_DATA to catalog_admin response code: $RESPONSE_CODE "
73+
74+ # Assign catalog_admin to service_admin
75+ RESPONSE_CODE=$( curl -s -X PUT -H " Authorization: Bearer ${SPARK_BEARER_TOKEN} " -H ' Accept: application/json' -H ' Content-Type: application/json' \
76+ http://${POLARIS_HOST:- localhost} :8181/api/management/v1/principal-roles/service_admin/catalog-roles/test-catalog-local \
77+ -d ' {"name": "catalog_admin"}' \
78+ --write-out " %{http_code}" )
79+ echo " Assign catalog_admin to service_admin response code: $RESPONSE_CODE "
80+
81+ # Assign service_admin to new-user
82+ RESPONSE_CODE=$( curl -s -X PUT -H " Authorization: Bearer ${SPARK_BEARER_TOKEN} " -H ' Accept: application/json' -H ' Content-Type: application/json' \
83+ http://${POLARIS_HOST:- localhost} :8181/api/management/v1/principals/new-user/principal-roles \
84+ -d ' {"name": "service_admin"}' \
85+ --write-out " %{http_code}" )
86+ echo " Assign service_admin to new-user response code: $RESPONSE_CODE "
87+
88+ # Step 4: Create external catalog
89+ echo " Creating external catalog (passthrough facade)..."
90+ RESPONSE_CODE=$( curl -s -X POST -H " Authorization: Bearer ${SPARK_BEARER_TOKEN} " -H ' Content-Type: application/json' \
91+ http://${POLARIS_HOST:- localhost} :8181/api/management/v1/catalogs \
92+ -d " {
93+ \" type\" : \" EXTERNAL\" ,
94+ \" name\" : \" test-catalog-external\" ,
95+ \" connectionConfigInfo\" : {
96+ \" connectionType\" : \" ICEBERG_REST\" ,
97+ \" uri\" : \" http://${POLARIS_HOST:- localhost} :8181/api/catalog\" ,
98+ \" remoteCatalogName\" : \" test-catalog-local\" ,
99+ \" authenticationParameters\" : {
100+ \" authenticationType\" : \" OAUTH\" ,
101+ \" tokenUri\" : \" http://${POLARIS_HOST:- localhost} :8181/api/catalog/v1/oauth/tokens\" ,
102+ \" clientId\" : \" ${NEW_CLIENT_ID} \" ,
103+ \" clientSecret\" : \" ${NEW_CLIENT_SECRET} \" ,
104+ \" scopes\" : [\" PRINCIPAL_ROLE:ALL\" ]
105+ }
106+ },
107+ \" properties\" : {
108+ \" default-base-location\" : \" file:///tmp/warehouse\"
109+ },
110+ \" storageConfigInfo\" : {
111+ \" storageType\" : \" FILE\" ,
112+ \" allowedLocations\" : [\" file:///tmp/warehouse\" ]
113+ }
114+ }" \
115+ --write-out " %{http_code}" )
116+ echo " Create external catalog response code: $RESPONSE_CODE "
117+
118+ # Step 5: Grant permissions for external catalog
119+ echo " Setting up permissions for external catalog..."
120+
121+ # Grant TABLE_WRITE_DATA privilege to catalog_admin role for test-catalog-external
122+ RESPONSE_CODE=$( curl -s -X PUT -H " Authorization: Bearer ${SPARK_BEARER_TOKEN} " -H ' Accept: application/json' -H ' Content-Type: application/json' \
123+ http://${POLARIS_HOST:- localhost} :8181/api/management/v1/catalogs/test-catalog-external/catalog-roles/catalog_admin/grants \
124+ -d ' {"type": "catalog", "privilege": "TABLE_WRITE_DATA"}' \
125+ --write-out " %{http_code}" )
126+ echo " Grant TABLE_WRITE_DATA to external catalog_admin response code: $RESPONSE_CODE "
127+
128+ # Assign catalog_admin role to service_admin principal-role for test-catalog-external
129+ RESPONSE_CODE=$( curl -s -X PUT -H " Authorization: Bearer ${SPARK_BEARER_TOKEN} " -H ' Accept: application/json' -H ' Content-Type: application/json' \
130+ http://${POLARIS_HOST:- localhost} :8181/api/management/v1/principal-roles/service_admin/catalog-roles/test-catalog-external \
131+ -d ' {"name": "catalog_admin"}' \
132+ --write-out " %{http_code}" )
133+ echo " Assign catalog_admin to service_admin for external catalog response code: $RESPONSE_CODE "
134+
135+ echo " Catalogs created successfully"
136+
137+ echo " "
138+ echo " === Starting federation test ==="
139+
140+ # Test data operations via local catalog
141+ echo " === Creating data via LOCAL catalog ==="
142+ cat << EOF | ${SPARK_HOME} /bin/spark-sql -S --conf spark.sql.catalog.polaris.token="${SPARK_BEARER_TOKEN} " --conf spark.sql.catalog.polaris.warehouse=test-catalog-local --conf spark.sql.defaultCatalog=polaris --conf spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions
143+ use polaris;
144+ create namespace if not exists ns1;
145+ create table if not exists ns1.test_table (id int, name string);
146+ insert into ns1.test_table values (1, 'Alice');
147+ insert into ns1.test_table values (2, 'Bob');
148+ create namespace if not exists ns2;
149+ create table if not exists ns2.test_table (id int, name string);
150+ insert into ns2.test_table values (1, 'Apache Spark');
151+ insert into ns2.test_table values (2, 'Apache Iceberg');
152+ EOF
153+
154+ echo " "
155+ echo " === Accessing data via EXTERNAL catalog ==="
156+ cat << EOF | ${SPARK_HOME} /bin/spark-sql -S --conf spark.sql.catalog.polaris.token="${SPARK_BEARER_TOKEN} " --conf spark.sql.catalog.polaris.warehouse=test-catalog-external --conf spark.sql.defaultCatalog=polaris --conf spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions
157+ use polaris;
158+ show namespaces;
159+ select * from ns1.test_table order by id;
160+ insert into ns1.test_table values (3, 'Charlie');
161+ select * from ns2.test_table order by id;
162+ insert into ns2.test_table values (3, 'Apache Polaris');
163+ EOF
164+
165+ echo " "
166+ echo " === Verifying federation via LOCAL catalog ==="
167+ cat << EOF | ${SPARK_HOME} /bin/spark-sql -S --conf spark.sql.catalog.polaris.token="${SPARK_BEARER_TOKEN} " --conf spark.sql.catalog.polaris.warehouse=test-catalog-local --conf spark.sql.defaultCatalog=polaris --conf spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions
168+ use polaris;
169+ select * from ns1.test_table order by id;
170+ select * from ns2.test_table order by id;
171+ drop table ns1.test_table;
172+ drop table ns2.test_table;
173+ drop namespace ns1;
174+ drop namespace ns2;
175+ EOF
176+
177+ echo " "
178+ echo " === Cleaning up catalogs and principal ==="
179+ # Clean up catalogs
180+ RESPONSE_CODE=$( curl -X DELETE -H " Authorization: Bearer ${SPARK_BEARER_TOKEN} " -H ' Accept: application/json' -H ' Content-Type: application/json' \
181+ http://${POLARIS_HOST:- localhost} :8181/api/management/v1/catalogs/test-catalog-external \
182+ --write-out " %{http_code}" )
183+ echo " Delete external catalog response code: $RESPONSE_CODE "
184+
185+ RESPONSE_CODE=$( curl -X DELETE -H " Authorization: Bearer ${SPARK_BEARER_TOKEN} " -H ' Accept: application/json' -H ' Content-Type: application/json' \
186+ http://${POLARIS_HOST:- localhost} :8181/api/management/v1/catalogs/test-catalog-local \
187+ --write-out " %{http_code}" )
188+ echo " Delete local catalog response code: $RESPONSE_CODE "
189+
190+ # Clean up principal
191+ RESPONSE_CODE=$( curl -X DELETE -H " Authorization: Bearer ${SPARK_BEARER_TOKEN} " -H ' Accept: application/json' -H ' Content-Type: application/json' \
192+ http://${POLARIS_HOST:- localhost} :8181/api/management/v1/principals/new-user \
193+ --write-out " %{http_code}" )
194+ echo " Delete principal response code: $RESPONSE_CODE "
195+
196+ echo " Catalog federation test completed successfully!"
0 commit comments