Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,20 @@ protected File createTempCliConfig() throws Exception {
File tempConfigFile = File.createTempFile("ice-rest-cli-", ".yaml");
tempConfigFile.deleteOnExit();

String configContent = "uri: http://localhost:8080\n";
// Match server warehouse + MinIO S3 settings so CLI commands that read s3:// paths directly
// (e.g. describe-metadata on metadata.json) use S3FileIO against MinIO, not default AWS.
String minioEndpoint = getMinioEndpoint();
String configContent =
"uri: http://localhost:8080\n"
+ "warehouse: s3://test-bucket/warehouse\n"
+ "s3:\n"
+ " endpoint: "
+ minioEndpoint
+ "\n"
+ " pathStyleAccess: true\n"
+ " accessKeyID: minioadmin\n"
+ " secretAccessKey: minioadmin\n"
+ " region: us-east-1\n";
Files.write(tempConfigFile.toPath(), configContent.getBytes());

return tempConfigFile;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ if command -v aws &>/dev/null && [ -n "{{MINIO_ENDPOINT}}" ]; then
S3_PATH="s3://${S3_BUCKET}/${NAMESPACE_NAME}/iris_no_copy/$(basename ${INPUT_PATH})"
export AWS_ACCESS_KEY_ID=minioadmin
export AWS_SECRET_ACCESS_KEY=minioadmin
# Iceberg S3FileIO / AWS SDK v2 need a region even for MinIO endpoints.
export AWS_REGION=us-east-1
export AWS_DEFAULT_REGION=us-east-1
if aws s3 cp --endpoint-url "{{MINIO_ENDPOINT}}" "${INPUT_PATH}" "${S3_PATH}" 2>/dev/null; then
if {{ICE_CLI}} --config {{CLI_CONFIG}} create-table ${TABLE_NO_COPY} --schema-from-parquet="file://${INPUT_PATH}" 2>/dev/null && \
{{ICE_CLI}} --config {{CLI_CONFIG}} insert ${TABLE_NO_COPY} --no-copy "${S3_PATH}" 2>/dev/null; then
Expand All @@ -123,6 +126,35 @@ fi
{{ICE_CLI}} --config {{CLI_CONFIG}} files ${TABLE_SORTED} >> /tmp/basic_ops_files.txt
echo "OK Listed files in tables"

# Describe-metadata --manifests on the iris table.
# We need the current metadata.json key on S3; locate it via aws ls (best-effort).
if command -v aws &>/dev/null && [ -n "{{MINIO_ENDPOINT}}" ]; then
export AWS_ACCESS_KEY_ID=minioadmin
export AWS_SECRET_ACCESS_KEY=minioadmin
export AWS_REGION=us-east-1
export AWS_DEFAULT_REGION=us-east-1
META_PREFIX="s3://${S3_BUCKET}/warehouse/${NAMESPACE_NAME}/iris/metadata/"
META_KEY=$(aws --endpoint-url "{{MINIO_ENDPOINT}}" s3 ls "${META_PREFIX}" 2>/dev/null \
| awk '/metadata\.json$/{print $NF}' | sort | tail -1)
if [ -n "${META_KEY}" ]; then
{{ICE_CLI}} --config {{CLI_CONFIG}} describe-metadata --manifests \
"${META_PREFIX}${META_KEY}" \
> /tmp/basic_ops_describe_metadata_manifests.txt
for needle in "manifests:" ".avro" "partitionSpecId:" "dataFiles:" "PARQUET"; do
if ! grep -q "${needle}" /tmp/basic_ops_describe_metadata_manifests.txt; then
echo "FAIL: describe-metadata --manifests output missing '${needle}'"
cat /tmp/basic_ops_describe_metadata_manifests.txt
exit 1
fi
done
echo "OK describe-metadata --manifests verified for ${TABLE_IRIS}"
else
echo "SKIP describe-metadata --manifests: no metadata.json found via aws ls"
fi
else
echo "SKIP describe-metadata --manifests: aws CLI or MINIO_ENDPOINT not available"
fi

# List tables in the namespace via list-tables command
{{ICE_CLI}} --config {{CLI_CONFIG}} list-tables ${NAMESPACE_NAME} > /tmp/basic_ops_list_tables.txt
for t in ${TABLE_IRIS} ${TABLE_PARTITIONED} ${TABLE_SORTED}; do
Expand All @@ -134,6 +166,7 @@ for t in ${TABLE_IRIS} ${TABLE_PARTITIONED} ${TABLE_SORTED}; do
done
echo "OK list-tables listed tables in ${NAMESPACE_NAME}"


# Cleanup tables then namespace
{{ICE_CLI}} --config {{CLI_CONFIG}} delete-table ${TABLE_IRIS}
{{ICE_CLI}} --config {{CLI_CONFIG}} delete-table ${TABLE_PARTITIONED}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,21 @@ for f in /tmp/basic_ops_scan_iris.txt /tmp/basic_ops_scan_partitioned.txt /tmp/b
fi
done

# Verify describe-metadata --manifests output if it was produced (aws available in run.sh)
DM="/tmp/basic_ops_describe_metadata_manifests.txt"
if [ -f "$DM" ]; then
if [ ! -s "$DM" ]; then
echo "FAIL $DM exists but is empty"
exit 1
fi
for needle in "manifests:" ".avro" "partitionSpecId:" "dataFiles:" "PARQUET"; do
if ! grep -q "${needle}" "$DM"; then
echo "FAIL $DM missing '${needle}'"
exit 1
fi
done
fi

# Verify files output contains expected structure (Snapshots/Snapshot/Manifest/Datafile and S3 paths)
F="/tmp/basic_ops_files.txt"
if [ ! -f "$F" ] || [ ! -s "$F" ]; then
Expand Down Expand Up @@ -81,6 +96,7 @@ for t in test_ns.iris test_ns.taxis_p_by_day test_ns.taxis_s_by_day; do
done

# Cleanup temp files
rm -f /tmp/basic_ops_list_namespace.txt /tmp/basic_ops_describe.txt /tmp/basic_ops_scan_iris.txt /tmp/basic_ops_scan_partitioned.txt /tmp/basic_ops_scan_sorted.txt /tmp/basic_ops_files.txt /tmp/basic_ops_describe_metadata_manifests.txt
rm -f /tmp/basic_ops_list_namespace.txt /tmp/basic_ops_describe.txt /tmp/basic_ops_scan_iris.txt /tmp/basic_ops_scan_partitioned.txt /tmp/basic_ops_scan_sorted.txt /tmp/basic_ops_files.txt /tmp/basic_ops_list_tables.txt

echo "OK Verification passed"
Expand Down
65 changes: 65 additions & 0 deletions ice/src/main/java/com/altinity/ice/cli/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.altinity.ice.cli.internal.cmd.DeleteNamespace;
import com.altinity.ice.cli.internal.cmd.DeleteTable;
import com.altinity.ice.cli.internal.cmd.Describe;
import com.altinity.ice.cli.internal.cmd.DescribeMetadata;
import com.altinity.ice.cli.internal.cmd.DescribeParquet;
import com.altinity.ice.cli.internal.cmd.Files;
import com.altinity.ice.cli.internal.cmd.Insert;
Expand Down Expand Up @@ -237,6 +238,70 @@ void describeParquet(
}
}

@CommandLine.Command(name = "describe-metadata", description = "Describe Iceberg metadata file.")
void describeMetadata(
@CommandLine.Parameters(
arity = "1",
paramLabel = "<target>",
description = "Path to metadata.json file")
String target,
@CommandLine.Option(
names = {"-a", "--all"},
description = "Show everything")
boolean showAll,
@CommandLine.Option(
names = {"-s", "--summary"},
description = "Show table UUID, format version, location, current snapshot, etc.")
boolean showSummary,
@CommandLine.Option(
names = {"-S", "--schema"},
description = "Show full schema with field IDs and types")
boolean showSchema,
@CommandLine.Option(
names = {"--snapshots"},
description = "List all snapshots")
boolean showSnapshots,
@CommandLine.Option(
names = {"--history"},
description = "Show snapshot log and metadata log")
boolean showHistory,
@CommandLine.Option(
names = {"--manifests"},
description = "Drill into manifest list for current snapshot")
boolean showManifests,
@CommandLine.Option(
names = {"--json"},
description = "Output JSON instead of YAML")
boolean json)
throws IOException {
Config config = Config.load(configFile());
var icebergConfig = config.toIcebergConfig();

var options = new ArrayList<DescribeMetadata.Option>();
if (showAll || showSummary) {
options.add(DescribeMetadata.Option.SUMMARY);
}
if (showAll || showSchema) {
options.add(DescribeMetadata.Option.SCHEMA);
}
if (showAll || showSnapshots) {
options.add(DescribeMetadata.Option.SNAPSHOTS);
}
if (showAll || showHistory) {
options.add(DescribeMetadata.Option.HISTORY);
}
if (showAll || showManifests) {
options.add(DescribeMetadata.Option.MANIFESTS);
}

if (options.isEmpty()) {
options.add(DescribeMetadata.Option.SUMMARY);
}

DescribeMetadata.run(
icebergConfig, target, json, options.toArray(new DescribeMetadata.Option[0]));
}

public record IceSortOrder(
@JsonProperty("column") String column,
@JsonProperty("desc") boolean desc,
Expand Down
Loading
Loading