Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Ability to save/load secrets through google secrets manager. #5791

Closed
wants to merge 24 commits into from

Conversation

airbyte-jenny
Copy link
Contributor

@airbyte-jenny airbyte-jenny commented Sep 1, 2021

What

Moves connector config json with secrets in it out of the main database and into a dedicated secrets manager. Provides a simple wrapper on google secrets manager SDK calls to simplify those calls.

How

Modifies ConfigRepository to use two persistence stores, and separate configs by type to save in each, recombining them on retrieval. Only works if the feature flag is enabled and GCP credentials are provided in the environment. This puts the entire config for source connection or destination connection into the secrets store; it does not break out individual secrets yet.

Recommended reading order

  1. ConfigRepository.java
  2. GoogleSecretsManager.java
  3. GoogleSecretsManagerConfigPersistence.java
  4. Everything else

@github-actions github-actions bot added the area/platform issues related to the platform label Sep 9, 2021
@airbyte-jenny airbyte-jenny changed the title WIP: Ability to save/load secrets through google secrets manager. Feature: Ability to save/load secrets through google secrets manager. Sep 15, 2021
@airbyte-jenny airbyte-jenny temporarily deployed to more-secrets September 17, 2021 16:13 Inactive
@airbyte-jenny airbyte-jenny temporarily deployed to more-secrets September 17, 2021 16:19 Inactive
@airbyte-jenny airbyte-jenny temporarily deployed to more-secrets September 17, 2021 16:33 Inactive
@airbyte-jenny airbyte-jenny temporarily deployed to more-secrets September 20, 2021 20:10 Inactive
@airbyte-jenny airbyte-jenny temporarily deployed to more-secrets September 20, 2021 20:17 Inactive
@airbyte-jenny airbyte-jenny temporarily deployed to more-secrets September 20, 2021 20:27 Inactive
@airbyte-jenny airbyte-jenny temporarily deployed to more-secrets September 20, 2021 21:24 Inactive
@airbyte-jenny airbyte-jenny temporarily deployed to more-secrets September 21, 2021 15:42 Inactive
@airbyte-jenny airbyte-jenny temporarily deployed to more-secrets September 21, 2021 16:06 Inactive
@airbyte-jenny airbyte-jenny temporarily deployed to more-secrets September 21, 2021 16:29 Inactive
@airbyte-jenny airbyte-jenny temporarily deployed to more-secrets September 21, 2021 18:07 Inactive
@airbyte-jenny airbyte-jenny temporarily deployed to more-secrets September 21, 2021 18:25 Inactive
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Stream;

public class ConfigRepository {

private final ConfigPersistence persistence;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick. Maybe rename this variable to something like routinePersistence or nonSecrectsPersistence to distinguish it from the newly added one?

Map<String, Stream<JsonNode>> bothConfigTypes = persistence.dumpConfigs();
// Secrets and nonsecrets might be stored separately, and if they are, go looking for the rest.
// No need to do that if they're in the same single store, because we don't need duplicates or
// double work.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick. This comment contains some pronouns that take a few seconds to understand: the rest, do that. Also the structure in replaceAllConfigs looks more clear than this one.

What about following the same structure as in replaceAllConfigs?

// If secrets and nonsecrets are stored in the same persistence,
// just return configs from one persistence becase we don't need duplicates or double work.
if (secretsPersistence == persistence) {
  return persistence.dumpConfigs();
}

// If secrets and nonsecrets are stored separately, return configs from both.
return MoreMaps.merge(persistence.dumpConfigs(), secretsPersistence.dumpConfigs());

* During migration, this runs through the list of connectors, determining their docker image and
* version, for purposes of skipping upgrading in-use definitions.
*
* @return
Copy link
Contributor

@tuliren tuliren Sep 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick. This @return doc is empty and can be removed.

}

/**
* During migration, this runs through the list of connectors, determining their docker image and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Migration is a loaded term. It's more like "connector definition update" instead of "migration".

* public void loadData(ConfigPersistence seedConfigPersistence) throws IOException {
* database.transaction(ctx -> { updateConfigsFromSeed(ctx, seedConfigPersistence); return null; });
* }
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be deleted.

for (DestinationConnection dest : destinations) {
definitionIds.add(dest.getDestinationDefinitionId().toString());
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are extra code blocks here. They can be removed.

}
}
return definitionIds;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here that this method does not need to be implemented in this implementation.

updateConfigsFromSeed(ctx, seedConfigPersistence, connectorRepositoriesInUse);
} else {
copyConfigsFromSeed(ctx, seedConfigPersistence);
}
Copy link
Contributor

@tuliren tuliren Sep 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The server start logic has been changed. The loadData method only updates the connector definitions now. It will only be called after the database has been initialized.

This is necessary to fix a bug during server launch. The rationale is that we cannot insert the seed or update the connector definitions as part of the initialization process. When the server launches, it can be on an old version, and the seed or the latest definitions may not be compatible with the old data structure. The server will run the migrations first, and then we call loadData to update the connectors.

So adding back the copyConfigsFromSeed is no not necessary here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry that this change was added recently. Let me know if you want to discuss this in person for clarification.

Copy link
Contributor

@tuliren tuliren Sep 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More context. The only thing we do to "initialize" the persistence is copying old configs from the file system if the user is running an old Airbyte version:
https://github.com/airbytehq/airbyte/blob/master/airbyte-server/src/main/java/io/airbyte/server/ServerApp.java#L187

We call loadData after file-based and database migrations here:
https://github.com/airbytehq/airbyte/blob/master/airbyte-server/src/main/java/io/airbyte/server/ServerApp.java#L248

assertRecordCount(2);
assertHasSource(SOURCE_GITHUB);
assertHasDestination(DESTINATION_SNOWFLAKE);
verify(configPersistence, times(1)).copyConfigsFromSeed(any(DSLContext.class), any(ConfigPersistence.class));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned above, the copyConfigsFromSeed won't be triggered in loadData method. So I feel that adding this test case is unnecessary.

configPersistence.loadData(seedPersistence, new HashSet<String>());
secretsPersistence = new FileSystemConfigPersistence(p);
configRepository = new ConfigRepository(configPersistence, secretsPersistence);
// configRepository = new ConfigRepository(configPersistence, configPersistence);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line can be removed.

@CLAassistant
Copy link

CLAassistant commented Sep 27, 2021

CLA assistant check
All committers have signed the CLA.

@airbyte-jenny
Copy link
Contributor Author

We moved parts from this PR to other implementations, and do not need this particular code anymore. Closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/platform issues related to the platform
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants