项目作者: HumanCellAtlas

项目描述 :
Design specs and prototypes for the HCA Data Storage System (DSS, "blue box")
高级语言: Python
项目地址: git://github.com/HumanCellAtlas/data-store.git
创建时间: 2017-03-13T20:38:34Z
项目社区:https://github.com/HumanCellAtlas/data-store

开源协议:Other

下载


HCA DSS: The Human Cell Atlas Data Storage System



Build Status
codecov

This repository maintains the data storage system of the
Human Cell Atlas. We use this
Google Drive folder for design docs and
meeting notes, and this Zenhub board to track our GitHub work.

Overview


The DSS is a replicated data storage system designed for hosting large sets of scientific experimental data on
Amazon S3 and Google Storage. The DSS exposes an API
for interacting with the data and is built using Chalice,
API Gateway and AWS Lambda. The API also
implements Step Functions to orchestrate Lambdas for long-running tasks such
as large file writes. You can find the API documentation and give it a try here.

Architectural Diagram

DSS Sync SFN diagram

DSS API

The DSS API uses Swagger to define the API specification according to the
OpenAPI 2.0 specification.
Connexion is used to map the API specification to its implementation in Python.

You can use the
Swagger Editor
to review and edit the API specification. When the API is live, the spec is also available at /v1/swagger.json.

You can find the DSS API documentation for each stage listed below:

Table of Contents

Getting Started

In this section, you’ll configure and deploy a local API server and your own suite of cloud services to run a
development version of the DSS.

Note that privileged access to cloud accounts (AWS, GCP, etc.) is required to deploy the data-store. IF your deployment fails
due to access restrictions, please consult your local systems administrators.

Also note that all commands given in this Readme should be run from the root of this repository after sourcing the
correct environment (see the Configuration section below). The root directory of the repository
is also available in the environment variable $DSS_HOME.

Install Dependencies

The DSS requires Python 3.6 to run.

Clone the repo and install dependencies:

  1. git clone git@github.com:HumanCellAtlas/data-store.git
  2. cd data-store
  3. pip install -r requirements-dev.txt

Also install terraform from Hashicorp from your favourite package manager.

Configuration

The DSS is configured via environment variables. The required environment variables and their default values
are defined in the file environment. To customize the values of these environment variables:

  1. Copy environment.local.example to environment.local
  2. Edit environment.local to add custom entries that override the default values in environment
  3. Run source environment now and whenever these environment files are modified.

The full list of configurable environment variables and their descriptions are documented here.

Configure Terraform

The DSS uses the Amazon S3 backend for Terraform-deployed resources. To run deployment, make sure that the requisite bucket exists. The bucket is named after $DSS_TERRAFORM_BACKEND_BUCKET_TEMPLATE, by default org-humancellatlas-{account_id}-terraform with {account_id} substituted with your AWS account ID.

Configure AWS

  1. Follow this tutorial to install the
    AWS command line utility and configure your AWS access credentials.

  2. Specify the names of S3 buckets in environment.local, using the environment variables DSS_S3_BUCKET_*, and verify
    that AWS_DEFAULT_REGION points to your prefered region.
    These buckets will be created with Terraform, and should not exist before deploying for the first time.

Configure GCP

  1. Generate OAuth application secrets to be used for your instance:

    1) Go to the GCP API and Service Credentials page. You may have to select Organization and Project again.

    1) Click Create Credentials and select OAuth client

    1) For Application type choose Other

    1) Under application name, use hca-dss- followed by the stage name (i.e. the value of DSS_DEPLOYMENT_STAGE). This
    is a convention only and carries no technical significance.

    1) Click Create, don’t worry about noting the client ID and secret, click OK

    1) Click the edit icon for the new credentials and click Download JSON

    1) Place the downloaded JSON file into the project root as application_secrets.json

    1) Run the command

    1. ### WARNING: RUNNING THIS COMMAND WILL
    2. ### CLEAR EXISTING SCRET VALUE
    3. cat $DSS_HOME/application_secrets.json | ./scripts/dss-ops.py secrets set --secret-name $GOOGLE_APPLICATION_SECRETS_SECRETS_NAME
  2. Download the gcloud command line utility.

  3. Run the command

    1. ./scripts/dss-ops.py lambda update

    This populates the environment variables defining your stage into an AWS Simple Systems Manager parameter store.
    These variables will be read from the parameter store and in-lined into the Lambda deployment packages during
    deployment. This command should be executed whenever the environment variables are updated. The environments
    of currently deployed Lambdas may optionally by updated in place with the flag --update-deployed.

  4. Choose a region that has support for Cloud Functions and set GCP_DEFAULT_REGION to that region. See
    the GCP locations list for a list of supported regions.

  5. Run gcloud config set project PROJECT_ID where PROJECT_ID is the ID, not the name (i.e: hca-store-21555, NOT just hca-store) of the GCP project you selected earlier.

  6. Enable required APIs:

    1. gcloud services enable cloudfunctions.googleapis.com
    2. gcloud services enable runtimeconfig.googleapis.com
    3. gcloud services enable iam.googleapis.com
  7. Specify the names of Google Cloud Storage buckets in environment.local, using the environment variables DSS_GS_BUCKET_*,
    and verify that GCP_DEFAULT_REGION points to your prefered region.
    These buckets will be created with Terraform, and should not exist before deploying for the first time.

Configure User Authentication/Authorization

The following environment variables must be set to enable user authentication and authorization.

  • OIDC_AUDIENCE must be populated with the expected JWT (JSON web token) audience.
  • OPENID_PROVIDER is the generator of the JWT, and is used to determine how the JWT is validated.
  • OIDC_GROUP_CLAIM is the JWT claim that specifies the group the users belongs to.
  • OIDC_EMAIL_CLAIM is the JWT claim that specifies the requests email.

Also update authorizationUrl in dss/dss-api.yml to point to an authorization endpoint which returns a
valid JWT.

Optional: To configure a custom swagger auth before deployment run:

  1. python scripts/swagger_auth.py -c='{"/path": "call"}'

Alternatively, to configure auth for all swagger endpoints, you can run:

  1. python scripts/swagger_auth.py --secure

Note: Removing auth from endpoints will currently break tests, however adding auth should be fine
(make test should run successfully).

Note: The auth config file for deployment can also be set in environment.local with AUTH_CONFIG_FILE.

Configure email notifications

Some daemons (dss-checkout-sfn for example) use Amazon SES to send emails. You must set DSS_NOTIFICATION_SENDER to
your email address and then verify that address using the SES Console enabling SES to send notification emails from it.

Deployment


Running the DSS API locally

Run ./dss-api in the top-level data-store directory to deploy the DSS API on your localhost.

Acquiring GCP credentials

When deploying for the first time, a Google Cloud Platform service account must first be created and credentialed.

  1. Specify the name of the Google Cloud Platform service account in environment.local using the variable DSS_GCP_SERVICE_ACCOUNT_NAME.

  2. Provision a set of credentials that will allow you to run deployment.

    1) In the Google Cloud Console, select the correct Google user account on the top
    right and the correct GCP project in the drop down in the top center. Go to “IAM & Admin”, then “Service accounts”.

    1) Click “Create service account” and select “Furnish a new private key”. Under “Roles”, select
    a) “Project – Owner”,
    a) “Service Accounts – Service Account User”
    a) “Cloud Functions – Cloud Function Developer”.

    1) Create the account and download the service account key JSON file.

    1) Place the file as $DSS_HOME/gcp-credentials.json. You will replace it later.

  3. Create the Google Cloud Platform service account using the command

    1. make -C infra COMPONENT=gcp_service_account apply

    This step can be skipped if you’re rotating credentials.

  4. Place the downloaded JSON file into the project root as gcp-credentials.json

  5. Run the command

    1. ### WARNING: RUNNING THIS COMMAND WILL
    2. ### CLEAR EXISTING SECRET VALUE
    3. cat $DSS_HOME/gcp-credentials.json | ./scripts/dss-ops.py secrets set --secret-name $GOOGLE_APPLICATION_CREDENTIALS_SECRETS_NAME

Setting admin emails

Set admin account emails within AWS Secret Manager:

  1. ### WARNING: RUNNING THIS COMMAND WILL
  2. ### CLEAR EXISTING SECRET VALUE
  3. echo -n 'user1@example.com,user2@example.com' | ./scripts/dss-ops.py secrets set --secret-name $ADMIN_USER_EMAILS_SECRETS_NAME

Deploying the DSS

Assuming the tests have passed above, the next step is to manually deploy. See the section below for information on
CI/CD with Travis if continuous deployment is your goal.

Several components in the DSS deployed separately as daemons, found in $DSS_HOME/daemons. Daemon deployment may
incorporate dependent infrastructure, such SQS queues or SNS topics, by placing Terraform files in daemon directory, e.g.
$DSS_HOME/daemons/dss-admin/my_queue_defs.tf. This infrastructure is deployed non-interactively, without the
usual plan/review Terraform workflow, and should therefore be lightweight in nature. Large infrastructure should be
added to $DSS_HOME/infra instead.

Resources

Cloud resources have the potential for naming collision in both AWS
and GCP, ensure that you rename resources as needed.

Buckets

Buckets within AWS and GCP need to be available for use by the DSS. Use Terraform to setup these resources:

  1. make -C infra COMPONENT=buckets plan
  2. make -C infra COMPONENT=buckets apply

ElasticSearch

The AWS Elasticsearch Service is used for metadata indexing. Currently the DSS uses version 5.5 of ElasticSearch. For typical development deployments the
t2.small.elasticsearch instance type is sufficient. Use the DSS_ES_ variables to adjust the cluster as needed.

Add allowed IPs for ElasticSearch to the secret manager, use comma separated IPs:

  1. ### WARNING: RUNNING THIS COMMAND WILL
  2. ### CLEAR EXISTING SECRET VALUE
  3. echo -n '1.1.1.1,2.2.2.2' | ./scripts/dss-ops.py secret set --secret-name $ES_ALLOWED_SOURCE_IP_SECRETS_NAME

Use Terraform to deploy ES resource:

  1. make -C infra COMPONENT=elasticsearch plan
  2. make -C infra COMPONENT=elasticsearch apply

Certificates

A certificate matching your domain must be registered with
AWS Certificate Manager. Set ACM_CERTIFICATE_IDENTIFIER
to the identifier of the certificate, which can be found on the AWS console.

An AWS route53 zone must be available for your domain name and configured in environment.

Deploying

Now deploy using make:

  1. make plan-infra
  2. make deploy-infra
  3. make deploy

If successful, you should be able to see the Swagger API documentation at:

  1. https://<domain_name>

And you should be able to list bundles like this:

  1. curl -X GET "https://<domain_name>/v1/bundles" -H "accept: application/json"

Monitoring

Please see the data-store-monitor repo for additional
monitoring tools.

CI/CD with Travis CI and GitLab

We use Travis CI for continuous unit testing that does
not involve deployed components. A private GitLab instance is used for deployment to
the dev environment if unit tests pass, as well as further testing of deployed components, for every commit
on the master branch. GitLab testing results are announced on the
data-store-eng Slack channel in the HumanCellAtlas workspace.
Travis behaviour is defined in .travis.yml, and GitLab behaviour is defined in .gitlab-ci.yml.

Authorizing Travis CI to deploy

Encrypted environment variables give Travis CI the AWS credentials needed to run the tests and deploy the app. Run
scripts/authorize_aws_deploy.sh IAM-PRINCIPAL-TYPE IAM-PRINCIPAL-NAME (e.g. authorize_aws_deploy.sh group travis-ci) to give that principal the permissions needed to deploy the app. Because a group policy has a higher size
limit (5,120 characters) than a user policy (2,048 characters), it is advisable to apply this to a group and add the
principal to that group. Because this is a limited set of permissions, it does not have write access to IAM. To set up
the IAM policies for resources in your account that the app will use, run make deploy using privileged account
credentials once from your workstation. After this is done, Travis CI will be able to deploy on its own. You must
repeat the make deploy step from a privileged account any time you change the IAM policies templates in
iam/policy-templates/.

Authorizing the event relay

Environment variables provide the AWS credentials needed to relay events originating from supported cloud platforms
outside of AWS. Run scripts/create_config_aws_event_relay_user.py to create an AWS IAM user with the appropriate
restricted access policy. This script also creates the user access key and stores it in an AWS Secrets Manager
store.

Note when executing the script above, ensure that the role/user used within AWS is authorized to perform: iam:CreateUser

Using the HCA Data Store CLI Client

Now that you have deployed the data store, the next step is to use the HCA Data Store CLI to upload and download data to
the system. See data-store-cli for installation instructions. The
client requires you change hca/api_spec.json to point to the correct host, schemes, and, possibly, basePath. Examples
of CLI use:

  1. # list bundles
  2. hca dss post-search --es-query "{}" --replica=aws | less
  3. # upload full bundle
  4. hca dss upload --replica aws --staging-bucket staging_bucket_name --src-dir ${DSS_HOME}/tests/fixtures/datafiles/example_bundle

Checking Indexing

Now that you’ve uploaded data, the next step is to confirm the indexing is working properly and you can query the
indexed metadata.

  1. hca dss post-search --replica aws --es-query '
  2. {
  3. "query": {
  4. "bool": {
  5. "must": [{
  6. "match": {
  7. "files.donor_organism_json.medical_history.smoking_history": "yes"
  8. }
  9. }, {
  10. "match": {
  11. "files.specimen_from_organism_json.genus_species.text": "Homo sapiens"
  12. }
  13. }, {
  14. "match": {
  15. "files.specimen_from_organism_json.organ.text": "brain"
  16. }
  17. }]
  18. }
  19. }
  20. }
  21. '

Running Tests

  1. Check that software packages required to test and deploy are available, and install them if necessary:

    make --dry-run

  2. Populate text fixture buckets with test fixture data (This command will completely empty the given buckets before populating them with test fixture data, please ensure
    the correct bucket names are provided)**
    :

    1. tests/fixtures/populate.py --s3-bucket $DSS_S3_BUCKET_TEST_FIXTURES --gs-bucket $DSS_GS_BUCKET_TEST_FIXTURES
  3. Set the environment variable DSS_TEST_ES_PATH to the path of the elasticsearch binary on your machine.

  4. Run tests with make test

Test suites

All tests for the DSS fall into one of two categories:

  • Standalone tests, which do not depend on deployed components, and
  • Integration tests, which depend on deployed components.

As such, standalone tests can be expected to pass even if no deployment is configured,
and in fact should pass before an initial deployment. For more information on tests,
see tests/README.md.

Development

Managing dependencies

The direct runtime dependencies of this project are defined in requirements.txt.in. Direct development dependencies
are defined in requirements-dev.txt.in. All dependencies, direct and transitive, are defined in the corresponding
requirements.txt and requirements-dev.txt files. The latter two can be generated using make requirements.txt or
make requirements-dev.txt respectively. Modifications to any of these four files need to be committed. This process is
aimed at making dependency handling more deterministic without accumulating the upgrade debt that would be incurred by
simply pinning all direct and transitive dependencies. Avoid being overly restrictive when constraining the allowed
version range of direct dependencies in -requirements.txt.in and requirements-dev.txt.in

If you need to modify or add a direct runtime dependency declaration, follow the steps below:

1) Make sure there are no pending changes to requirements.txt or requirements-dev.txt.
1) Make the desired change to requirements.txt.in or requirements-dev.txt.in
1) Run make requirements.txt. Run make requirements-dev.txt if you have modified requirements-dev.txt.in.
1) Visually check the changes to requirements.txt and requirements-dev.txt.
1) Commit them with a message like Bumping dependencies.

You now have two commits, one that catches up with updates to transitive dependencies, and one that tracks your explict
change to a direct dependency. This process applies to development dependencies as well, except for
requirements-dev.txt and requirements-dev.txt.in respectively.

If you wish to re-pin all the dependencies, run make refresh_all_requirements. It is advisable to do a full
test-deploy-test cycle after this (the test after the deploy is required to test the lambdas).

Logging conventions

  1. Always use a module-level logger, call it logger and initialize it as follows:

    1. import logging
    2. logger = logging.getLogger(__name__)
  2. Do not configure logging at module scope. It should be possible to import any module without side-effects on
    logging. The dss.logging module contains functions that configure logging for this application, its Lambda
    functions and unit tests.

  3. When logging a message, pass either

    • an f-string as the first and only positional argument or

    • a %-string as the first argument and substitution values as subsequent arguments. Do not mix the two string
      interpolation methods. If you mix them, any percent sign in a substituted value will raise an exception.

    1. # In other words, use
    2. logger.info(f"Foo is {foo} and bar is {bar}")
    3. # or
    4. logger.info("Foo is %s and bar is %s", foo, bar)
    5. # but not
    6. logger.info(f"Foo is {foo} and bar is %s", bar)
    7. # Keyword arguments can be used safely in conjunction with f-strings:
    8. logger.info(f"Foo is {foo}", exc_info=True)
  4. To enable verbose logging by application code, set the environment variable DSS_DEBUG to 1. To enable verbose
    logging by dependencies set DSS_DEBUG to 2. To disable verbose logging unset DSS_DEBUG or set it to 0.

  5. To assert in tests that certain messages were logged, use the dss logger or one of its children

    1. dss_logger = logging.getLogger('dss')
    2. with self.assertLogs(dss_logger) as log_monitor:
    3. # do stuff
    4. # or
    5. import dss
    6. with self.assertLogs(dss.logger) as log_monitor:
    7. # do stuff

Enabling Profiling

AWS Xray tracing is used for profiling the performance of deployed lambdas. This can be enabled for chalice/app.py by
setting the lambda environment variable DSS_XRAY_TRACE=1. For all other daemons you must also check
“Enable active tracking” under “Debugging and error handling” in the AWS Lambda console.

Security Policy

See our Security Policy.

Contributing

External contributions are welcome. Please review the Contributing Guidelines