fix(bootstrap): Creating dedicated thread pool for executing async bootstrap steps + misc fixes (#5798)

This commit is contained in:
John Joyce 2022-08-31 19:34:17 -07:00 committed by GitHub
parent af1fc8d91d
commit ca29f8b679
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 40 additions and 23 deletions

View File

@ -71,8 +71,8 @@ export const LogIn: React.VFC<LogInProps> = () => {
analytics.event({ type: EventType.LogInEvent }); analytics.event({ type: EventType.LogInEvent });
return Promise.resolve(); return Promise.resolve();
}) })
.catch((error) => { .catch((_) => {
message.error(`Failed to log in! ${error}`); message.error(`Failed to log in! An unexpected error occurred.`);
}) })
.finally(() => setLoading(false)); .finally(() => setLoading(false));
}, },

View File

@ -75,8 +75,8 @@ export const ResetCredentials: React.VFC<ResetCredentialsProps> = () => {
analytics.event({ type: EventType.ResetCredentialsEvent }); analytics.event({ type: EventType.ResetCredentialsEvent });
return Promise.resolve(); return Promise.resolve();
}) })
.catch((error) => { .catch((_) => {
message.error(`Failed to log in! ${error}`); message.error(`Failed to log in!`);
}) })
.finally(() => setLoading(false)); .finally(() => setLoading(false));
}, },

View File

@ -90,8 +90,8 @@ export const SignUp: React.VFC<SignUpProps> = () => {
analytics.event({ type: EventType.SignUpEvent, title: values.title }); analytics.event({ type: EventType.SignUpEvent, title: values.title });
return Promise.resolve(); return Promise.resolve();
}) })
.catch((error) => { .catch((_) => {
message.error(`Failed to log in! ${error}`); message.error(`Failed to log in! An unexpected error occurred.`);
}) })
.finally(() => setLoading(false)); .finally(() => setLoading(false));
}, },

View File

@ -52,6 +52,7 @@ export function HeaderLinks(props: Props) {
const showSettings = true; const showSettings = true;
const showIngestion = const showIngestion =
isIngestionEnabled && me && me.platformPrivileges.manageIngestion && me.platformPrivileges.manageSecrets; isIngestionEnabled && me && me.platformPrivileges.manageIngestion && me.platformPrivileges.manageSecrets;
const showDomains = me?.platformPrivileges.createDomains || me?.platformPrivileges.manageDomains;
return ( return (
<LinksWrapper areLinksHidden={areLinksHidden}> <LinksWrapper areLinksHidden={areLinksHidden}>
@ -82,11 +83,13 @@ export function HeaderLinks(props: Props) {
<BookOutlined style={{ fontSize: '14px', fontWeight: 'bold' }} /> Glossary <BookOutlined style={{ fontSize: '14px', fontWeight: 'bold' }} /> Glossary
</Link> </Link>
</MenuItem> </MenuItem>
<MenuItem key="1"> {showDomains && (
<Link to="/domains"> <MenuItem key="1">
<FolderOutlined style={{ fontSize: '14px', fontWeight: 'bold' }} /> Domains <Link to="/domains">
</Link> <FolderOutlined style={{ fontSize: '14px', fontWeight: 'bold' }} /> Domains
</MenuItem> </Link>
</MenuItem>
)}
</Menu> </Menu>
} }
> >

View File

@ -61,7 +61,7 @@ public class NativeUserService {
} }
public void createNativeUser(@Nonnull String userUrnString, @Nonnull String fullName, @Nonnull String email, public void createNativeUser(@Nonnull String userUrnString, @Nonnull String fullName, @Nonnull String email,
@Nonnull String title, @Nonnull String password, @Nonnull String inviteToken, Authentication authentication) @Nonnull String title, @Nonnull String password, @Nonnull String inviteToken, @Nonnull Authentication authentication)
throws Exception { throws Exception {
Objects.requireNonNull(userUrnString, "userUrnSting must not be null!"); Objects.requireNonNull(userUrnString, "userUrnSting must not be null!");
Objects.requireNonNull(fullName, "fullName must not be null!"); Objects.requireNonNull(fullName, "fullName must not be null!");
@ -69,6 +69,7 @@ public class NativeUserService {
Objects.requireNonNull(title, "title must not be null!"); Objects.requireNonNull(title, "title must not be null!");
Objects.requireNonNull(password, "password must not be null!"); Objects.requireNonNull(password, "password must not be null!");
Objects.requireNonNull(inviteToken, "inviteToken must not be null!"); Objects.requireNonNull(inviteToken, "inviteToken must not be null!");
Objects.requireNonNull(inviteToken, "authentication must not be null!");
InviteToken inviteTokenAspect = InviteToken inviteTokenAspect =
(InviteToken) _entityService.getLatestAspect(Urn.createFromString(GLOBAL_INVITE_TOKEN), (InviteToken) _entityService.getLatestAspect(Urn.createFromString(GLOBAL_INVITE_TOKEN),
@ -125,7 +126,7 @@ public class NativeUserService {
} }
void updateCorpUserCredentials(@Nonnull Urn userUrn, @Nonnull String password, void updateCorpUserCredentials(@Nonnull Urn userUrn, @Nonnull String password,
Authentication authentication) throws Exception { @Nonnull Authentication authentication) throws Exception {
// Construct corpUserCredentials // Construct corpUserCredentials
CorpUserCredentials corpUserCredentials = new CorpUserCredentials(); CorpUserCredentials corpUserCredentials = new CorpUserCredentials();
final byte[] salt = getRandomBytes(SALT_TOKEN_LENGTH); final byte[] salt = getRandomBytes(SALT_TOKEN_LENGTH);

View File

@ -165,11 +165,12 @@ public class AuthServiceController {
String titleString = title.asText(); String titleString = title.asText();
String passwordString = password.asText(); String passwordString = password.asText();
String inviteTokenString = inviteToken.asText(); String inviteTokenString = inviteToken.asText();
Authentication auth = AuthenticationContext.getAuthentication();
log.debug(String.format("Attempting to create credentials for native user %s", userUrnString)); log.debug(String.format("Attempting to create credentials for native user %s", userUrnString));
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
try { try {
_nativeUserService.createNativeUser(userUrnString, fullNameString, emailString, titleString, passwordString, _nativeUserService.createNativeUser(userUrnString, fullNameString, emailString, titleString, passwordString,
inviteTokenString, AuthenticationContext.getAuthentication()); inviteTokenString, auth);
String response = buildSignUpResponse(); String response = buildSignUpResponse();
return new ResponseEntity<>(response, HttpStatus.OK); return new ResponseEntity<>(response, HttpStatus.OK);
} catch (Exception e) { } catch (Exception e) {
@ -225,11 +226,12 @@ public class AuthServiceController {
String userUrnString = userUrn.asText(); String userUrnString = userUrn.asText();
String passwordString = password.asText(); String passwordString = password.asText();
String resetTokenString = resetToken.asText(); String resetTokenString = resetToken.asText();
Authentication auth = AuthenticationContext.getAuthentication();
log.debug(String.format("Attempting to reset credentials for native user %s", userUrnString)); log.debug(String.format("Attempting to reset credentials for native user %s", userUrnString));
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
try { try {
_nativeUserService.resetCorpUserCredentials(userUrnString, passwordString, resetTokenString, _nativeUserService.resetCorpUserCredentials(userUrnString, passwordString, resetTokenString,
AuthenticationContext.getAuthentication()); auth);
String response = buildResetNativeUserCredentialsResponse(); String response = buildResetNativeUserCredentialsResponse();
return new ResponseEntity<>(response, HttpStatus.OK); return new ResponseEntity<>(response, HttpStatus.OK);
} catch (Exception e) { } catch (Exception e) {

View File

@ -2,6 +2,8 @@ package com.linkedin.metadata.boot;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -13,6 +15,7 @@ import org.springframework.stereotype.Component;
@Component @Component
public class BootstrapManager { public class BootstrapManager {
private final ExecutorService _asyncExecutor = Executors.newFixedThreadPool(5);
private final List<BootstrapStep> _bootSteps; private final List<BootstrapStep> _bootSteps;
public BootstrapManager(final List<BootstrapStep> bootSteps) { public BootstrapManager(final List<BootstrapStep> bootSteps) {
@ -42,7 +45,7 @@ public class BootstrapManager {
} catch (Exception e) { } catch (Exception e) {
log.error(String.format("Caught exception while executing bootstrap step %s. Continuing...", step.name()), e); log.error(String.format("Caught exception while executing bootstrap step %s. Continuing...", step.name()), e);
} }
}); }, _asyncExecutor);
} }
} }
} }

View File

@ -15,6 +15,7 @@ import com.linkedin.mxe.GenericAspect;
import com.linkedin.mxe.MetadataChangeProposal; import com.linkedin.mxe.MetadataChangeProposal;
import com.linkedin.policy.DataHubRoleInfo; import com.linkedin.policy.DataHubRoleInfo;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import javax.annotation.Nonnull;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
@ -25,6 +26,7 @@ import static com.linkedin.metadata.Constants.*;
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class IngestRolesStep implements BootstrapStep { public class IngestRolesStep implements BootstrapStep {
private static final int SLEEP_SECONDS = 60;
private final EntityService _entityService; private final EntityService _entityService;
@Override @Override
@ -32,10 +34,19 @@ public class IngestRolesStep implements BootstrapStep {
return this.getClass().getSimpleName(); return this.getClass().getSimpleName();
} }
@Nonnull
@Override
public ExecutionMode getExecutionMode() {
return ExecutionMode.ASYNC;
}
@Override @Override
public void execute() throws Exception { public void execute() throws Exception {
final ObjectMapper mapper = new ObjectMapper(); final ObjectMapper mapper = new ObjectMapper();
// Sleep to ensure deployment process finishes.
Thread.sleep(SLEEP_SECONDS * 1000);
// 0. Execute preflight check to see whether we need to ingest Roles // 0. Execute preflight check to see whether we need to ingest Roles
log.info("Ingesting default Roles..."); log.info("Ingesting default Roles...");

View File

@ -437,8 +437,10 @@ public class JavaEntityClient implements EntityClient {
// TODO: Factor out ingest logic into a util that can be accessed by the java client and the resource // TODO: Factor out ingest logic into a util that can be accessed by the java client and the resource
@SneakyThrows @SneakyThrows
@Override @Override
public String ingestProposal(@Nonnull MetadataChangeProposal metadataChangeProposal, public String ingestProposal(
@Nonnull final MetadataChangeProposal metadataChangeProposal,
@Nonnull final Authentication authentication) throws RemoteInvocationException { @Nonnull final Authentication authentication) throws RemoteInvocationException {
String actorUrnStr = authentication.getActor() != null ? authentication.getActor().toUrnStr() : Constants.UNKNOWN_ACTOR; String actorUrnStr = authentication.getActor() != null ? authentication.getActor().toUrnStr() : Constants.UNKNOWN_ACTOR;
final AuditStamp auditStamp = final AuditStamp auditStamp =
new AuditStamp().setTime(_clock.millis()).setActor(Urn.createFromString(actorUrnStr)); new AuditStamp().setTime(_clock.millis()).setActor(Urn.createFromString(actorUrnStr));

View File

@ -239,9 +239,6 @@
] ]
}, },
"privileges":[ "privileges":[
"MANAGE_INGESTION",
"MANAGE_SECRETS",
"VIEW_ANALYTICS",
"GENERATE_PERSONAL_ACCESS_TOKENS", "GENERATE_PERSONAL_ACCESS_TOKENS",
"MANAGE_DOMAINS", "MANAGE_DOMAINS",
"MANAGE_GLOSSARIES", "MANAGE_GLOSSARIES",
@ -314,9 +311,7 @@
"urn:li:dataHubRole:Reader" "urn:li:dataHubRole:Reader"
] ]
}, },
"privileges":[ "privileges":[],
"VIEW_ANALYTICS"
],
"displayName":"Readers - Platform Policy", "displayName":"Readers - Platform Policy",
"description":"Readers can view analytics.", "description":"Readers can view analytics.",
"state":"ACTIVE", "state":"ACTIVE",