Skip to content

Commit

Permalink
begins building out frontend to cs integration
Browse files Browse the repository at this point in the history
  • Loading branch information
tonytheleg committed Jun 4, 2024
1 parent d96a7e6 commit a337a60
Show file tree
Hide file tree
Showing 11 changed files with 294 additions and 72 deletions.
15 changes: 3 additions & 12 deletions dev-infrastructure/docs/development-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ Should your development needs require a running instance of CS to test with, her

To complete the below steps you will need:
1) `podman`, `ocm` cli (latest), and [`yq`](https://github.com/mikefarah/yq) cli (version 4+)
2) The [Clusters Service repo](https://gitlab.cee.redhat.com/service/uhc-clusters-service) cloned down (can also use a fork if you have one)
2) An up-to-date [Clusters Service repo](https://gitlab.cee.redhat.com/service/uhc-clusters-service) cloned down (can also use a fork if you have one)

> If you don't have or want to install `yq`, any steps below using `yq` can be done manually
Expand Down Expand Up @@ -270,17 +270,6 @@ make db/setup

# Initialize the DB
./clusters-service init --config-file ./development.yml

# Update DB to set avaialble versions
# NOTE: required until https://gitlab.cee.redhat.com/service/uhc-clusters-service/-/merge_requests/7895 is merged
# login to db
make db/login

INSERT INTO versions (id, raw_id, enabled, dflt, channel_group, hypershift_enabled, hypershift_default, release_image, rosa_enabled)
VALUES ('openshift-v4.15.11', '4.15.11', 'true', 'true', 'stable', 'true', 'true', '', 'true');

# logout
\q
```

3) Start CS:
Expand Down Expand Up @@ -340,6 +329,8 @@ cat cluster-test.json | ocm post /api/clusters_mgmt/v1/clusters

You should now have a cluster in OCM. You can verify using `ocm list clusters` or `ocm get cluster CLUSTERID`

To create a cluster in CS using a locally running Frontend, see the frontend [README](../../frontend/README.md)

## CS Dev Cleanup

To tear down your CS setup:
Expand Down
3 changes: 3 additions & 0 deletions frontend/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ REGION ?= eastus
frontend:
go build -o aro-hcp-frontend .

run:
./aro-hcp-frontend --use-cache --region eastus --clusters-service-url http://localhost:8000

clean:
rm -f aro-hcp-frontend

Expand Down
29 changes: 21 additions & 8 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# ARO-HCP-FRONTEND

## Build frontend binary for local testing
```
make frontend
```

## Run the frontend binary locally (requires a local running CS to fully function)
```
make run
```

## Build the frontend container
```bash
# Note: until the ACR location is defined, you must set the image base
Expand Down Expand Up @@ -39,8 +49,11 @@ make undeploy-private
## Available endpoints

> Note: If you need a test cluster.json file for some of the below API calls, you can generate one using [utils/create.go](./utils/create.go)
>
> `go run utils/create.go`
>
> Any Create/Get/Delete cluster calls below will expect a running CS in order to function for now


Update a subscription state (Must be **Registered** for other calls to function)
```bash
Expand All @@ -49,36 +62,36 @@ curl -X PUT localhost:8443/subscriptions/YOUR_SUBSCRIPTION_ID?api-version=2.0 --

List the Operations for the Provider
```bash
curl -X GET "https://localhost:8443/providers/Microsoft.RedHatOpenshift/operations?api-version=2024-06-10-preview"
curl -X GET "localhost:8443/providers/Microsoft.RedHatOpenshift/operations?api-version=2024-06-10-preview"
```

List HcpOpenShiftVersions Resources by Location

```bash
curl -X GET "https://localhost:8443/subscriptions/YOUR_SUBSCRIPTION_ID/locations/YOUR_LOCATION/providers/Microsoft.RedHatOpenshift/hcpOpenShiftVersions?api-version=2024-06-10-preview"
curl -X GET "localhost:8443/subscriptions/YOUR_SUBSCRIPTION_ID/locations/YOUR_LOCATION/providers/Microsoft.RedHatOpenshift/hcpOpenShiftVersions?api-version=2024-06-10-preview"
```

List HcpOpenShiftClusterResource Resources by Subscription ID
```bash
curl -X GET "https://localhost:8443/subscriptions/YOUR_SUBSCRIPTION_ID/providers/Microsoft.RedHatOpenshift/hcpOpenShiftClusters?api-version=2024-06-10-preview"
curl -X GET "localhost:8443/subscriptions/YOUR_SUBSCRIPTION_ID/providers/Microsoft.RedHatOpenshift/hcpOpenShiftClusters?api-version=2024-06-10-preview"
```

Get a HcpOpenShiftClusterResource
```bash
curl -X GET "https://localhost:8443/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/YOUR_RESOURCE_GROUP_NAME/providers/Microsoft.RedHatOpenshift/hcpOpenShiftClusters/YOUR_CLUSTER_NAME?api-version=2024-06-10-preview"
curl -X GET "localhost:8443/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/YOUR_RESOURCE_GROUP_NAME/providers/Microsoft.RedHatOpenshift/hcpOpenShiftClusters/YOUR_CLUSTER_NAME?api-version=2024-06-10-preview"
```

Create or Update a HcpOpenShiftClusterResource
```bash
curl -X PUT "https://localhost:8443/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/YOUR_RESOURCE_GROUP_NAME/providers/Microsoft.RedHatOpenshift/hcpOpenShiftClusters/YOUR_CLUSTER_NAME?api-version=2024-06-10-preview" --json @cluster.json
curl -X PUT "localhost:8443/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/YOUR_RESOURCE_GROUP_NAME/providers/Microsoft.RedHatOpenshift/hcpOpenShiftClusters/YOUR_CLUSTER_NAME?api-version=2024-06-10-preview" --json @cluster.json
```

Delete a HcpOpenShiftClusterResource
```bash
curl -X DELETE "https://localhost:8443/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/YOUR_RESOURCE_GROUP_NAME/providers/Microsoft.RedHatOpenshift/hcpOpenShiftClusters/YOUR_CLUSTER_NAME?api-version=2024-06-10-preview"
curl -X DELETE "localhost:8443/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/YOUR_RESOURCE_GROUP_NAME/providers/Microsoft.RedHatOpenshift/hcpOpenShiftClusters/YOUR_CLUSTER_NAME?api-version=2024-06-10-preview"
```

Execute deployment preflight checks
```bash
curl -X POST "https://localhost:8443/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/YOUR_RESOURCE_GROUP_NAME/providers/Microsoft.RedHatOpenshift/deployments/YOUR_DEPLOYMENT_NAME/preflight?api-version=2020-06-01" --json preflight.json
curl -X POST "localhost:8443/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/YOUR_RESOURCE_GROUP_NAME/providers/Microsoft.RedHatOpenshift/deployments/YOUR_DEPLOYMENT_NAME/preflight?api-version=2020-06-01" --json preflight.json
```
4 changes: 2 additions & 2 deletions frontend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ require (

require (
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1
github.com/Azure/azure-sdk-for-go/sdk/internal v1.7.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
Expand All @@ -45,7 +45,7 @@ require (
github.com/microcosm-cc/bluemonday v1.0.18 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/openshift/api v0.0.0-20240429104249-ac9356ba1784 // indirect
github.com/openshift/api v0.0.0-20240429104249-ac9356ba1784
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
Expand Down
6 changes: 4 additions & 2 deletions frontend/pkg/database/cache.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package database

import "context"
import (
"context"
)

var _ DBClient = &Cache{}

Expand Down Expand Up @@ -29,7 +31,7 @@ func (c *Cache) GetClusterDoc(ctx context.Context, resourceID string, partitionK
}

func (c *Cache) SetClusterDoc(ctx context.Context, doc *HCPOpenShiftClusterDocument) error {
c.cluster[doc.ResourceID] = doc
c.cluster[doc.Key] = doc
return nil
}

Expand Down
123 changes: 91 additions & 32 deletions frontend/pkg/frontend/frontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ import (
"strings"
"sync/atomic"

v1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1"

"github.com/google/uuid"
sdk "github.com/openshift-online/ocm-sdk-go"
"github.com/prometheus/client_golang/prometheus/promhttp"

"github.com/Azure/ARO-HCP/frontend/pkg/database"
"github.com/Azure/ARO-HCP/frontend/pkg/ocm"
"github.com/Azure/ARO-HCP/internal/api"
"github.com/Azure/ARO-HCP/internal/api/arm"
)
Expand Down Expand Up @@ -258,12 +261,24 @@ func (f *Frontend) ArmResourceRead(writer http.ResponseWriter, request *http.Req

// URL path is already lowercased by middleware.
resourceID := request.URL.Path
cluster, found := f.cache.GetCluster(resourceID)
if !found {
writer.WriteHeader(http.StatusNotFound)
return
subscriptionID := request.PathValue(PathSegmentSubscriptionID)
doc, err := f.dbClient.GetClusterDoc(ctx, resourceID, subscriptionID)
if err != nil {
if errors.Is(err, database.ErrNotFound) {
f.logger.Error(fmt.Sprintf("existing document not found for cluster: %s", resourceID))
writer.WriteHeader(http.StatusNoContent)
return
} else {
f.logger.Error(err.Error())
writer.WriteHeader(http.StatusInternalServerError)
return
}
}
versionedResource := versionedInterface.NewHCPOpenShiftCluster(cluster)

cluster, _ := f.conn.ClustersMgmt().V1().Clusters().Cluster(doc.ClusterID).Get().Send()
hcpCluster, _ := ocm.ConvertCStoFrontend(*cluster.Body())

versionedResource := versionedInterface.NewHCPOpenShiftCluster(hcpCluster)
resp, err := json.Marshal(versionedResource)
if err != nil {
f.logger.Error(err.Error())
Expand Down Expand Up @@ -296,17 +311,49 @@ func (f *Frontend) ArmResourceCreateOrUpdate(writer http.ResponseWriter, request

// URL path is already lowercased by middleware.
resourceID := request.URL.Path
resourceGroup := request.PathValue(PathSegmentResourceGroupName)
subscriptionID := request.PathValue(PathSegmentSubscriptionID)

cluster, updating := f.cache.GetCluster(resourceID)
versionedCurrentCluster := versionedInterface.NewHCPOpenShiftCluster(cluster)
var doc *database.HCPOpenShiftClusterDocument
var updating bool = true
doc, err = f.dbClient.GetClusterDoc(ctx, resourceID, subscriptionID)
if err != nil {
if errors.Is(err, database.ErrNotFound) {
updating = false
f.logger.Info(fmt.Sprintf("existing document not found for cluster - creating one for %s", resourceID))
doc = &database.HCPOpenShiftClusterDocument{
ID: uuid.New().String(),
Key: resourceID,
PartitionKey: subscriptionID,
}
} else {
f.logger.Error(fmt.Sprintf("failed to fetch document for %s: %v", resourceID, err))
arm.WriteInternalServerError(writer)
return
}
}

var hcpCluster *api.HCPOpenShiftCluster
var csResp *v1.ClusterGetResponse
if doc.ClusterID != "" {
csResp, err = f.conn.ClustersMgmt().V1().Clusters().Cluster(doc.ClusterID).Get().Send()
if err != nil {
f.logger.Error(fmt.Sprintf("failed to fetch document for %s: %v", resourceID, err))
arm.WriteInternalServerError(writer)
return
}
if csResp.Body() != nil {
hcpCluster, _ = ocm.ConvertCStoFrontend(*csResp.Body())
}
}
versionedCurrentCluster := versionedInterface.NewHCPOpenShiftCluster(hcpCluster)

var versionedRequestCluster api.VersionedHCPOpenShiftCluster
switch request.Method {
case http.MethodPut:
versionedRequestCluster = versionedInterface.NewHCPOpenShiftCluster(nil)
case http.MethodPatch:
if cluster == nil {
if hcpCluster == nil {
// PATCH request will not create a new cluster.
originalPath, _ := OriginalPathFromContext(ctx)
f.logger.Error("Resource not found")
Expand All @@ -315,7 +362,7 @@ func (f *Frontend) ArmResourceCreateOrUpdate(writer http.ResponseWriter, request
originalPath, "Resource not found")
return
}
versionedRequestCluster = versionedInterface.NewHCPOpenShiftCluster(cluster)
versionedRequestCluster = versionedInterface.NewHCPOpenShiftCluster(hcpCluster)
}

body, err := BodyFromContext(ctx)
Expand All @@ -336,28 +383,24 @@ func (f *Frontend) ArmResourceCreateOrUpdate(writer http.ResponseWriter, request
return
}

cluster = api.NewDefaultHCPOpenShiftCluster()
versionedRequestCluster.Normalize(cluster)
f.cache.SetCluster(resourceID, cluster)
hcpCluster = api.NewDefaultHCPOpenShiftCluster()
versionedRequestCluster.Normalize(hcpCluster)

var doc *database.HCPOpenShiftClusterDocument
doc, err = f.dbClient.GetClusterDoc(ctx, resourceID, subscriptionID)
hcpCluster.Name = request.PathValue(PathSegmentResourceName)
newCsCluster, err := ocm.BuildCSCluster(resourceGroup, subscriptionID, hcpCluster)
if err != nil {
if errors.Is(err, database.ErrNotFound) {
f.logger.Info(fmt.Sprintf("existing document not found for cluster - creating one for %s", resourceID))
doc = &database.HCPOpenShiftClusterDocument{
ID: uuid.New().String(),
Key: resourceID,
ClusterID: NewUID(),
PartitionKey: subscriptionID,
}
} else {
f.logger.Error(fmt.Sprintf("failed to fetch document for %s: %v", resourceID, err))
arm.WriteInternalServerError(writer)
return
}
f.logger.Error(err.Error())
arm.WriteInternalServerError(writer)
return
}

req, err := f.conn.ClustersMgmt().V1().Clusters().Add().Body(newCsCluster).Send()
if err != nil {
f.logger.Error(err.Error())
arm.WriteInternalServerError(writer)
return
}
doc.ClusterID = req.Body().ID()
err = f.dbClient.SetClusterDoc(ctx, doc)
if err != nil {
f.logger.Error(fmt.Sprintf("failed to create document for resource %s: %v", resourceID, err))
Expand Down Expand Up @@ -399,12 +442,28 @@ func (f *Frontend) ArmResourceDelete(writer http.ResponseWriter, request *http.R
resourceID := request.URL.Path
subscriptionID := request.PathValue(PathSegmentSubscriptionID)

_, found := f.cache.GetCluster(resourceID)
if !found {
writer.WriteHeader(http.StatusNotFound)
return
var doc *database.HCPOpenShiftClusterDocument
doc, err = f.dbClient.GetClusterDoc(ctx, resourceID, subscriptionID)
if err != nil {
if errors.Is(err, database.ErrNotFound) {
f.logger.Info(fmt.Sprintf("cluster document cannot be deleted -- document not found for %s", resourceID))
writer.WriteHeader(http.StatusNoContent)
return
} else {
f.logger.Error(fmt.Sprintf("failed to fetch document for %s: %v", resourceID, err))
arm.WriteInternalServerError(writer)
return
}
}

if doc.ClusterID != "" {
_, err = f.conn.ClustersMgmt().V1().Clusters().Cluster(doc.ClusterID).Delete().Send()
if err != nil {
f.logger.Error(fmt.Sprintf("failed to delete cluster %s: %v", doc.ClusterID, err))
arm.WriteInternalServerError(writer)
return
}
}
f.cache.DeleteCluster(resourceID)

err = f.dbClient.DeleteClusterDoc(ctx, resourceID, subscriptionID)
if err != nil {
Expand Down
3 changes: 1 addition & 2 deletions frontend/pkg/frontend/middleware_validatestatic.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func MiddlewareValidateStatic(w http.ResponseWriter, r *http.Request, next http.
}
}

if resourceID.Name != "" {
if resourceID.Name != "" && resourceID.ResourceType.String() != "Microsoft.Resources/subscriptions" {
if !rxResourceName.MatchString(resourceID.Name) {
arm.WriteError(w, http.StatusBadRequest,
arm.CloudErrorInvalidResourceName,
Expand All @@ -59,6 +59,5 @@ func MiddlewareValidateStatic(w http.ResponseWriter, r *http.Request, next http.
}
}
}

next(w, r)
}
5 changes: 5 additions & 0 deletions frontend/pkg/frontend/middleware_validatestatic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ func TestMiddlewareValidateStatic(t *testing.T) {
expectedStatusCode: http.StatusBadRequest,
expectedBody: "The Resource 'MICROSOFT.REDHATOPENSHIFT/HCPOPENSHIFTCLUSTERS/$' under resource group 'MyResourceGroup' is invalid.",
},
{
name: "Resource name is a valid subscription ID",
path: "/SUBSCRIPTIONS/00000000-0000-0000-0000-000000000000",
expectedStatusCode: http.StatusOK,
},
}

for _, tc := range tests {
Expand Down
Loading

0 comments on commit a337a60

Please sign in to comment.