query + tests for maintenancemanifests

This commit is contained in:
Amber Brown 2024-07-18 13:18:51 +10:00
Родитель e9a940316e
Коммит e4be577423
4 изменённых файлов: 276 добавлений и 14 удалений

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

@ -1,5 +1,8 @@
package admin
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import "github.com/Azure/ARO-RP/pkg/api"
type maintenanceManifestConverter struct{}

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

@ -5,14 +5,18 @@ package frontend
import (
"context"
"encoding/json"
"fmt"
"math"
"net/http"
"path/filepath"
"strconv"
"strings"
"github.com/sirupsen/logrus"
"github.com/ugorji/go/codec"
"github.com/Azure/ARO-RP/pkg/api"
"github.com/Azure/ARO-RP/pkg/api/admin"
"github.com/Azure/ARO-RP/pkg/frontend/middleware"
)
@ -20,7 +24,7 @@ func (f *frontend) getAdminMaintManifests(w http.ResponseWriter, r *http.Request
ctx := r.Context()
log := ctx.Value(middleware.ContextKeyLog).(*logrus.Entry)
resourceID := strings.TrimPrefix(filepath.Dir(r.URL.Path), "/admin")
b, err := f._getAdminMaintManifests(ctx, resourceID)
b, err := f._getAdminMaintManifests(ctx, r, resourceID)
if cloudErr, ok := err.(*api.CloudError); ok {
api.WriteCloudError(w, cloudErr)
@ -30,28 +34,65 @@ func (f *frontend) getAdminMaintManifests(w http.ResponseWriter, r *http.Request
adminReply(log, w, nil, b, err)
}
func (f *frontend) _getAdminMaintManifests(ctx context.Context, resourceID string) ([]byte, error) {
doc, err := f.dbOpenShiftClusters.Get(ctx, resourceID)
func (f *frontend) _getAdminMaintManifests(ctx context.Context, r *http.Request, resourceID string) ([]byte, error) {
limitstr := r.URL.Query().Get("limit")
limit, err := strconv.Atoi(limitstr)
if err != nil {
limit = 100
}
converter := f.apis[admin.APIVersion].MaintenanceManifestConverter
dbOpenShiftClusters, err := f.dbGroup.OpenShiftClusters()
if err != nil {
return nil, api.NewCloudError(http.StatusInternalServerError, api.CloudErrorCodeInternalServerError, "", err.Error())
}
dbMaintenanceManifests, err := f.dbGroup.MaintenanceManifests()
if err != nil {
return nil, api.NewCloudError(http.StatusInternalServerError, api.CloudErrorCodeInternalServerError, "", err.Error())
}
doc, err := dbOpenShiftClusters.Get(ctx, resourceID)
if err != nil {
return nil, api.NewCloudError(http.StatusNotFound, api.CloudErrorCodeNotFound, "", "cluster not found")
}
f.dbOpenShiftVersions
if doc.OpenShiftCluster.Properties.HiveProfile.Namespace == "" {
return nil, api.NewCloudError(http.StatusNoContent, api.CloudErrorCodeResourceNotFound, "", "cluster is not managed by hive")
if doc.OpenShiftCluster.Properties.ProvisioningState == api.ProvisioningStateDeleting {
return nil, api.NewCloudError(http.StatusNotFound, api.CloudErrorCodeNotFound, "", "cluster being deleted")
}
cd, err := f.hiveClusterManager.GetClusterDeployment(ctx, doc)
skipToken, err := f.parseSkipToken(r.URL.String())
if err != nil {
return nil, api.NewCloudError(http.StatusNotFound, api.CloudErrorCodeNotFound, "", "cluster deployment not found")
return nil, err
}
var b []byte
err = codec.NewEncoderBytes(&b, &codec.JsonHandle{}).Encode(cd)
i, err := dbMaintenanceManifests.GetByClusterResourceID(ctx, resourceID, skipToken)
if err != nil {
return nil, api.NewCloudError(http.StatusInternalServerError, api.CloudErrorCodeInternalServerError, "", "unable to marshal response")
return nil, api.NewCloudError(http.StatusInternalServerError, api.CloudErrorCodeInternalServerError, "", err.Error())
}
return b, nil
docList := make([]*api.MaintenanceManifestDocument, 0)
for {
docs, err := i.Next(ctx, int(math.Min(float64(limit), 10)))
if err != nil {
return nil, api.NewCloudError(http.StatusInternalServerError, api.CloudErrorCodeInternalServerError, "", fmt.Errorf("failed reading next manifest document: %w", err).Error())
}
if docs == nil {
break
}
docList = append(docList, docs.MaintenanceManifestDocuments...)
if len(docList) >= limit {
break
}
}
nextLink, err := f.buildNextLink(r.Header.Get("Referer"), i.Continuation())
if err != nil {
return nil, err
}
return json.MarshalIndent(converter.ToExternalList(docList, nextLink), "", " ")
}

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

@ -0,0 +1,214 @@
package frontend
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import (
"context"
"encoding/base64"
"fmt"
"net/http"
"net/url"
"strings"
"testing"
"time"
"github.com/Azure/ARO-RP/pkg/api"
"github.com/Azure/ARO-RP/pkg/api/admin"
"github.com/Azure/ARO-RP/pkg/metrics/noop"
testdatabase "github.com/Azure/ARO-RP/test/database"
)
func TestMIMOList(t *testing.T) {
mockSubID := "00000000-0000-0000-0000-000000000000"
mockTenantID := "00000000-0000-0000-0000-000000000000"
resourceID := fmt.Sprintf("/subscriptions/%s/resourcegroups/resourceGroup/providers/Microsoft.RedHatOpenShift/openShiftClusters/resourceName", mockSubID)
ctx := context.Background()
type test struct {
name string
fixtures func(f *testdatabase.Fixture)
limit int
wantStatusCode int
wantResponse *admin.MaintenanceManifestList
wantError string
}
for _, tt := range []*test{
{
name: "no entries",
fixtures: func(f *testdatabase.Fixture) {
f.AddOpenShiftClusterDocuments(&api.OpenShiftClusterDocument{
Key: strings.ToLower(resourceID),
OpenShiftCluster: &api.OpenShiftCluster{
ID: resourceID,
Name: "resourceName",
Type: "Microsoft.RedHatOpenShift/openshiftClusters",
},
})
},
wantResponse: &admin.MaintenanceManifestList{
MaintenanceManifests: []*admin.MaintenanceManifest{},
},
wantStatusCode: http.StatusOK,
},
{
name: "single entry",
fixtures: func(f *testdatabase.Fixture) {
f.AddOpenShiftClusterDocuments(&api.OpenShiftClusterDocument{
Key: strings.ToLower(resourceID),
OpenShiftCluster: &api.OpenShiftCluster{
ID: resourceID,
Name: "resourceName",
Type: "Microsoft.RedHatOpenShift/openshiftClusters",
},
})
f.AddMaintenanceManifestDocuments(&api.MaintenanceManifestDocument{
ClusterResourceID: strings.ToLower(resourceID),
MaintenanceManifest: &api.MaintenanceManifest{
MaintenanceSetID: "exampleset",
State: api.MaintenanceManifestStatePending,
RunAfter: 1,
RunBefore: 1,
},
})
},
wantResponse: &admin.MaintenanceManifestList{
MaintenanceManifests: []*admin.MaintenanceManifest{
{
ID: "07070707-0707-0707-0707-070707070001",
MaintenanceSetID: "exampleset",
State: admin.MaintenanceManifestStatePending,
Priority: 0,
RunAfter: 1,
RunBefore: 1,
},
},
},
wantStatusCode: http.StatusOK,
},
{
name: "limit over",
limit: 1,
fixtures: func(f *testdatabase.Fixture) {
f.AddOpenShiftClusterDocuments(&api.OpenShiftClusterDocument{
Key: strings.ToLower(resourceID),
OpenShiftCluster: &api.OpenShiftCluster{
ID: resourceID,
Name: "resourceName",
Type: "Microsoft.RedHatOpenShift/openshiftClusters",
},
})
f.AddMaintenanceManifestDocuments(&api.MaintenanceManifestDocument{
ClusterResourceID: strings.ToLower(resourceID),
MaintenanceManifest: &api.MaintenanceManifest{
MaintenanceSetID: "exampleset",
State: api.MaintenanceManifestStatePending,
RunAfter: 1,
RunBefore: 1,
},
})
f.AddMaintenanceManifestDocuments(&api.MaintenanceManifestDocument{
ClusterResourceID: strings.ToLower(resourceID),
MaintenanceManifest: &api.MaintenanceManifest{
MaintenanceSetID: "exampleset2",
State: api.MaintenanceManifestStatePending,
RunAfter: 1,
RunBefore: 1,
},
})
},
wantResponse: &admin.MaintenanceManifestList{
NextLink: "https://mockrefererhost/?%24skipToken=" + url.QueryEscape(base64.StdEncoding.EncodeToString([]byte("FAKE1"))),
MaintenanceManifests: []*admin.MaintenanceManifest{
{
ID: "07070707-0707-0707-0707-070707070001",
MaintenanceSetID: "exampleset",
State: admin.MaintenanceManifestStatePending,
Priority: 0,
RunAfter: 1,
RunBefore: 1,
},
},
},
wantStatusCode: http.StatusOK,
},
{
name: "cluster being deleted",
fixtures: func(f *testdatabase.Fixture) {
f.AddOpenShiftClusterDocuments(&api.OpenShiftClusterDocument{
Key: strings.ToLower(resourceID),
OpenShiftCluster: &api.OpenShiftCluster{
ID: resourceID,
Name: "resourceName",
Type: "Microsoft.RedHatOpenShift/openshiftClusters",
Properties: api.OpenShiftClusterProperties{
ProvisioningState: api.ProvisioningStateDeleting,
},
},
})
},
wantError: "404: NotFound: : cluster being deleted",
wantStatusCode: http.StatusNotFound,
},
{
name: "missing cluster",
wantError: "404: NotFound: : cluster not found",
wantStatusCode: http.StatusNotFound,
},
} {
t.Run(tt.name, func(t *testing.T) {
now := func() time.Time { return time.Unix(1000, 0) }
ti := newTestInfra(t).WithOpenShiftClusters().WithSubscriptions().WithMaintenanceManifests(now)
defer ti.done()
ti.fixture.AddSubscriptionDocuments(&api.SubscriptionDocument{
ID: mockSubID,
Subscription: &api.Subscription{
State: api.SubscriptionStateRegistered,
Properties: &api.SubscriptionProperties{
TenantID: mockTenantID,
},
},
})
if tt.fixtures != nil {
tt.fixtures(ti.fixture)
}
err := ti.buildFixtures(nil)
if err != nil {
t.Fatal(err)
}
f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, ti.dbGroup, api.APIs, &noop.Noop{}, &noop.Noop{}, testdatabase.NewFakeAEAD(), nil, nil, nil, nil, nil)
if err != nil {
t.Fatal(err)
}
go f.Run(ctx, nil, nil)
if tt.limit == 0 {
tt.limit = 100
}
fmt.Printf("limit: %d", tt.limit)
resp, b, err := ti.request(http.MethodGet,
fmt.Sprintf("https://server/admin%s/maintenancemanifests?limit=%d", resourceID, tt.limit),
http.Header{
"Referer": []string{"https://mockrefererhost/"},
}, nil)
if err != nil {
t.Fatal(err)
}
err = validateResponse(resp, b, tt.wantStatusCode, tt.wantError, tt.wantResponse)
if err != nil {
t.Error(err)
}
})
}
}

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

@ -48,6 +48,7 @@ type frontendDBs interface {
database.DatabaseGroupWithAsyncOperations
database.DatabaseGroupWithSubscriptions
database.DatabaseGroupWithPlatformWorkloadIdentityRoleSets
database.DatabaseGroupWithMaintenanceManifests
}
type kubeActionsFactory func(*logrus.Entry, env.Interface, *api.OpenShiftCluster) (adminactions.KubeActions, error)
@ -326,6 +327,9 @@ func (f *frontend) chiAuthenticatedRoutes(router chi.Router) {
r.With(f.maintenanceMiddleware.UnplannedMaintenanceSignal).Post("/etcdcertificaterenew", f.postAdminOpenShiftClusterEtcdCertificateRenew)
r.With(f.maintenanceMiddleware.UnplannedMaintenanceSignal).Post("/deletemanagedresource", f.postAdminOpenShiftDeleteManagedResource)
// MIMO
r.Get("/maintenancemanifests", f.getAdminMaintManifests)
})
})