|
| 1 | +# Licensed to the Apache Software Foundation (ASF) under one |
| 2 | +# or more contributor license agreements. See the NOTICE file |
| 3 | +# distributed with this work for additional information |
| 4 | +# regarding copyright ownership. The ASF licenses this file |
| 5 | +# to you under the Apache License, Version 2.0 (the |
| 6 | +# "License"); you may not use this file except in compliance |
| 7 | +# with the License. You may obtain a copy of the License at |
| 8 | +# |
| 9 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | +# |
| 11 | +# Unless required by applicable law or agreed to in writing, |
| 12 | +# software distributed under the License is distributed on an |
| 13 | +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 14 | +# KIND, either express or implied. See the License for the |
| 15 | +# specific language governing permissions and limitations |
| 16 | +# under the License. |
| 17 | + |
| 18 | + |
| 19 | +import requests |
| 20 | + |
| 21 | +POLARIS_URL = "http://localhost:8181/api/management/v1" |
| 22 | +POLARIS_TOKEN_URL = "http://localhost:8181/api/catalog/v1/oauth/tokens" |
| 23 | + |
| 24 | + |
| 25 | +def get_token(client_id: str, client_secret: str) -> str: |
| 26 | + response = requests.post( |
| 27 | + POLARIS_TOKEN_URL, |
| 28 | + data={ |
| 29 | + "grant_type": "client_credentials", |
| 30 | + "client_id": client_id, |
| 31 | + "client_secret": client_secret, |
| 32 | + "scope": "PRINCIPAL_ROLE:ALL", |
| 33 | + }, |
| 34 | + headers={"realm": "POLARIS"}, |
| 35 | + ) |
| 36 | + response.raise_for_status() |
| 37 | + return response.json()["access_token"] |
| 38 | + |
| 39 | + |
| 40 | +def provision() -> None: |
| 41 | + # Initial authentication with root credentials |
| 42 | + token = get_token("root", "s3cr3t") |
| 43 | + headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json", "realm": "POLARIS"} |
| 44 | + |
| 45 | + # 1. Create Principal |
| 46 | + principal_name = "pyiceberg_principal" |
| 47 | + principal_resp = requests.post( |
| 48 | + f"{POLARIS_URL}/principals", |
| 49 | + headers=headers, |
| 50 | + json={"name": principal_name, "type": "PRINCIPAL"}, |
| 51 | + ) |
| 52 | + if principal_resp.status_code == 409: |
| 53 | + principal_resp = requests.post( |
| 54 | + f"{POLARIS_URL}/principals/{principal_name}/rotate-credentials", |
| 55 | + headers=headers, |
| 56 | + ) |
| 57 | + principal_resp.raise_for_status() |
| 58 | + principal_data = principal_resp.json() |
| 59 | + client_id = principal_data["credentials"]["clientId"] |
| 60 | + client_secret = principal_data["credentials"]["clientSecret"] |
| 61 | + |
| 62 | + # 2. Assign service_admin role to our principal |
| 63 | + requests.put( |
| 64 | + f"{POLARIS_URL}/principals/{principal_name}/principal-roles", |
| 65 | + headers=headers, |
| 66 | + json={"principalRole": {"name": "service_admin"}}, |
| 67 | + ).raise_for_status() |
| 68 | + |
| 69 | + # 3. Create Principal Role for catalog access |
| 70 | + role_name = "pyiceberg_role" |
| 71 | + requests.post( |
| 72 | + f"{POLARIS_URL}/principal-roles", |
| 73 | + headers=headers, |
| 74 | + json={"principalRole": {"name": role_name}}, |
| 75 | + ) # Ignore error if exists |
| 76 | + |
| 77 | + # 4. Link Principal to Principal Role |
| 78 | + requests.put( |
| 79 | + f"{POLARIS_URL}/principals/{principal_name}/principal-roles", |
| 80 | + headers=headers, |
| 81 | + json={"principalRole": {"name": role_name}}, |
| 82 | + ).raise_for_status() |
| 83 | + |
| 84 | + # 5. Create Catalog |
| 85 | + catalog_name = "polaris" |
| 86 | + requests.post( |
| 87 | + f"{POLARIS_URL}/catalogs", |
| 88 | + headers=headers, |
| 89 | + json={ |
| 90 | + "catalog": { |
| 91 | + "name": catalog_name, |
| 92 | + "type": "INTERNAL", |
| 93 | + "readOnly": False, |
| 94 | + "properties": { |
| 95 | + "default-base-location": "s3://warehouse/polaris/", |
| 96 | + "polaris.config.drop-with-purge.enabled": "true", |
| 97 | + }, |
| 98 | + "storageConfigInfo": { |
| 99 | + "storageType": "S3", |
| 100 | + "allowedLocations": ["s3://warehouse/polaris/"], |
| 101 | + "region": "us-east-1", |
| 102 | + "endpoint": "http://minio:9000", |
| 103 | + }, |
| 104 | + } |
| 105 | + }, |
| 106 | + ) # Ignore error if exists |
| 107 | + |
| 108 | + # 6. Link catalog_admin role to our principal role |
| 109 | + requests.put( |
| 110 | + f"{POLARIS_URL}/principal-roles/{role_name}/catalog-roles/{catalog_name}", |
| 111 | + headers=headers, |
| 112 | + json={"catalogRole": {"name": "catalog_admin"}}, |
| 113 | + ).raise_for_status() |
| 114 | + |
| 115 | + # 7. Grant explicit privileges to catalog_admin role for this catalog |
| 116 | + for privilege in [ |
| 117 | + "CATALOG_MANAGE_CONTENT", |
| 118 | + "CATALOG_MANAGE_METADATA", |
| 119 | + "TABLE_CREATE", |
| 120 | + "TABLE_WRITE_DATA", |
| 121 | + "TABLE_LIST", |
| 122 | + "NAMESPACE_CREATE", |
| 123 | + "NAMESPACE_LIST", |
| 124 | + ]: |
| 125 | + requests.put( |
| 126 | + f"{POLARIS_URL}/catalogs/{catalog_name}/catalog-roles/catalog_admin/grants", |
| 127 | + headers=headers, |
| 128 | + json={"grant": {"type": "catalog", "privilege": privilege}}, |
| 129 | + ).raise_for_status() |
| 130 | + |
| 131 | + # Print credentials for use in CI |
| 132 | + print(f"CLIENT_ID={client_id}") |
| 133 | + print(f"CLIENT_SECRET={client_secret}") |
| 134 | + |
| 135 | + |
| 136 | +if __name__ == "__main__": |
| 137 | + provision() |
0 commit comments