Skip to content

Commit

Permalink
[apache#5061] Basic user and group CLI (apache#5133)
Browse files Browse the repository at this point in the history
### What changes were proposed in this pull request?

Added basic user and group commands.

### Why are the changes needed?

For the CLI to support users and groups.

Fix: apache#5061

### Does this PR introduce _any_ user-facing change?

No, but extends CLI support.

### How was this patch tested?

Tested locally.
  • Loading branch information
justinmclean authored Nov 8, 2024
1 parent 72b92d6 commit 3a21ce4
Show file tree
Hide file tree
Showing 17 changed files with 758 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public class CommandEntities {
public static final String SCHEMA = "schema";
public static final String TABLE = "table";
public static final String COLUMN = "column";
public static final String USER = "user";
public static final String GROUP = "group";

private static final HashSet<String> VALID_ENTITIES = new HashSet<>();

Expand All @@ -40,6 +42,8 @@ public class CommandEntities {
VALID_ENTITIES.add(SCHEMA);
VALID_ENTITIES.add(TABLE);
VALID_ENTITIES.add(COLUMN);
VALID_ENTITIES.add(USER);
VALID_ENTITIES.add(GROUP);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,8 @@ public class ErrorMessages {
public static final String METALAKE_EXISTS = "Metalake already exists.";
public static final String CATALOG_EXISTS = "Catalog already exists.";
public static final String SCHEMA_EXISTS = "Schema already exists.";
public static final String UNKNOWN_USER = "Unknown user.";
public static final String USER_EXISTS = "User already exists.";
public static final String UNKNOWN_GROUP = "Unknown group.";
public static final String GROUP_EXISTS = "Group already exists.";
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,27 @@
import org.apache.gravitino.cli.commands.CatalogDetails;
import org.apache.gravitino.cli.commands.ClientVersion;
import org.apache.gravitino.cli.commands.CreateCatalog;
import org.apache.gravitino.cli.commands.CreateGroup;
import org.apache.gravitino.cli.commands.CreateMetalake;
import org.apache.gravitino.cli.commands.CreateSchema;
import org.apache.gravitino.cli.commands.CreateUser;
import org.apache.gravitino.cli.commands.DeleteCatalog;
import org.apache.gravitino.cli.commands.DeleteGroup;
import org.apache.gravitino.cli.commands.DeleteMetalake;
import org.apache.gravitino.cli.commands.DeleteSchema;
import org.apache.gravitino.cli.commands.DeleteTable;
import org.apache.gravitino.cli.commands.DeleteUser;
import org.apache.gravitino.cli.commands.GroupDetails;
import org.apache.gravitino.cli.commands.ListCatalogProperties;
import org.apache.gravitino.cli.commands.ListCatalogs;
import org.apache.gravitino.cli.commands.ListColumns;
import org.apache.gravitino.cli.commands.ListGroups;
import org.apache.gravitino.cli.commands.ListMetalakeProperties;
import org.apache.gravitino.cli.commands.ListMetalakes;
import org.apache.gravitino.cli.commands.ListSchema;
import org.apache.gravitino.cli.commands.ListSchemaProperties;
import org.apache.gravitino.cli.commands.ListTables;
import org.apache.gravitino.cli.commands.ListUsers;
import org.apache.gravitino.cli.commands.MetalakeAuditInfo;
import org.apache.gravitino.cli.commands.MetalakeDetails;
import org.apache.gravitino.cli.commands.RemoveCatalogProperty;
Expand All @@ -55,6 +62,7 @@
import org.apache.gravitino.cli.commands.UpdateCatalogName;
import org.apache.gravitino.cli.commands.UpdateMetalakeComment;
import org.apache.gravitino.cli.commands.UpdateMetalakeName;
import org.apache.gravitino.cli.commands.UserDetails;

/* Gravitino Command line */
public class GravitinoCommandLine {
Expand Down Expand Up @@ -152,6 +160,10 @@ private void executeCommand() {
handleCatalogCommand();
} else if (entity.equals(CommandEntities.METALAKE)) {
handleMetalakeCommand();
} else if (entity.equals(CommandEntities.USER)) {
handleUserCommand();
} else if (entity.equals(CommandEntities.GROUP)) {
handleGroupCommand();
}
}

Expand Down Expand Up @@ -217,7 +229,7 @@ private void handleCatalogCommand() {
} else if (CommandActions.CREATE.equals(command)) {
String comment = line.getOptionValue(GravitinoOptions.COMMENT);
String provider = line.getOptionValue(GravitinoOptions.PROVIDER);
String properties = line.getOptionValue(GravitinoOptions.PROPERTIES);
String[] properties = line.getOptionValues(GravitinoOptions.PROPERTIES);
Map<String, String> propertyMap = new Properties().parse(properties);
new CreateCatalog(url, ignore, metalake, catalog, provider, comment, propertyMap).handle();
} else if (CommandActions.DELETE.equals(command)) {
Expand Down Expand Up @@ -304,6 +316,42 @@ private void handleTableCommand() {
}
}

/** Handles the command execution for Users based on command type and the command line options. */
protected void handleUserCommand() {
String url = getUrl();
FullName name = new FullName(line);
String metalake = name.getMetalakeName();
String user = line.getOptionValue(GravitinoOptions.USER);

if (CommandActions.DETAILS.equals(command)) {
new UserDetails(url, ignore, metalake, user).handle();
} else if (CommandActions.LIST.equals(command)) {
new ListUsers(url, ignore, metalake).handle();
} else if (CommandActions.CREATE.equals(command)) {
new CreateUser(url, ignore, metalake, user).handle();
} else if (CommandActions.DELETE.equals(command)) {
new DeleteUser(url, ignore, metalake, user).handle();
}
}

/** Handles the command execution for Group based on command type and the command line options. */
protected void handleGroupCommand() {
String url = getUrl();
FullName name = new FullName(line);
String metalake = name.getMetalakeName();
String group = line.getOptionValue(GravitinoOptions.GROUP);

if (CommandActions.DETAILS.equals(command)) {
new GroupDetails(url, ignore, metalake, group).handle();
} else if (CommandActions.LIST.equals(command)) {
new ListGroups(url, ignore, metalake).handle();
} else if (CommandActions.CREATE.equals(command)) {
new CreateGroup(url, ignore, metalake, group).handle();
} else if (CommandActions.DELETE.equals(command)) {
new DeleteGroup(url, ignore, metalake, group).handle();
}
}

/**
* Handles the command execution for Columns based on command type and the command line options.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public class GravitinoOptions {
public static final String VALUE = "value";
public static final String PROVIDER = "provider";
public static final String PROPERTIES = "properties";
public static final String USER = "user";
public static final String GROUP = "group";
public static final String AUDIT = "audit";

/**
Expand Down Expand Up @@ -64,12 +66,13 @@ public Options options() {
options.addOption(createArgOption("V", VALUE, "property value"));
options.addOption(
createArgOption(
"g", PROVIDER, "provider one of hadoop, hive, mysql, postgres, iceberg, kafka"));
"t", PROVIDER, "provider one of hadoop, hive, mysql, postgres, iceberg, kafka"));
options.addOption(createArgOption("l", USER, "user name"));
options.addOption(createArgOption("g", GROUP, "group name"));

// Properties option can have multiple values
Option properties =
createArgOption("p", PROPERTIES, "comma separated property name/value pairs");
properties.hasArgs();
Option.builder("p").longOpt(PROPERTIES).desc("property name/value pairs").hasArgs().build();
options.addOption(properties);

return options;
Expand Down
20 changes: 11 additions & 9 deletions clients/cli/src/main/java/org/apache/gravitino/cli/Properties.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,21 @@ public Properties(String delimiter, String keyValueSeparator) {
* <p>Each pair in the input string is split by the specified delimiter, and then each pair is
* further split by the key-value separator.
*
* @param input The input string containing name-value pairs.
* @param inputs An arrays of input strings containing name-value pairs.
* @return A map of entries, where each entry represents a key-value pair from the input string.
*/
public Map<String, String> parse(String input) {
public Map<String, String> parse(String[] inputs) {
HashMap<String, String> map = new HashMap<>();

// Split the input by the delimiter into key-value pairs
String[] pairs = input.split(delimiter);
for (String pair : pairs) {
// Split each key-value pair by the separator
String[] keyValue = pair.split(keyValueSeparator, 2);
if (keyValue.length == 2) {
map.put(keyValue[0].trim(), keyValue[1].trim());
for (String input : inputs) {
// Split the input by the delimiter into key-value pairs
String[] pairs = input.split(delimiter);
for (String pair : pairs) {
// Split each key-value pair by the separator
String[] keyValue = pair.split(keyValueSeparator, 2);
if (keyValue.length == 2) {
map.put(keyValue[0].trim(), keyValue[1].trim());
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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.
*/

package org.apache.gravitino.cli.commands;

import org.apache.gravitino.cli.ErrorMessages;
import org.apache.gravitino.client.GravitinoClient;
import org.apache.gravitino.exceptions.GroupAlreadyExistsException;
import org.apache.gravitino.exceptions.NoSuchMetalakeException;

public class CreateGroup extends Command {
protected final String metalake;
protected final String group;

/**
* Create a new group.
*
* @param url The URL of the Gravitino server.
* @param ignoreVersions If true don't check the client/server versions match.
* @param metalake The name of the metalake.
* @param group The name of the group.
*/
public CreateGroup(String url, boolean ignoreVersions, String metalake, String group) {
super(url, ignoreVersions);
this.metalake = metalake;
this.group = group;
}

/** Create a new group. */
@Override
public void handle() {
try {
GravitinoClient client = buildClient(metalake);
client.addGroup(group);
} catch (NoSuchMetalakeException err) {
System.err.println(ErrorMessages.UNKNOWN_METALAKE);
return;
} catch (GroupAlreadyExistsException err) {
System.err.println(ErrorMessages.GROUP_EXISTS);
return;
} catch (Exception exp) {
System.err.println(exp.getMessage());
return;
}

System.out.println(group + " created");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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.
*/

package org.apache.gravitino.cli.commands;

import org.apache.gravitino.cli.ErrorMessages;
import org.apache.gravitino.client.GravitinoClient;
import org.apache.gravitino.exceptions.NoSuchMetalakeException;
import org.apache.gravitino.exceptions.UserAlreadyExistsException;

public class CreateUser extends Command {
protected final String metalake;
protected final String user;

/**
* Create a new User.
*
* @param url The URL of the Gravitino server.
* @param ignoreVersions If true don't check the client/server versions match.
* @param metalake The name of the metalake.
* @param user The name of the user.
*/
public CreateUser(String url, boolean ignoreVersions, String metalake, String user) {
super(url, ignoreVersions);
this.metalake = metalake;
this.user = user;
}

/** Create a new user. */
@Override
public void handle() {
try {
GravitinoClient client = buildClient(metalake);
client.addUser(user);
} catch (NoSuchMetalakeException err) {
System.err.println(ErrorMessages.UNKNOWN_METALAKE);
return;
} catch (UserAlreadyExistsException err) {
System.err.println(ErrorMessages.USER_EXISTS);
return;
} catch (Exception exp) {
System.err.println(exp.getMessage());
return;
}

System.out.println(user + " created");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* 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.
*/

package org.apache.gravitino.cli.commands;

import org.apache.gravitino.cli.ErrorMessages;
import org.apache.gravitino.client.GravitinoClient;
import org.apache.gravitino.exceptions.NoSuchGroupException;
import org.apache.gravitino.exceptions.NoSuchMetalakeException;

public class DeleteGroup extends Command {

protected final String metalake;
protected final String group;

/**
* Delete a group.
*
* @param url The URL of the Gravitino server.
* @param ignoreVersions If true don't check the client/server versions match.
* @param metalake The name of the metalake.
* @param group The name of the group.
*/
public DeleteGroup(String url, boolean ignoreVersions, String metalake, String group) {
super(url, ignoreVersions);
this.metalake = metalake;
this.group = group;
}

/** Delete a group. */
@Override
public void handle() {
boolean deleted = false;

try {
GravitinoClient client = buildClient(metalake);
deleted = client.removeGroup(group);
} catch (NoSuchMetalakeException err) {
System.err.println(ErrorMessages.UNKNOWN_METALAKE);
return;
} catch (NoSuchGroupException err) {
System.err.println(ErrorMessages.UNKNOWN_GROUP);
return;
} catch (Exception exp) {
System.err.println(exp.getMessage());
return;
}

if (deleted) {
System.out.println(group + " deleted.");
} else {
System.out.println(group + " not deleted.");
}
}
}
Loading

0 comments on commit 3a21ce4

Please sign in to comment.