This patch adds a `holos register user` command. Given an authenticated
id token and no other record of the user in the database, the cli tool
use the API to:
1. User is registered in `holos server`
2. User is linked to one Holos Organization.
3. Holos Organization has the `bare` platform.
4. Holos Organization has the `reference` platform.
5. Ensure `~/.holos/client-context.json` contains the user id and an
org id.
The `holos.ClientContext` struct is intended as a light weight way to
save and load the current organization id to the file system for further
API calls.
The assumption is most users will have only one single org. We can add
a more complicated config context system like kubectl uses if and when
we need it.
This patch adds the GetVersion rpc method to
holos.system.v1alpha1.SystemService and wires the version information up
to the Web UI.
This is a good example to crib from later regarding fetching and
refreshing data from the web ui using grpc and field masks.
This patch refactors the API following the [API Best Practices][api]
documentation. The UpdatePlatform method is modeled after a mutating
operation described [by Netflix][nflx] instead of using a REST resource
representation. This makes it much easier to iterate over the fields
that need to be updated as the PlatformUpdateOperation is a flat data
structure while a Platform resource may have nested fields. Nested
fields are more complicated and less clear to handle with a FieldMask.
This patch also adds a snapckbar message on save. Previously, the save
button didn't give any indication of success or failure. This patch
fixes the problem by adding a snackbar message that pop up at the bottom
of the screen nicely.
When the snackbar message is dismissed or times out the save button is
re-enabled.
[api]: https://protobuf.dev/programming-guides/api/
[nflx]: https://netflixtechblog.com/practical-api-design-at-netflix-part-2-protobuf-fieldmask-for-mutation-operations-2e75e1d230e4
Examples:
FieldMask for ListPlatforms
```
grpcurl -H "x-oidc-id-token: $(holos token)" -d @ ${HOLOS_SERVER##*/} holos.platform.v1alpha1.PlatformService.ListPlatforms <<EOF
{
"org_id": "018f36fb-e3f7-7f7f-a1c5-c85fb735d215",
"field_mask": { "paths": ["id","name"] }
}
EOF
```
```json
{
"platforms": [
{
"id": "018f36fb-e3ff-7f7f-a5d1-7ca2bf499e94",
"name": "bare"
},
{
"id": "018f6b06-9e57-7223-91a9-784e145d998c",
"name": "gary"
},
{
"id": "018f6b06-9e53-7223-8ae1-1ad53d46b158",
"name": "jeff"
},
{
"id": "018f6b06-9e5b-7223-8b8b-ea62618e8200",
"name": "nate"
}
]
}
```
Closes: #171
This patch refactors the API to be resource-oriented around one service
per resource type. PlatformService, OrganizationService, UserService,
etc...
Validation is improved to use CEL rules provided by [protovalidate][1].
Place holders for FieldMask and other best practices are added, but are
unimplemented as per [API Best Practices][2].
The intent is to set us up well for copying and pasting solid existing
examples as we add features.
With this patch the server and web app client are both updated to use
the refactored API, however the following are not working:
1. Update the model.
2. Field Masks.
[1]: https://buf.build/bufbuild/protovalidate
[2]: https://protobuf.dev/programming-guides/api/
The way we were organizing fields into section broke Formly validation.
This patch fixes the problem by using the recommended approach of
[Nested Forms][1].
This patch also refactors the PlatformService API to clean it up.
GetForm / PutForm are separated from the Platform methods. Similarly
GetModel / PutModel are separated out and are specific to get and put
the model data.
NOTE: I'm not sure we should have separated out the platform service
into it's own protobuf package. Seems maybe unnecessary.
❯ grpcurl -H "x-oidc-id-token: $(holos token)" -d '{"platform_id":"018f36fb-e3ff-7f7f-a5d1-7ca2bf499e94"}' jeff.app.dev.k2.holos.run:443 holos.platform.v1alpha1.PlatformService.GetModel
{
"model": {
"org": {
"contactEmail": "platform@openinfrastructure.co",
"displayName": "Open Infrastructure Services LLC",
"domain": "ois.run",
"name": "ois"
},
"privacy": {
"country": "earth",
"regions": [
"us-east-2",
"us-west-2"
]
},
"terms": {
"didAgree": true
}
}
}
[1]: https://formly.dev/docs/examples/other/nested-formly-forms
Problem:
The GetConfig response value isn't directly usable with CUE without some
gymnastics.
Solution:
Refactor the protobuf definition and response output to make the user
defined and supplied config values provided by the API directly usable
in the CUE code that defines the platform.
Result:
The top level platform config is directly usable in the
`internal/platforms/bare` directory:
grpcurl -H "x-oidc-id-token: $(holos token)" -d '{"platform_id":"'${platformID}'"}' $host \
holos.v1alpha1.PlatformService.GetConfig \
> platform.holos.json
Vet the user supplied data:
cue vet ./ -d '#PlatformConfig' platform.holos.json
Build the holos component. The ConfigMap consumes the user supplied
data:
cue export --out yaml -t cluster=k2 ./components/configmap platform.holos.json \
| yq .spec.components
Note the data provided by the input form is embedded into the
ConfigMap managed by Holos:
```yaml
KubernetesObjectsList:
- metadata:
name: platform-configmap
apiObjectMap:
ConfigMap:
platform: |
metadata:
name: platform
namespace: default
labels:
app.holos.run/managed: "true"
data:
platform: |
kind: Platform
spec:
config:
user:
sections:
org:
fields:
contactEmail: jeff@openinfrastructure.co
displayName: Open Infrastructure Services LLC
domain: ois.run
name: ois
apiVersion: app.holos.run/v1alpha1
metadata:
name: bare
labels: {}
annotations: {}
holos:
flags:
cluster: k2
kind: ConfigMap
apiVersion: v1
Skip: false
```