Change the Admin Portal APIs for more consistency while we have a chance to

This commit is contained in:
Amber Brown 2021-08-18 09:47:41 +10:00
Родитель 0a236a3dae
Коммит 6a023afc1f
4 изменённых файлов: 246 добавлений и 128 удалений

Просмотреть файл

@ -4,33 +4,26 @@ package portal
// Licensed under the Apache License 2.0.
import (
"context"
"encoding/json"
"fmt"
"net/http"
"sort"
"strings"
"github.com/Azure/ARO-RP/pkg/api/validate"
"github.com/Azure/ARO-RP/pkg/portal/cluster"
"github.com/Azure/ARO-RP/pkg/proxy"
"time"
"github.com/Azure/go-autorest/autorest/azure"
)
type AdminOpenShiftCluster struct {
Key string `json:"key"`
Name string `json:"name"`
Id string `json:"id"`
State string `json:"state"`
Version string `json:"version"`
CreatedDate string `json:"createdDate"`
LastModified string `json:"lastModified"`
ProvisionedBy string `json:"provisionedBy"`
FailedState string `json:"failedState"`
Subscription string `json:"subscription"`
ResourceGroup string `json:"resourceGroup"`
ConsoleLink string `json:"consoleLink"`
Key string `json:"key"`
Name string `json:"name"`
Subscription string `json:"subscription"`
ResourceGroup string `json:"resourceGroup"`
ResourceId string `json:"resourceId"`
ProvisioningState string `json:"provisioningState"`
FailedProvisioningState string `json:"failedprovisioningState"`
Version string `json:"version"`
CreatedAt string `json:"createdAt"`
ProvisionedBy string `json:"provisionedBy"`
}
func (p *portal) clusters(w http.ResponseWriter, r *http.Request) {
@ -59,23 +52,23 @@ func (p *portal) clusters(w http.ResponseWriter, r *http.Request) {
resourceGroup = resource.ResourceGroup
name = resource.ResourceName
}
LastModified := "Unknown"
if doc.OpenShiftCluster.SystemData.LastModifiedAt != nil {
LastModified = doc.OpenShiftCluster.SystemData.LastModifiedAt.Format("2006-01-02 15:04:05")
createdAt := "Unknown"
if !doc.OpenShiftCluster.Properties.CreatedAt.IsZero() {
createdAt = doc.OpenShiftCluster.Properties.CreatedAt.Format(time.RFC3339)
}
clusters = append(clusters, &AdminOpenShiftCluster{
Key: doc.ID,
Id: doc.OpenShiftCluster.ID,
Name: name,
Subscription: subscription,
ResourceGroup: resourceGroup,
Version: doc.OpenShiftCluster.Properties.ClusterProfile.Version,
CreatedDate: doc.OpenShiftCluster.Properties.CreatedAt.Format("2006-01-02 15:04:05"),
LastModified: LastModified,
ProvisionedBy: doc.OpenShiftCluster.Properties.ProvisionedBy,
State: ps.String(),
FailedState: fps.String(),
Key: doc.ID,
ResourceId: doc.OpenShiftCluster.ID,
Name: name,
Subscription: subscription,
ResourceGroup: resourceGroup,
Version: doc.OpenShiftCluster.Properties.ClusterProfile.Version,
CreatedAt: createdAt,
ProvisionedBy: doc.OpenShiftCluster.Properties.ProvisionedBy,
ProvisioningState: ps.String(),
FailedProvisioningState: fps.String(),
})
}
@ -91,32 +84,6 @@ func (p *portal) clusters(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write(b)
}
func (p *portal) makeFetcher(ctx context.Context, r *http.Request) (cluster.FetchClient, error) {
resourceID := strings.Join(strings.Split(r.URL.Path, "/")[:9], "/")
if !validate.RxClusterID.MatchString(resourceID) {
return nil, fmt.Errorf("invalid resource ID")
}
doc, err := p.dbOpenShiftClusters.Get(ctx, resourceID)
if err != nil {
return nil, err
}
// In development mode, we can have localhost "fake" APIServers which don't
// get proxied, so use a direct dialer for this
var dialer proxy.Dialer
if p.env.IsLocalDevelopmentMode() && doc.OpenShiftCluster.Properties.APIServerProfile.IP == "127.0.0.1" {
dialer, err = proxy.NewDialer(false)
if err != nil {
return nil, err
}
} else {
dialer = p.dialer
}
return cluster.NewFetchClient(p.log, dialer, doc)
}
func (p *portal) clusterOperators(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

Просмотреть файл

@ -5,32 +5,31 @@ package portal
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"time"
"github.com/gorilla/mux"
)
// TODO: Commented out fields contain complex objects
type AdminOpenShiftClusterDocument struct {
ResourceID string `json:"resourceId"`
type AdminOpenShiftClusterDetail struct {
Name string `json:"name"`
Location string `json:"location"`
CreatedBy string `json:"createdBy"`
CreatedAt string `json:"createdAt"`
LastModifiedBy string `json:"lastModifiedBy"`
LastModifiedAt string `json:"lastModifiedAt"`
ArchitectureVersion string `json:"architectureVersion"`
Subscription string `json:"subscription"`
ResourceGroup string `json:"resourceGroup"`
ResourceId string `json:"resourceId"`
ProvisioningState string `json:"provisioningState"`
LastProvisioningState string `json:"lastProvisioningState"`
FailedProvisioningState string `json:"failedProvisioningState"`
LastAdminUpdateError string `json:"lastAdminUpdateError"`
Version string `json:"version"`
ConsoleLink string `json:"consoleLink"`
CreatedAt string `json:"createdAt"`
ProvisionedBy string `json:"provisionedBy"`
CreatedBy string `json:"createdBy"`
ArchitectureVersion string `json:"architectureVersion"`
LastProvisioningState string `json:"lastProvisioningState"`
LastAdminUpdateError string `json:"lastAdminUpdateError"`
InfraId string `json:"infraId"`
ApiServerVisibility string `json:"apiServerVisibility"`
ApiServerURL string `json:"apiServerURL"`
InstallPhase string `json:"installStatus"`
}
@ -43,23 +42,17 @@ func (p *portal) clusterInfo(w http.ResponseWriter, r *http.Request) {
resourceGroup := apiVars["resourceGroup"]
clusterName := apiVars["name"]
resourceId := strings.ToLower("/subscriptions/" + subscription + "/resourceGroups/" + resourceGroup + "/providers/Microsoft.RedHatOpenShift/openShiftClusters/" + clusterName)
resourceId := strings.ToLower(fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.RedHatOpenShift/openShiftClusters/%s", subscription, resourceGroup, clusterName))
doc, err := p.dbOpenShiftClusters.Get(ctx, resourceId)
if err != nil {
http.Error(w, "Cluster not found", http.StatusNotFound)
return
}
createdAt := "Unknown"
if doc.OpenShiftCluster.SystemData.CreatedAt != nil {
createdAt = doc.OpenShiftCluster.SystemData.CreatedAt.Format("2006-01-02 15:04:05")
}
lastModifiedAt := "Unknown"
if doc.OpenShiftCluster.SystemData.CreatedAt != nil {
lastModifiedAt = doc.OpenShiftCluster.SystemData.LastModifiedAt.Format("2006-01-02 15:04:05")
if !doc.OpenShiftCluster.Properties.CreatedAt.IsZero() {
createdAt = doc.OpenShiftCluster.Properties.CreatedAt.Format(time.RFC3339)
}
installPhase := "Installed"
@ -67,26 +60,24 @@ func (p *portal) clusterInfo(w http.ResponseWriter, r *http.Request) {
installPhase = doc.OpenShiftCluster.Properties.Install.Phase.String()
}
// TODO: Commented out fields contain complex objects
clusterInfo := AdminOpenShiftClusterDocument{
ResourceID: resourceId,
clusterInfo := AdminOpenShiftClusterDetail{
ResourceId: resourceId,
Name: clusterName,
Location: doc.OpenShiftCluster.Location,
CreatedBy: doc.OpenShiftCluster.SystemData.CreatedBy,
Subscription: subscription,
ResourceGroup: resourceGroup,
CreatedAt: createdAt,
LastModifiedBy: doc.OpenShiftCluster.SystemData.LastModifiedBy,
LastModifiedAt: lastModifiedAt,
ArchitectureVersion: strconv.Itoa(int(doc.OpenShiftCluster.Properties.ArchitectureVersion)),
ProvisionedBy: doc.OpenShiftCluster.Properties.ProvisionedBy,
ProvisioningState: doc.OpenShiftCluster.Properties.ProvisioningState.String(),
LastProvisioningState: doc.OpenShiftCluster.Properties.LastProvisioningState.String(),
FailedProvisioningState: doc.OpenShiftCluster.Properties.FailedProvisioningState.String(),
LastAdminUpdateError: doc.OpenShiftCluster.Properties.LastAdminUpdateError,
Version: doc.OpenShiftCluster.Properties.ClusterProfile.Version,
ConsoleLink: doc.OpenShiftCluster.Properties.ConsoleProfile.URL,
InfraId: doc.OpenShiftCluster.Properties.InfraID,
ApiServerVisibility: string(doc.OpenShiftCluster.Properties.APIServerProfile.Visibility),
ApiServerURL: doc.OpenShiftCluster.Properties.APIServerProfile.URL,
InstallPhase: installPhase,
CreatedBy: doc.OpenShiftCluster.Properties.CreatedBy,
ArchitectureVersion: strconv.Itoa(int(doc.OpenShiftCluster.Properties.ArchitectureVersion)),
LastProvisioningState: doc.OpenShiftCluster.Properties.LastProvisioningState.String(),
LastAdminUpdateError: doc.OpenShiftCluster.Properties.LastAdminUpdateError,
InfraId: doc.OpenShiftCluster.Properties.InfraID,
ApiServerVisibility: string(doc.OpenShiftCluster.Properties.APIServerProfile.Visibility),
InstallPhase: installPhase,
}
b, err := json.MarshalIndent(clusterInfo, "", " ")

Просмотреть файл

@ -8,38 +8,47 @@ import (
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/go-test/deep"
"github.com/gorilla/mux"
"github.com/Azure/ARO-RP/pkg/api"
testdatabase "github.com/Azure/ARO-RP/test/database"
)
func TestClusters(t *testing.T) {
func TestClusterList(t *testing.T) {
dbOpenShiftClusters, _ := testdatabase.NewFakeOpenShiftClusters()
fixture := testdatabase.NewFixture().
WithOpenShiftClusters(dbOpenShiftClusters)
fixture.AddOpenShiftClusterDocuments(&api.OpenShiftClusterDocument{
ID: "00000000-0000-0000-0000-000000000000",
Key: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourcegroupname/providers/microsoft.redhatopenshift/openshiftclusters/succeeded",
OpenShiftCluster: &api.OpenShiftCluster{
ID: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourceGroupName/providers/microsoft.redhatopenshift/openshiftclusters/succeeded",
Properties: api.OpenShiftClusterProperties{
ProvisioningState: api.ProvisioningStateSucceeded,
parsedTime, err := time.Parse(time.RFC3339, "2011-01-02T01:03:00Z")
if err != nil {
t.Error(err)
}
fixture.AddOpenShiftClusterDocuments(
&api.OpenShiftClusterDocument{
ID: "00000000-0000-0000-0000-000000000000",
Key: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourcegroupname/providers/microsoft.redhatopenshift/openshiftclusters/succeeded",
OpenShiftCluster: &api.OpenShiftCluster{
ID: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourceGroupName/providers/microsoft.redhatopenshift/openshiftclusters/succeeded",
Properties: api.OpenShiftClusterProperties{
ProvisioningState: api.ProvisioningStateSucceeded,
CreatedAt: parsedTime,
},
},
}, &api.OpenShiftClusterDocument{
ID: "00000000-0000-0000-0000-000000000001",
Key: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourcegroupname/providers/microsoft.redhatopenshift/openshiftclusters/creating",
OpenShiftCluster: &api.OpenShiftCluster{
ID: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourceGroupName/providers/microsoft.redhatopenshift/openshiftclusters/creating",
Properties: api.OpenShiftClusterProperties{
ProvisioningState: api.ProvisioningStateCreating,
},
},
},
}, &api.OpenShiftClusterDocument{
ID: "00000000-0000-0000-0000-000000000001",
Key: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourcegroupname/providers/microsoft.redhatopenshift/openshiftclusters/creating",
OpenShiftCluster: &api.OpenShiftCluster{
ID: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourceGroupName/providers/microsoft.redhatopenshift/openshiftclusters/creating",
Properties: api.OpenShiftClusterProperties{
ProvisioningState: api.ProvisioningStateCreating,
},
},
},
&api.OpenShiftClusterDocument{
ID: "00000000-0000-0000-0000-000000000002",
Key: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourcegroupname/providers/microsoft.redhatopenshift/openshiftclusters/failedcreate",
@ -52,7 +61,7 @@ func TestClusters(t *testing.T) {
},
})
err := fixture.Create()
err = fixture.Create()
if err != nil {
t.Fatal(err)
}
@ -61,9 +70,15 @@ func TestClusters(t *testing.T) {
dbOpenShiftClusters: dbOpenShiftClusters,
}
w := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/api/clusters", nil)
if err != nil {
t.Error(err)
}
p.clusters(w, &http.Request{})
aadAuthenticatedRouter := mux.NewRouter()
p.aadAuthenticatedRoutes(aadAuthenticatedRouter)
w := httptest.NewRecorder()
aadAuthenticatedRouter.ServeHTTP(w, req)
if w.Header().Get("Content-Type") != "application/json" {
t.Error(w.Header().Get("Content-Type"))
@ -77,21 +92,33 @@ func TestClusters(t *testing.T) {
expected := []AdminOpenShiftCluster{
{
Key: "00000000-0000-0000-0000-000000000000",
Name: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourceGroupName/providers/microsoft.redhatopenshift/openshiftclusters/succeeded",
State: api.ProvisioningStateSucceeded.String(),
Key: "00000000-0000-0000-0000-000000000000",
ResourceId: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourceGroupName/providers/microsoft.redhatopenshift/openshiftclusters/succeeded",
Name: "succeeded",
ResourceGroup: "resourceGroupName",
Subscription: "00000000-0000-0000-0000-000000000000",
CreatedAt: "2011-01-02T01:03:00Z",
ProvisioningState: api.ProvisioningStateSucceeded.String(),
},
{
Key: "00000000-0000-0000-0000-000000000001",
Name: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourceGroupName/providers/microsoft.redhatopenshift/openshiftclusters/creating",
State: api.ProvisioningStateCreating.String(),
Key: "00000000-0000-0000-0000-000000000001",
ResourceId: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourceGroupName/providers/microsoft.redhatopenshift/openshiftclusters/creating",
Name: "creating",
ResourceGroup: "resourceGroupName",
Subscription: "00000000-0000-0000-0000-000000000000",
CreatedAt: "Unknown",
ProvisioningState: api.ProvisioningStateCreating.String(),
},
{
Key: "00000000-0000-0000-0000-000000000002",
Name: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourceGroupName/providers/microsoft.redhatopenshift/openshiftclusters/failedcreate",
State: api.ProvisioningStateFailed.String(),
FailedState: api.ProvisioningStateCreating.String(),
Key: "00000000-0000-0000-0000-000000000002",
ResourceId: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourceGroupName/providers/microsoft.redhatopenshift/openshiftclusters/failedcreate",
Name: "failedcreate",
ResourceGroup: "resourceGroupName",
Subscription: "00000000-0000-0000-0000-000000000000",
CreatedAt: "Unknown",
ProvisioningState: api.ProvisioningStateFailed.String(),
FailedProvisioningState: api.ProvisioningStateCreating.String(),
},
}
@ -99,3 +126,106 @@ func TestClusters(t *testing.T) {
t.Error(l)
}
}
func TestClusterDetail(t *testing.T) {
dbOpenShiftClusters, _ := testdatabase.NewFakeOpenShiftClusters()
fixture := testdatabase.NewFixture().
WithOpenShiftClusters(dbOpenShiftClusters)
parsedTime, err := time.Parse(time.RFC3339, "2011-01-02T01:03:00Z")
if err != nil {
t.Error(err)
}
fixture.AddOpenShiftClusterDocuments(
&api.OpenShiftClusterDocument{
ID: "00000000-0000-0000-0000-000000000000",
Key: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourcegroupname/providers/microsoft.redhatopenshift/openshiftclusters/succeeded",
OpenShiftCluster: &api.OpenShiftCluster{
ID: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourceGroupName/providers/microsoft.redhatopenshift/openshiftclusters/succeeded",
Properties: api.OpenShiftClusterProperties{
LastProvisioningState: api.ProvisioningStateCreating,
ProvisioningState: api.ProvisioningStateSucceeded,
CreatedAt: parsedTime,
ProvisionedBy: "aaaaab",
InfraID: "blah",
ArchitectureVersion: 2,
CreatedBy: "aaa",
ClusterProfile: api.ClusterProfile{
Version: "4.4.4",
},
APIServerProfile: api.APIServerProfile{
IP: "1.2.3.4",
IntIP: "2.3.4.5",
Visibility: api.VisibilityPrivate,
URL: "example.com",
},
},
},
},
&api.OpenShiftClusterDocument{
ID: "00000000-0000-0000-0000-000000000002",
Key: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourcegroupname/providers/microsoft.redhatopenshift/openshiftclusters/failedcreate",
OpenShiftCluster: &api.OpenShiftCluster{
ID: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourceGroupName/providers/microsoft.redhatopenshift/openshiftclusters/failedcreate",
Properties: api.OpenShiftClusterProperties{
ProvisioningState: api.ProvisioningStateFailed,
FailedProvisioningState: api.ProvisioningStateCreating,
},
},
})
err = fixture.Create()
if err != nil {
t.Fatal(err)
}
p := &portal{
dbOpenShiftClusters: dbOpenShiftClusters,
}
req, err := http.NewRequest("GET", "/api/00000000-0000-0000-0000-000000000000/resourcegroupname/succeeded", nil)
if err != nil {
t.Error(err)
}
aadAuthenticatedRouter := mux.NewRouter()
p.aadAuthenticatedRoutes(aadAuthenticatedRouter)
w := httptest.NewRecorder()
aadAuthenticatedRouter.ServeHTTP(w, req)
if w.Header().Get("Content-Type") != "application/json" {
t.Error(w.Body)
t.Error(w.Header().Get("Content-Type"))
}
var r map[string]string
err = json.NewDecoder(w.Body).Decode(&r)
if err != nil {
t.Fatal(err)
}
expected := map[string]string{
"resourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourcegroupname/providers/microsoft.redhatopenshift/openshiftclusters/succeeded",
"name": "succeeded",
"resourceGroup": "resourcegroupname",
"subscription": "00000000-0000-0000-0000-000000000000",
"createdAt": "2011-01-02T01:03:00Z",
"infraId": "blah",
"version": "4.4.4",
"createdBy": "aaa",
"provisionedBy": "aaaaab",
"architectureVersion": "2",
"failedProvisioningState": "",
"apiServerVisibility": "Private",
"lastAdminUpdateError": "",
"lastProvisioningState": api.ProvisioningStateCreating.String(),
"provisioningState": api.ProvisioningStateSucceeded.String(),
"installStatus": "Installed",
}
for _, l := range deep.Equal(expected, r) {
t.Error(l)
}
}

Просмотреть файл

@ -14,16 +14,19 @@ import (
"log"
"net"
"net/http"
"strings"
"time"
"github.com/gorilla/csrf"
"github.com/gorilla/mux"
"github.com/sirupsen/logrus"
"github.com/Azure/ARO-RP/pkg/api/validate"
"github.com/Azure/ARO-RP/pkg/database"
"github.com/Azure/ARO-RP/pkg/env"
frontendmiddleware "github.com/Azure/ARO-RP/pkg/frontend/middleware"
"github.com/Azure/ARO-RP/pkg/metrics"
"github.com/Azure/ARO-RP/pkg/portal/cluster"
"github.com/Azure/ARO-RP/pkg/portal/kubeconfig"
"github.com/Azure/ARO-RP/pkg/portal/middleware"
"github.com/Azure/ARO-RP/pkg/portal/prometheus"
@ -260,8 +263,35 @@ func (p *portal) aadAuthenticatedRoutes(r *mux.Router) {
r.NewRoute().Methods(http.MethodGet).Path("/api/info").HandlerFunc(p.info)
// Cluster-specific routes
r.NewRoute().PathPrefix("/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/microsoft.redhatopenshift/openshiftclusters/{resourceName}/clusteroperators").HandlerFunc(p.clusterOperators)
r.NewRoute().Methods(http.MethodGet).Path("/api/{subscription}/{resourceGroup}/{name}/clusterinfo").HandlerFunc(p.clusterInfo)
r.NewRoute().PathPrefix("/api/{subscription}/{resourceGroup}/{name}/clusteroperators").HandlerFunc(p.clusterOperators)
r.NewRoute().Methods(http.MethodGet).Path("/api/{subscription}/{resourceGroup}/{name}").HandlerFunc(p.clusterInfo)
}
// makeFetcher creates a cluster.FetchClient suitable for use by the Portal REST API
func (p *portal) makeFetcher(ctx context.Context, r *http.Request) (cluster.FetchClient, error) {
resourceID := strings.Join(strings.Split(r.URL.Path, "/")[:9], "/")
if !validate.RxClusterID.MatchString(resourceID) {
return nil, fmt.Errorf("invalid resource ID")
}
doc, err := p.dbOpenShiftClusters.Get(ctx, resourceID)
if err != nil {
return nil, err
}
// In development mode, we can have localhost "fake" APIServers which don't
// get proxied, so use a direct dialer for this
var dialer proxy.Dialer
if p.env.IsLocalDevelopmentMode() && doc.OpenShiftCluster.Properties.APIServerProfile.IP == "127.0.0.1" {
dialer, err = proxy.NewDialer(false)
if err != nil {
return nil, err
}
} else {
dialer = p.dialer
}
return cluster.NewFetchClient(p.log, dialer, doc)
}
func (p *portal) serve(path string) func(w http.ResponseWriter, r *http.Request) {