One of the most frequent WSM tasks is to add a new resource. I have tried to make this as simple as practical. I have also tried to allow parallel resource development with a minimum of file conflicts.
This section summarizes the steps to add a new resource. I recommend this order to be able to compile your code as you go.
- Create your resource type and attributes in the
openapi
subproject.- Add your resource file to
src/resources/
directory, defining the resource and the attributes objects. - Edit the
resource_type.yaml
file in that directory, creating an enumeration for the resource and adding the appropriate resource element to the union objects. - Run
./gradlew :openapi:buildOpenApi
to make sure your syntax and references are correct.
- Add your resource file to
- Create your Java resource package in
service/src/main/java/bio.terra/workspace/service/resources/
in the appropriate packagecontrolled/{cloud}/{your resource}
referenced/{cloud}/{your resource}
- Create classes for your resource
- Attributes class describing the fields beyond the common metadata
- Resource class. This is the most complex class to implement and supplies the bulk of your resource code
- Handler class
- If you are making a controlled resource you will also need to add:
- Step classes as required for object create and update. These will be needed to complete the resource class.
- Handler class. Used primarily to give the
ResourceDao
a way to create resource objects by type.
- Add your new resource type to
resource/model/WsmResourceFamily
,resource/model/WsmResourceType
andApiResourceType
. At this point,./gradlew :service:compileJava
should succeed. - Add your REST API
-
In the
openapi
subproject, in theparts
directory, create a yaml file for your resource API. The file should contain all resource-type-specific parameters, responses, and schemas as well as the endpoint paths. You can find shared components in thesrc/common
directory.NOTE: At this point in time, there is too much variation in the different resource APIs. Please follow the pattern in the
??_gcp_big_query_dataset
interface.
-
- Edit the appropriate controller to implement your API. Depending on the implementation
details, you may need to add methods to the
ControlledResourceService
or theReferencedResourceService
. - Tests
- Unit tests: When you don't need to talk to real cloud. Example: Search for
ControlledBqDataset
in
(ControlledGcpResourceApiControllerTest.java
) - Connected tests: If you need to talk to real cloud, for example to create your
resource. The majority of tests should be connected tests. Each resource is in a
different test file. Example:
(
ControlledGcpResourceApiControllerBqDatasetTest.java
)- Controller tests are preferred over service tests, since former also tests controller layer.
- Integration test: Tests using TestRunner framework. If your resource supports cloning, add your resource to
CloneWorkspace
integration test.
- Unit tests: When you don't need to talk to real cloud. Example: Search for
We have a strong bias for synchronous APIs. If you have an operation that runs over the network time more that 10% of the time, it is appropriate to make it an asynchronous interface. See MC Terra - Async REST API Specification for details about how async interfaces are provided in MC Terra.
When writing steps for a Stairway flight, you need to make your code idempotent. It needs to be able to be restarted in the event of pod failures and rolling upgrades. See Stairway Github Repo for further documentation. In particular, the Flight Developer Guide.
This section itemizes the files you will probably need to create or edit to add your resource.
- {stewardship} is either
controlled
orreferenced
as defined in StewardshipType.java - {cloud} is either
gcp
,azure
, orany
as defined in CloudPlatform.java - {resource} is the name of your resource; for example,
BigQueryDataset
Most of the code you make is in files specific to the new resource. These are:
Project | Directory Path | File | Notes |
---|---|---|---|
integration | src/main/java/scripts/testscripts | {resource}Lifecycle.java |
|
integration | src/main/resources/configs/integration | {resource}Lifecycle.json |
|
openapi | src/parts | {stewardship}_{cloud}_{resource}.yaml |
|
openapi | src/resources | {cloud}_{resource}.yaml |
|
service | src/main/java/.../service/resource/{stewardship}/cloud/{cloud}/{resource} | {resource}Attributes.java |
|
service | src/main/java/.../service/resource/{stewardship}/cloud/{cloud}/{resource} | {resource}Handler.java |
controlled only |
service | src/main/java/.../service/resource/{stewardship}/cloud/{cloud}/{resource} | {resource}Resource.java |
|
service | src/main/java/.../service/resource/{stewardship}/cloud/{cloud}/{resource} | *Steps.java |
Create and update step files; controlled only |
service | src/test/java/.../service/resource/{stewardship}/cloud/{cloud}/{resource} | *Test.java |
Unit and connected tests |
Project | Directory Path | File | Notes |
---|---|---|---|
integration | src/main/resources/suites | FullIntegration.json | If your test should run as part of the automated integration tests |
openapi | src/resources | resource_type.yaml | |
service | src/main/java/.../app/controller | {stewardship}{cloud}Controller.java |
|
service | src/main/java/.../service/resource/{stewardship} | {stewardship}ResourceService.java |
If the common paths are not sufficient |
service | src/main/java/.../service/resource/model | WsmResource.java |
enum of all specific resources |
service | src/main/java/.../service/resource/model | WsmResourceFamily.java |
enum of kinds of resources |
All cloud resource allocation in WSM should be done using CRL. CRL provides a common point of logging and, in the fullness of time, perhaps tagging resources. It is out of scope for this document to describe adding things to CRL. See Common Resource Library Github for information.
Adding resource allocation to CRL sometimes requires adding resource cleanup to the Janitor service. We use the Janitor service in development to clean up test resources. That way, tests can crash and burn and we are still able to clean up. See Janitor Github for more information.
When adding a controlled resource in GCP, there are often other tasks that need to be done.
Typically, the built-in GCP roles for objects do not work well for WSM. WSM needs to be in sole control of create, delete, and IAM. Often, roles that allow attribute update also allow those actions. The solution is to create custom roles.
The code structure for adding these roles is in resource/controlled/cloud/gcp
in the
files CustomGcpIamRole
and CustomGcpIamRoleMapping
.
Enabling resources can take a while so we do it in the Resource Buffer Service (RBS). RBS manages pools of pre-configured projects. When a GCP cloud context is created, we request a project from a particular pool in RBS.
If you need to enable a new API to support your new GCP resource, it needs to be done in RBS. See Resource Buffer Service Github for more details.
When deleting an Azure resource, the deletion step should subclass DeleteAzureControlledResourceStep.java
,
which contains handling for common errors that happen when deleting Azure resources.
TBS