[ARO-9169] Remove disused clustermanager frontend code (#3642)

This commit is contained in:
Amber Brown 2024-07-17 09:39:37 +10:00 коммит произвёл GitHub
Родитель 639fd8698e
Коммит 94cd8d7f27
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
9 изменённых файлов: 0 добавлений и 1480 удалений

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

@ -1,67 +0,0 @@
package frontend
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import (
"context"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/sirupsen/logrus"
"github.com/Azure/ARO-RP/pkg/api"
"github.com/Azure/ARO-RP/pkg/database/cosmosdb"
"github.com/Azure/ARO-RP/pkg/frontend/middleware"
)
func (f *frontend) deleteClusterManagerConfiguration(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
log := ctx.Value(middleware.ContextKeyLog).(*logrus.Entry)
var err error
apiVersion, ocmResourceType := r.URL.Query().Get(api.APIVersionKey), chi.URLParam(r, "ocmResourceType")
err = f.validateOcmResourceType(apiVersion, ocmResourceType)
if err != nil {
api.WriteError(w, http.StatusBadRequest, api.CloudErrorCodeInvalidResourceType, "", err.Error())
return
}
err = f._deleteClusterManagerConfigurationDocument(ctx, log, r)
switch {
case cosmosdb.IsErrorStatusCode(err, http.StatusNotFound):
err = statusCodeError(http.StatusNoContent)
case err == nil:
err = statusCodeError(http.StatusOK)
}
reply(log, w, nil, nil, err)
}
func (f *frontend) _deleteClusterManagerConfigurationDocument(ctx context.Context, log *logrus.Entry, r *http.Request) error {
_, err := f.validateSubscriptionState(ctx, r.URL.Path, api.SubscriptionStateRegistered, api.SubscriptionStateSuspended, api.SubscriptionStateWarned)
if err != nil {
return err
}
resourceType, resourceName, ocmResourceType, ocmResourceName, resourceGroupName := chi.URLParam(r, "resourceType"), chi.URLParam(r, "resourceName"), chi.URLParam(r, "ocmResourceType"), chi.URLParam(r, "ocmResourceName"), chi.URLParam(r, "resourceGroupName")
doc, err := f.dbClusterManagerConfiguration.Get(ctx, r.URL.Path)
switch {
case cosmosdb.IsErrorStatusCode(err, http.StatusNotFound):
return api.NewCloudError(http.StatusNotFound, api.CloudErrorCodeResourceNotFound, "", "The Resource '%s/%s/%s/%s' under resource group '%s' was not found.",
resourceType, resourceName, ocmResourceType, ocmResourceName, resourceGroupName)
case err != nil:
return err
}
// Right now we are going to assume that the backend will delete the document, we will just mark for deletion.
doc.Deleting = true
err = cosmosdb.RetryOnPreconditionFailed(func() error {
var err error
_, err = f.dbClusterManagerConfiguration.Update(ctx, doc)
return err
})
return err
}

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

@ -1,146 +0,0 @@
package frontend
import (
"context"
"fmt"
"net/http"
"testing"
"github.com/Azure/ARO-RP/pkg/api"
"github.com/Azure/ARO-RP/pkg/env"
"github.com/Azure/ARO-RP/pkg/metrics/noop"
testdatabase "github.com/Azure/ARO-RP/test/database"
)
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
func TestDeleteClusterManagerConfiguration(t *testing.T) {
ctx := context.Background()
mockSubscriptionId := "00000000-0000-0000-0000-000000000000"
tenantId := "11111111-1111-1111-1111-111111111111"
resourcePayload := "eyAKICAiYXBpVmVyc2lvbiI6ICJoaXZlLm9wZW5zaGlmdC5pby92MSIsCiAgImtpbmQiOiAiU3luY1NldCIsCiAgIm1ldGFkYXRhIjogewogICAgIm5hbWUiOiAic2FtcGxlIiwKICAgICJuYW1lc3BhY2UiOiAiYXJvLWY2MGFlOGEyLWJjYTEtNDk4Ny05MDU2LWYyZjZhMTgzN2NhYSIKICB9LAogICJzcGVjIjogewogICAgImNsdXN0ZXJEZXBsb3ltZW50UmVmcyI6IFtdLAogICAgInJlc291cmNlcyI6IFsKICAgICAgewogICAgICAgICJhcGlWZXJzaW9uIjogInYxIiwKICAgICAgICAia2luZCI6ICJDb25maWdNYXAiLAogICAgICAgICJtZXRhZGF0YSI6IHsKICAgICAgICAgICJuYW1lIjogIm15Y29uZmlnbWFwIgogICAgICAgIH0KICAgICAgfQogICAgXQogIH0KfQo="
type test struct {
name string
ocmResourceType string
ocmResourceName string
clusterName string
apiVersion string
fixture func(*testdatabase.Fixture, *test, string)
wantStatusCode int
wantError string
}
createSingleDocument := func(f *testdatabase.Fixture, tt *test, resourceKey string) {
f.AddSubscriptionDocuments(&api.SubscriptionDocument{
ID: mockSubscriptionId,
Subscription: &api.Subscription{
State: api.SubscriptionStateRegistered,
Properties: &api.SubscriptionProperties{
TenantID: tenantId,
},
},
})
f.AddClusterManagerConfigurationDocuments(
&api.ClusterManagerConfigurationDocument{
ID: mockSubscriptionId,
Key: resourceKey,
SyncSet: &api.SyncSet{
Properties: api.SyncSetProperties{
Resources: resourcePayload,
},
},
},
)
}
for _, tt := range []*test{
{
name: "single syncset",
ocmResourceType: "syncSet",
ocmResourceName: "deleteSyncSet",
clusterName: "myCluster",
apiVersion: "2022-09-04",
fixture: createSingleDocument,
wantStatusCode: http.StatusOK,
},
{
name: "does not exist",
ocmResourceType: "syncSet",
ocmResourceName: "deleteSyncSet",
clusterName: "myCluster",
apiVersion: "2022-09-04",
fixture: func(f *testdatabase.Fixture, tt *test, resourceKey string) {
f.AddSubscriptionDocuments(&api.SubscriptionDocument{
ID: mockSubscriptionId,
Subscription: &api.Subscription{
State: api.SubscriptionStateRegistered,
Properties: &api.SubscriptionProperties{
TenantID: tenantId,
},
},
})
},
wantStatusCode: http.StatusNotFound,
wantError: "404: ResourceNotFound: : The Resource 'openshiftclusters/resourcename/syncset/deletesyncset' under resource group 'resourcegroup' was not found.",
},
{
name: "unsupported api version",
ocmResourceType: "syncSet",
ocmResourceName: "deleteSyncSet",
clusterName: "myCluster",
apiVersion: "2022-04-01",
fixture: createSingleDocument,
wantStatusCode: http.StatusBadRequest,
wantError: "400: InvalidResourceType: : the resource type 'syncset' is not valid for api version '2022-04-01'",
},
{
name: "unsupported resource type",
ocmResourceType: "unsupported",
ocmResourceName: "deleteSyncSet",
clusterName: "myCluster",
apiVersion: "2022-09-04",
fixture: createSingleDocument,
wantStatusCode: http.StatusBadRequest,
wantError: "400: InvalidResourceType: : the resource type 'unsupported' is not valid for api version '2022-09-04'",
},
} {
t.Run(tt.name, func(t *testing.T) {
ti := newTestInfraWithFeatures(t, map[env.Feature]bool{env.FeatureRequireD2sV3Workers: false, env.FeatureDisableReadinessDelay: false, env.FeatureEnableOCMEndpoints: true}).WithClusterManagerConfigurations().WithSubscriptions()
defer ti.done()
resourceKey := fmt.Sprintf("/subscriptions/%s/resourcegroups/resourcegroup/providers/microsoft.redhatopenshift/openshiftclusters/resourcename/%s/%s",
mockSubscriptionId,
tt.ocmResourceType,
tt.ocmResourceName)
err := ti.buildFixtures(func(f *testdatabase.Fixture) { tt.fixture(f, tt, resourceKey) })
if err != nil {
t.Fatal(err)
}
f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, nil, ti.clusterManagerDatabase, nil, ti.subscriptionsDatabase, nil, nil, api.APIs, &noop.Noop{}, &noop.Noop{}, nil, nil, nil, nil, nil, nil)
if err != nil {
t.Fatal(err)
}
go f.Run(ctx, nil, nil)
resp, b, err := ti.request(http.MethodDelete,
fmt.Sprintf("https://server%s?api-version=%s",
resourceKey,
tt.apiVersion,
),
nil, nil)
if err != nil {
t.Fatalf("%s: %s", err, string(b))
}
err = validateResponse(resp, b, tt.wantStatusCode, tt.wantError, "")
if err != nil {
t.Errorf("%s: %s", err, string(b))
}
})
}
}

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

@ -1,115 +0,0 @@
package frontend
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import (
"context"
"encoding/json"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/sirupsen/logrus"
"github.com/Azure/ARO-RP/pkg/api"
"github.com/Azure/ARO-RP/pkg/database/cosmosdb"
"github.com/Azure/ARO-RP/pkg/frontend/middleware"
)
func (f *frontend) getClusterManagerConfiguration(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
log := ctx.Value(middleware.ContextKeyLog).(*logrus.Entry)
ocmResourceType := chi.URLParam(r, "ocmResourceType")
var (
b []byte
err error
)
apiVersion := r.URL.Query().Get(api.APIVersionKey)
err = f.validateOcmResourceType(apiVersion, ocmResourceType)
if err != nil {
api.WriteError(w, http.StatusBadRequest, api.CloudErrorCodeInvalidResourceType, "", err.Error())
return
}
switch ocmResourceType {
case "syncset":
b, err = f._getSyncSetConfiguration(ctx, log, r, f.apis[apiVersion].SyncSetConverter)
case "machinepool":
b, err = f._getMachinePoolConfiguration(ctx, log, r, f.apis[apiVersion].MachinePoolConverter)
case "syncidentityprovider":
b, err = f._getSyncIdentityProviderConfiguration(ctx, log, r, f.apis[apiVersion].SyncIdentityProviderConverter)
case "secret":
b, err = f._getSecretConfiguration(ctx, log, r, f.apis[apiVersion].SecretConverter)
default:
return
}
reply(log, w, nil, b, err)
}
func (f *frontend) _getSyncSetConfiguration(ctx context.Context, log *logrus.Entry, r *http.Request, converter api.SyncSetConverter) ([]byte, error) {
resType, resName, ocmResType, ocmResName, resGroupName := chi.URLParam(r, "resourceType"), chi.URLParam(r, "resourceName"), chi.URLParam(r, "ocmResourceType"), chi.URLParam(r, "ocmResourceName"), chi.URLParam(r, "resourceGroupName")
doc, err := f.validateResourceForGet(ctx, resType, resName, ocmResType, ocmResName, resGroupName, r.URL.Path, r)
if err != nil {
return nil, err
}
ext := converter.ToExternal(doc.SyncSet)
return json.MarshalIndent(ext, "", " ")
}
func (f *frontend) _getMachinePoolConfiguration(ctx context.Context, log *logrus.Entry, r *http.Request, converter api.MachinePoolConverter) ([]byte, error) {
resType, resName, ocmResType, ocmResName, resGroupName := chi.URLParam(r, "resourceType"), chi.URLParam(r, "resourceName"), chi.URLParam(r, "ocmResourceType"), chi.URLParam(r, "ocmResourceName"), chi.URLParam(r, "resourceGroupName")
doc, err := f.validateResourceForGet(ctx, resType, resName, ocmResType, ocmResName, resGroupName, r.URL.Path, r)
if err != nil {
return nil, err
}
ext := converter.ToExternal(doc.MachinePool)
return json.MarshalIndent(ext, "", " ")
}
func (f *frontend) _getSyncIdentityProviderConfiguration(ctx context.Context, log *logrus.Entry, r *http.Request, converter api.SyncIdentityProviderConverter) ([]byte, error) {
resType, resName, ocmResType, ocmResName, resGroupName := chi.URLParam(r, "resourceType"), chi.URLParam(r, "resourceName"), chi.URLParam(r, "ocmResourceType"), chi.URLParam(r, "ocmResourceName"), chi.URLParam(r, "resourceGroupName")
doc, err := f.validateResourceForGet(ctx, resType, resName, ocmResType, ocmResName, resGroupName, r.URL.Path, r)
if err != nil {
return nil, err
}
ext := converter.ToExternal(doc.SyncIdentityProvider)
return json.MarshalIndent(ext, "", " ")
}
func (f *frontend) _getSecretConfiguration(ctx context.Context, log *logrus.Entry, r *http.Request, converter api.SecretConverter) ([]byte, error) {
resType, resName, ocmResType, ocmResName, resGroupName := chi.URLParam(r, "resourceType"), chi.URLParam(r, "resourceName"), chi.URLParam(r, "ocmResourceType"), chi.URLParam(r, "ocmResourceName"), chi.URLParam(r, "resourceGroupName")
doc, err := f.validateResourceForGet(ctx, resType, resName, ocmResType, ocmResName, resGroupName, r.URL.Path, r)
if err != nil {
return nil, err
}
ext := converter.ToExternal(doc.Secret)
return json.MarshalIndent(ext, "", " ")
}
func (f *frontend) validateResourceForGet(ctx context.Context, resType, resName, ocmResType, ocmResName, resGroupName, path string, r *http.Request) (*api.ClusterManagerConfigurationDocument, error) {
doc, err := f.dbClusterManagerConfiguration.Get(ctx, r.URL.Path)
if err != nil {
switch {
case cosmosdb.IsErrorStatusCode(err, http.StatusNotFound):
return nil, api.NewCloudError(http.StatusNotFound, api.CloudErrorCodeResourceNotFound, "", "The Resource '%s/%s/%s/%s' under resource group '%s' was not found.",
resType, resName, ocmResType, ocmResName, resGroupName)
default:
return nil, err
}
}
if doc.Deleting {
return nil, api.NewCloudError(http.StatusBadRequest, api.CloudErrorCodeRequestNotAllowed, "", "Request is not allowed on a resource marked for deletion.")
}
return doc, nil
}

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

@ -1,149 +0,0 @@
package frontend
import (
"context"
"fmt"
"net/http"
"testing"
"github.com/Azure/ARO-RP/pkg/api"
v20220904 "github.com/Azure/ARO-RP/pkg/api/v20220904"
"github.com/Azure/ARO-RP/pkg/env"
"github.com/Azure/ARO-RP/pkg/metrics/noop"
testdatabase "github.com/Azure/ARO-RP/test/database"
)
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
func TestGetClusterManagerConfiguration(t *testing.T) {
ctx := context.Background()
mockSubscriptionId := "00000000-0000-0000-0000-000000000000"
resourcePayload := "eyAKICAiYXBpVmVyc2lvbiI6ICJoaXZlLm9wZW5zaGlmdC5pby92MSIsCiAgImtpbmQiOiAiU3luY1NldCIsCiAgIm1ldGFkYXRhIjogewogICAgIm5hbWUiOiAic2FtcGxlIiwKICAgICJuYW1lc3BhY2UiOiAiYXJvLWY2MGFlOGEyLWJjYTEtNDk4Ny05MDU2LWYyZjZhMTgzN2NhYSIKICB9LAogICJzcGVjIjogewogICAgImNsdXN0ZXJEZXBsb3ltZW50UmVmcyI6IFtdLAogICAgInJlc291cmNlcyI6IFsKICAgICAgewogICAgICAgICJhcGlWZXJzaW9uIjogInYxIiwKICAgICAgICAia2luZCI6ICJDb25maWdNYXAiLAogICAgICAgICJtZXRhZGF0YSI6IHsKICAgICAgICAgICJuYW1lIjogIm15Y29uZmlnbWFwIgogICAgICAgIH0KICAgICAgfQogICAgXQogIH0KfQo="
type test struct {
name string
ocmResourceType string
ocmResourceName string
clusterName string
apiVersion string
fixture func(*testdatabase.Fixture, *test, string)
wantStatusCode int
wantResponse *v20220904.SyncSet
wantError string
}
createSingleDocument := func(f *testdatabase.Fixture, tt *test, resourceKey string) {
f.AddClusterManagerConfigurationDocuments(
&api.ClusterManagerConfigurationDocument{
ID: mockSubscriptionId,
Key: resourceKey,
SyncSet: &api.SyncSet{
Name: tt.ocmResourceName,
Properties: api.SyncSetProperties{
Resources: resourcePayload,
},
},
},
)
}
for _, tt := range []*test{
{
name: "single syncset",
ocmResourceType: "syncSet",
ocmResourceName: "mySyncSet",
clusterName: "myCluster",
apiVersion: "2022-09-04",
fixture: createSingleDocument,
wantStatusCode: http.StatusOK,
wantResponse: &v20220904.SyncSet{
Name: "mySyncSet",
Properties: v20220904.SyncSetProperties{
Resources: resourcePayload,
},
},
},
{
name: "syncset is deleting",
ocmResourceType: "syncSet",
ocmResourceName: "myDeletingSyncSet",
clusterName: "myCluster",
apiVersion: "2022-09-04",
fixture: func(f *testdatabase.Fixture, tt *test, resourceKey string) {
f.AddClusterManagerConfigurationDocuments(
&api.ClusterManagerConfigurationDocument{
ID: mockSubscriptionId,
Key: resourceKey,
Deleting: true,
SyncSet: &api.SyncSet{
Name: tt.ocmResourceName,
Properties: api.SyncSetProperties{
Resources: resourcePayload,
},
},
},
)
},
wantStatusCode: http.StatusBadRequest,
wantError: "400: RequestNotAllowed: : Request is not allowed on a resource marked for deletion.",
},
{
name: "wrong version",
ocmResourceType: "syncset",
ocmResourceName: "mySyncSet",
clusterName: "myCluster",
apiVersion: "2022-04-01",
fixture: createSingleDocument,
wantStatusCode: http.StatusBadRequest,
wantError: "400: InvalidResourceType: : the resource type 'syncset' is not valid for api version '2022-04-01'",
},
{
name: "unsupported resource type",
ocmResourceType: "unsupported",
ocmResourceName: "invalidResourceType",
clusterName: "myCluster",
apiVersion: "2022-09-04",
fixture: createSingleDocument,
wantStatusCode: http.StatusBadRequest,
wantError: "400: InvalidResourceType: : the resource type 'unsupported' is not valid for api version '2022-09-04'",
},
} {
t.Run(tt.name, func(t *testing.T) {
ti := newTestInfraWithFeatures(t, map[env.Feature]bool{env.FeatureRequireD2sV3Workers: false, env.FeatureDisableReadinessDelay: false, env.FeatureEnableOCMEndpoints: true}).WithClusterManagerConfigurations()
defer ti.done()
resourceKey := fmt.Sprintf("/subscriptions/%s/resourcegroups/resourcegroup/providers/microsoft.redhatopenshift/openshiftclusters/resourcename/%s/%s",
mockSubscriptionId,
tt.ocmResourceType,
tt.ocmResourceName)
err := ti.buildFixtures(func(f *testdatabase.Fixture) { tt.fixture(f, tt, resourceKey) })
if err != nil {
t.Fatal(err)
}
f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, nil, ti.clusterManagerDatabase, nil, nil, nil, nil, api.APIs, &noop.Noop{}, &noop.Noop{}, nil, nil, nil, nil, nil, nil)
if err != nil {
t.Fatal(err)
}
go f.Run(ctx, nil, nil)
resp, b, err := ti.request(http.MethodGet,
fmt.Sprintf("https://server%s?api-version=%s",
resourceKey,
tt.apiVersion,
),
nil, nil)
if err != nil {
t.Fatalf("%s: %s", err, string(b))
}
err = validateResponse(resp, b, tt.wantStatusCode, tt.wantError, tt.wantResponse)
if err != nil {
t.Errorf("%s: %s", err, string(b))
}
})
}
}

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

@ -1,480 +0,0 @@
package frontend
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import (
"context"
"encoding/json"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/sirupsen/logrus"
"github.com/Azure/ARO-RP/pkg/api"
"github.com/Azure/ARO-RP/pkg/database/cosmosdb"
"github.com/Azure/ARO-RP/pkg/frontend/middleware"
"github.com/Azure/ARO-RP/pkg/util/arm"
)
func (f *frontend) putOrPatchClusterManagerConfiguration(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
log := ctx.Value(middleware.ContextKeyLog).(*logrus.Entry)
var (
header http.Header
b []byte
err error
)
apiVersion, ocmResourceType := r.URL.Query().Get(api.APIVersionKey), chi.URLParam(r, "ocmResourceType")
err = f.validateOcmResourceType(apiVersion, ocmResourceType)
if err != nil {
api.WriteError(w, http.StatusBadRequest, api.CloudErrorCodeInvalidResourceType, "", err.Error())
return
}
err = cosmosdb.RetryOnPreconditionFailed(func() error {
var err error
switch ocmResourceType {
case "syncset":
b, err = f._putOrPatchSyncSet(ctx, log, r, &header, f.apis[apiVersion].SyncSetConverter, f.apis[apiVersion].ClusterManagerStaticValidator)
case "machinepool":
b, err = f._putOrPatchMachinePool(ctx, log, r, &header, f.apis[apiVersion].MachinePoolConverter, f.apis[apiVersion].ClusterManagerStaticValidator)
case "syncidentityprovider":
b, err = f._putOrPatchSyncIdentityProvider(ctx, log, r, &header, f.apis[apiVersion].SyncIdentityProviderConverter, f.apis[apiVersion].ClusterManagerStaticValidator)
case "secret":
b, err = f._putOrPatchSecret(ctx, log, r, &header, f.apis[apiVersion].SecretConverter, f.apis[apiVersion].ClusterManagerStaticValidator)
}
return err
})
reply(log, w, header, b, err)
}
func (f *frontend) _putOrPatchSyncSet(ctx context.Context, log *logrus.Entry, r *http.Request, header *http.Header, converter api.SyncSetConverter, staticValidator api.ClusterManagerStaticValidator) ([]byte, error) {
body := r.Context().Value(middleware.ContextKeyBody).([]byte)
correlationData := r.Context().Value(middleware.ContextKeyCorrelationData).(*api.CorrelationData)
systemData, _ := r.Context().Value(middleware.ContextKeySystemData).(*api.SystemData) // don't panic
resType, resName, resGroupName := chi.URLParam(r, "resourceType"), chi.URLParam(r, "resourceName"), chi.URLParam(r, "resourceGroupName")
ocmResourceType, ocmResourceName := chi.URLParam(r, "ocmResourceType"), chi.URLParam(r, "ocmResourceName")
originalPath, err := f.extractOriginalPath(ctx, r, resType, resName, resGroupName)
if err != nil {
return nil, err
}
ocmdoc, err := f.dbClusterManagerConfiguration.Get(ctx, r.URL.Path)
if err != nil && !cosmosdb.IsErrorStatusCode(err, http.StatusNotFound) {
return nil, err
} else if cosmosdb.IsErrorStatusCode(err, http.StatusNotFound) && r.Method == http.MethodPatch {
return nil, api.NewCloudError(http.StatusNotFound, api.CloudErrorCodeResourceNotFound, "", "The Resource '%s/%s/%s/%s' under resource group '%s' was not found.",
resType, resName, ocmResourceType, ocmResourceName, resGroupName)
}
var resources string
err = json.Unmarshal(body, &resources)
if err != nil {
return nil, api.NewCloudError(http.StatusBadRequest, api.CloudErrorCodeInvalidRequestContent, "", "The request content was invalid and could not be deserialized: %q.", err)
}
err = staticValidator.Static(resources, ocmResourceType)
if err != nil {
return nil, api.NewCloudError(http.StatusBadRequest, api.CloudErrorCodeInvalidRequestContent, "", "The 'Kind' in the request payload does not match 'Kind' in the request path: %q.", err)
}
isCreate := ocmdoc == nil
uuid := f.dbClusterManagerConfiguration.NewUUID()
if isCreate {
ocmdoc = &api.ClusterManagerConfigurationDocument{
ID: uuid,
Key: r.URL.Path,
}
ocmdoc.SyncSet = &api.SyncSet{
Name: ocmResourceName,
Type: "Microsoft.RedHatOpenShift/SyncSet",
ID: originalPath,
Properties: api.SyncSetProperties{
Resources: resources,
},
}
var newdoc *api.ClusterManagerConfigurationDocument
err = cosmosdb.RetryOnPreconditionFailed(func() error {
newdoc, err = f.dbClusterManagerConfiguration.Create(ctx, ocmdoc)
return err
})
ocmdoc = newdoc
} else {
if ocmdoc.Deleting {
return nil, api.NewCloudError(http.StatusBadRequest, api.CloudErrorCodeRequestNotAllowed, "", "Request is not allowed on a resource marked for deletion.")
}
ocmdoc.SyncSet.Properties.Resources = resources
}
ocmdoc.CorrelationData = correlationData
f.systemDataSyncSetEnricher(ocmdoc, systemData)
ocmdoc, err = f.dbClusterManagerConfiguration.Update(ctx, ocmdoc)
if err != nil {
return nil, err
}
ext := converter.ToExternal(ocmdoc.SyncSet)
b, err := json.MarshalIndent(ext, "", " ")
return b, err
}
func (f *frontend) _putOrPatchMachinePool(ctx context.Context, log *logrus.Entry, r *http.Request, header *http.Header, converter api.MachinePoolConverter, staticValidator api.ClusterManagerStaticValidator) ([]byte, error) {
body := r.Context().Value(middleware.ContextKeyBody).([]byte)
correlationData := r.Context().Value(middleware.ContextKeyCorrelationData).(*api.CorrelationData)
systemData, _ := r.Context().Value(middleware.ContextKeySystemData).(*api.SystemData) // don't panic
resType, resName, resGroupName := chi.URLParam(r, "resourceType"), chi.URLParam(r, "resourceName"), chi.URLParam(r, "resourceGroupName")
ocmResourceType, ocmResourceName := chi.URLParam(r, "ocmResourceType"), chi.URLParam(r, "ocmResourceName")
originalPath, err := f.extractOriginalPath(ctx, r, resType, resName, resGroupName)
if err != nil {
return nil, err
}
ocmdoc, err := f.dbClusterManagerConfiguration.Get(ctx, r.URL.Path)
if err != nil && !cosmosdb.IsErrorStatusCode(err, http.StatusNotFound) {
return nil, err
} else if cosmosdb.IsErrorStatusCode(err, http.StatusNotFound) && r.Method == http.MethodPatch {
return nil, api.NewCloudError(http.StatusNotFound, api.CloudErrorCodeResourceNotFound, "", "The Resource '%s/%s/%s/%s' under resource group '%s' was not found.",
resType, resName, ocmResourceType, ocmResourceName, resGroupName)
}
var resources string
err = json.Unmarshal(body, &resources)
if err != nil {
return nil, api.NewCloudError(http.StatusBadRequest, api.CloudErrorCodeInvalidRequestContent, "", "The request content was invalid and could not be deserialized: %q.", err)
}
err = staticValidator.Static(resources, ocmResourceType)
if err != nil {
return nil, api.NewCloudError(http.StatusBadRequest, api.CloudErrorCodeInvalidRequestContent, "", "The 'Kind' in the request payload does not match 'Kind' in the request path: %q.", err)
}
isCreate := ocmdoc == nil
uuid := f.dbClusterManagerConfiguration.NewUUID()
if isCreate {
ocmdoc = &api.ClusterManagerConfigurationDocument{
ID: uuid,
Key: r.URL.Path,
}
ocmdoc.MachinePool = &api.MachinePool{
Name: ocmResourceName,
Type: "Microsoft.RedHatOpenShift/MachinePool",
ID: originalPath,
Properties: api.MachinePoolProperties{
Resources: resources,
},
}
var newdoc *api.ClusterManagerConfigurationDocument
err = cosmosdb.RetryOnPreconditionFailed(func() error {
newdoc, err = f.dbClusterManagerConfiguration.Create(ctx, ocmdoc)
return err
})
ocmdoc = newdoc
} else {
if ocmdoc.Deleting {
return nil, api.NewCloudError(http.StatusBadRequest, api.CloudErrorCodeRequestNotAllowed, "", "Request is not allowed on a resource marked for deletion.")
}
ocmdoc.MachinePool.Properties.Resources = resources
}
ocmdoc.CorrelationData = correlationData
f.systemDataMachinePoolEnricher(ocmdoc, systemData)
ocmdoc, err = f.dbClusterManagerConfiguration.Update(ctx, ocmdoc)
if err != nil {
return nil, err
}
ext := converter.ToExternal(ocmdoc.MachinePool)
b, err := json.MarshalIndent(ext, "", " ")
return b, err
}
func (f *frontend) _putOrPatchSyncIdentityProvider(ctx context.Context, log *logrus.Entry, r *http.Request, header *http.Header, converter api.SyncIdentityProviderConverter, staticValidator api.ClusterManagerStaticValidator) ([]byte, error) {
body := r.Context().Value(middleware.ContextKeyBody).([]byte)
correlationData := r.Context().Value(middleware.ContextKeyCorrelationData).(*api.CorrelationData)
systemData, _ := r.Context().Value(middleware.ContextKeySystemData).(*api.SystemData) // don't panic
resType, resName, resGroupName := chi.URLParam(r, "resourceType"), chi.URLParam(r, "resourceName"), chi.URLParam(r, "resourceGroupName")
ocmResourceType, ocmResourceName := chi.URLParam(r, "ocmResourceType"), chi.URLParam(r, "ocmResourceName")
originalPath, err := f.extractOriginalPath(ctx, r, resType, resName, resGroupName)
if err != nil {
return nil, err
}
ocmdoc, err := f.dbClusterManagerConfiguration.Get(ctx, r.URL.Path)
if err != nil && !cosmosdb.IsErrorStatusCode(err, http.StatusNotFound) {
return nil, err
} else if cosmosdb.IsErrorStatusCode(err, http.StatusNotFound) && r.Method == http.MethodPatch {
return nil, api.NewCloudError(http.StatusNotFound, api.CloudErrorCodeResourceNotFound, "", "The Resource '%s/%s/%s/%s' under resource group '%s' was not found.",
resType, resName, ocmResourceType, ocmResourceName, resGroupName)
}
var resources string
err = json.Unmarshal(body, &resources)
if err != nil {
return nil, api.NewCloudError(http.StatusBadRequest, api.CloudErrorCodeInvalidRequestContent, "", "The request content was invalid and could not be deserialized: %q.", err)
}
err = staticValidator.Static(resources, ocmResourceType)
if err != nil {
return nil, api.NewCloudError(http.StatusBadRequest, api.CloudErrorCodeInvalidRequestContent, "", "The 'Kind' in the request payload does not match 'Kind' in the request path: %q.", err)
}
isCreate := ocmdoc == nil
uuid := f.dbClusterManagerConfiguration.NewUUID()
if isCreate {
ocmdoc = &api.ClusterManagerConfigurationDocument{
ID: uuid,
Key: r.URL.Path,
}
ocmdoc.SyncIdentityProvider = &api.SyncIdentityProvider{
Name: ocmResourceName,
Type: "Microsoft.RedHatOpenShift/SyncIdentityProvider",
ID: originalPath,
Properties: api.SyncIdentityProviderProperties{
Resources: resources,
},
}
var newdoc *api.ClusterManagerConfigurationDocument
err = cosmosdb.RetryOnPreconditionFailed(func() error {
newdoc, err = f.dbClusterManagerConfiguration.Create(ctx, ocmdoc)
return err
})
ocmdoc = newdoc
} else {
if ocmdoc.Deleting {
return nil, api.NewCloudError(http.StatusBadRequest, api.CloudErrorCodeRequestNotAllowed, "", "Request is not allowed on a resource marked for deletion.")
}
ocmdoc.SyncIdentityProvider.Properties.Resources = resources
}
ocmdoc.CorrelationData = correlationData
f.systemDataSyncIdentityProviderEnricher(ocmdoc, systemData)
ocmdoc, err = f.dbClusterManagerConfiguration.Update(ctx, ocmdoc)
if err != nil {
return nil, err
}
ext := converter.ToExternal(ocmdoc.SyncIdentityProvider)
b, err := json.MarshalIndent(ext, "", " ")
return b, err
}
func (f *frontend) _putOrPatchSecret(ctx context.Context, log *logrus.Entry, r *http.Request, header *http.Header, converter api.SecretConverter, staticValidator api.ClusterManagerStaticValidator) ([]byte, error) {
body := r.Context().Value(middleware.ContextKeyBody).([]byte)
correlationData := r.Context().Value(middleware.ContextKeyCorrelationData).(*api.CorrelationData)
systemData, _ := r.Context().Value(middleware.ContextKeySystemData).(*api.SystemData) // don't panic
resType, resName, resGroupName := chi.URLParam(r, "resourceType"), chi.URLParam(r, "resourceName"), chi.URLParam(r, "resourceGroupName")
ocmResourceType, ocmResourceName := chi.URLParam(r, "ocmResourceType"), chi.URLParam(r, "ocmResourceName")
originalPath, err := f.extractOriginalPath(ctx, r, resType, resName, resGroupName)
if err != nil {
return nil, err
}
ocmdoc, err := f.dbClusterManagerConfiguration.Get(ctx, r.URL.Path)
if err != nil && !cosmosdb.IsErrorStatusCode(err, http.StatusNotFound) {
return nil, err
} else if cosmosdb.IsErrorStatusCode(err, http.StatusNotFound) && r.Method == http.MethodPatch {
return nil, api.NewCloudError(http.StatusNotFound, api.CloudErrorCodeResourceNotFound, "", "The Resource '%s/%s/%s/%s' under resource group '%s' was not found.",
resType, resName, ocmResourceType, ocmResourceName, resGroupName)
}
var resources string
err = json.Unmarshal(body, &resources)
if err != nil {
return nil, api.NewCloudError(http.StatusBadRequest, api.CloudErrorCodeInvalidRequestContent, "", "The request content was invalid and could not be deserialized: %q.", err)
}
err = staticValidator.Static(resources, ocmResourceType)
if err != nil {
return nil, api.NewCloudError(http.StatusBadRequest, api.CloudErrorCodeInvalidRequestContent, "", "The 'Kind' in the request payload does not match 'Kind' in the request path: %q.", err)
}
isCreate := ocmdoc == nil
uuid := f.dbClusterManagerConfiguration.NewUUID()
if isCreate {
ocmdoc = &api.ClusterManagerConfigurationDocument{
ID: uuid,
Key: r.URL.Path,
}
ocmdoc.Secret = &api.Secret{
Name: ocmResourceName,
Type: "Microsoft.RedHatOpenShift/Secret",
ID: originalPath,
Properties: api.SecretProperties{
SecretResources: api.SecureString(resources),
},
}
var newdoc *api.ClusterManagerConfigurationDocument
err = cosmosdb.RetryOnPreconditionFailed(func() error {
newdoc, err = f.dbClusterManagerConfiguration.Create(ctx, ocmdoc)
return err
})
ocmdoc = newdoc
} else {
if ocmdoc.Deleting {
return nil, api.NewCloudError(http.StatusBadRequest, api.CloudErrorCodeRequestNotAllowed, "", "Request is not allowed on a resource marked for deletion.")
}
ocmdoc.Secret.Properties.SecretResources = api.SecureString(resources)
}
ocmdoc.CorrelationData = correlationData
f.systemDataSecretEnricher(ocmdoc, systemData)
ocmdoc, err = f.dbClusterManagerConfiguration.Update(ctx, ocmdoc)
if err != nil {
return nil, err
}
ext := converter.ToExternal(ocmdoc.Secret)
b, err := json.MarshalIndent(ext, "", " ")
return b, err
}
func (f *frontend) extractOriginalPath(ctx context.Context, r *http.Request, resType, resName, resGroupName string) (string, error) {
_, err := f.validateSubscriptionState(ctx, r.URL.Path, api.SubscriptionStateRegistered)
if err != nil {
return "", err
}
originalPath := r.Context().Value(middleware.ContextKeyOriginalPath).(string)
armResource, err := arm.ParseArmResourceId(originalPath)
if err != nil {
return "", err
}
ocp, err := f.dbOpenShiftClusters.Get(ctx, armResource.ParentResource())
if err != nil && !cosmosdb.IsErrorStatusCode(err, http.StatusNotFound) {
return "", err
}
if ocp == nil || cosmosdb.IsErrorStatusCode(err, http.StatusNotFound) {
return "", api.NewCloudError(http.StatusNotFound, api.CloudErrorCodeResourceNotFound, "", "The Resource '%s/%s' under resource group '%s' was not found.", resType, resName, resGroupName)
}
return originalPath, err
}
// TODO once we hit go1.18 we can refactor to use generics for any document using systemData
// enrichClusterManagerSystemData will selectively overwrite systemData fields based on
// arm inputs
func enrichSyncSetSystemData(doc *api.ClusterManagerConfigurationDocument, systemData *api.SystemData) {
if systemData == nil {
return
}
if doc.SyncSet.SystemData == nil {
doc.SyncSet.SystemData = &api.SystemData{}
}
if systemData.CreatedAt != nil {
doc.SyncSet.SystemData.CreatedAt = systemData.CreatedAt
}
if systemData.CreatedBy != "" {
doc.SyncSet.SystemData.CreatedBy = systemData.CreatedBy
}
if systemData.CreatedByType != "" {
doc.SyncSet.SystemData.CreatedByType = systemData.CreatedByType
}
if systemData.LastModifiedAt != nil {
doc.SyncSet.SystemData.LastModifiedAt = systemData.LastModifiedAt
}
if systemData.LastModifiedBy != "" {
doc.SyncSet.SystemData.LastModifiedBy = systemData.LastModifiedBy
}
if systemData.LastModifiedByType != "" {
doc.SyncSet.SystemData.LastModifiedByType = systemData.LastModifiedByType
}
}
func enrichMachinePoolSystemData(doc *api.ClusterManagerConfigurationDocument, systemData *api.SystemData) {
if systemData == nil {
return
}
if doc.MachinePool.SystemData == nil {
doc.MachinePool.SystemData = &api.SystemData{}
}
if systemData.CreatedAt != nil {
doc.MachinePool.SystemData.CreatedAt = systemData.CreatedAt
}
if systemData.CreatedBy != "" {
doc.MachinePool.SystemData.CreatedBy = systemData.CreatedBy
}
if systemData.CreatedByType != "" {
doc.MachinePool.SystemData.CreatedByType = systemData.CreatedByType
}
if systemData.LastModifiedAt != nil {
doc.MachinePool.SystemData.LastModifiedAt = systemData.LastModifiedAt
}
if systemData.LastModifiedBy != "" {
doc.MachinePool.SystemData.LastModifiedBy = systemData.LastModifiedBy
}
if systemData.LastModifiedByType != "" {
doc.MachinePool.SystemData.LastModifiedByType = systemData.LastModifiedByType
}
}
func enrichSyncIdentityProviderSystemData(doc *api.ClusterManagerConfigurationDocument, systemData *api.SystemData) {
if systemData == nil {
return
}
if doc.SyncIdentityProvider.SystemData == nil {
doc.SyncIdentityProvider.SystemData = &api.SystemData{}
}
if systemData.CreatedAt != nil {
doc.SyncIdentityProvider.SystemData.CreatedAt = systemData.CreatedAt
}
if systemData.CreatedBy != "" {
doc.SyncIdentityProvider.SystemData.CreatedBy = systemData.CreatedBy
}
if systemData.CreatedByType != "" {
doc.SyncIdentityProvider.SystemData.CreatedByType = systemData.CreatedByType
}
if systemData.LastModifiedAt != nil {
doc.SyncIdentityProvider.SystemData.LastModifiedAt = systemData.LastModifiedAt
}
if systemData.LastModifiedBy != "" {
doc.SyncIdentityProvider.SystemData.LastModifiedBy = systemData.LastModifiedBy
}
if systemData.LastModifiedByType != "" {
doc.SyncIdentityProvider.SystemData.LastModifiedByType = systemData.LastModifiedByType
}
}
func enrichSecretSystemData(doc *api.ClusterManagerConfigurationDocument, systemData *api.SystemData) {
if systemData == nil {
return
}
if doc.Secret.SystemData == nil {
doc.Secret.SystemData = &api.SystemData{}
}
if systemData.CreatedAt != nil {
doc.Secret.SystemData.CreatedAt = systemData.CreatedAt
}
if systemData.CreatedBy != "" {
doc.Secret.SystemData.CreatedBy = systemData.CreatedBy
}
if systemData.CreatedByType != "" {
doc.Secret.SystemData.CreatedByType = systemData.CreatedByType
}
if systemData.LastModifiedAt != nil {
doc.Secret.SystemData.LastModifiedAt = systemData.LastModifiedAt
}
if systemData.LastModifiedBy != "" {
doc.Secret.SystemData.LastModifiedBy = systemData.LastModifiedBy
}
if systemData.LastModifiedByType != "" {
doc.Secret.SystemData.LastModifiedByType = systemData.LastModifiedByType
}
}

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

@ -1,308 +0,0 @@
package frontend
import (
"context"
"fmt"
"net/http"
"strings"
"testing"
"time"
"github.com/Azure/ARO-RP/pkg/api"
v20220904 "github.com/Azure/ARO-RP/pkg/api/v20220904"
"github.com/Azure/ARO-RP/pkg/env"
"github.com/Azure/ARO-RP/pkg/metrics/noop"
testdatabase "github.com/Azure/ARO-RP/test/database"
)
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
func TestPutOrPatchClusterManagerConfiguration(t *testing.T) {
ctx := context.Background()
mockSubscriptionId := "00000000-0000-0000-0000-000000000000"
tenantId := "11111111-1111-1111-1111-111111111111"
resourcePayload := "eyAKICAiYXBpVmVyc2lvbiI6ICJoaXZlLm9wZW5zaGlmdC5pby92MSIsCiAgImtpbmQiOiAiU3luY1NldCIsCiAgIm1ldGFkYXRhIjogewogICAgIm5hbWUiOiAic2FtcGxlIiwKICAgICJuYW1lc3BhY2UiOiAiYXJvLWY2MGFlOGEyLWJjYTEtNDk4Ny05MDU2LWYyZjZhMTgzN2NhYSIKICB9LAogICJzcGVjIjogewogICAgImNsdXN0ZXJEZXBsb3ltZW50UmVmcyI6IFtdLAogICAgInJlc291cmNlcyI6IFsKICAgICAgewogICAgICAgICJhcGlWZXJzaW9uIjogInYxIiwKICAgICAgICAia2luZCI6ICJDb25maWdNYXAiLAogICAgICAgICJtZXRhZGF0YSI6IHsKICAgICAgICAgICJuYW1lIjogIm15Y29uZmlnbWFwIgogICAgICAgIH0KICAgICAgfQogICAgXQogIH0KfQo="
modifiedPayload := "eyAKICAiYXBpVmVyc2lvbiI6ICJoaXZlLm9wZW5zaGlmdC5pby92MSIsCiAgImtpbmQiOiAiU3luY1NldCIsCiAgIm1ldGFkYXRhIjogewogICAgIm5hbWUiOiAibW9kaWZpZWQtc2FtcGxlIiwKICAgICJuYW1lc3BhY2UiOiAiYXJvLWY2MGFlOGEyLWJjYTEtNDk4Ny05MDU2LWYyZjZhMTgzN2NhYSIKICB9LAogICJzcGVjIjogewogICAgImNsdXN0ZXJEZXBsb3ltZW50UmVmcyI6IFtdLAogICAgInJlc291cmNlcyI6IFsKICAgICAgewogICAgICAgICJhcGlWZXJzaW9uIjogInYxIiwKICAgICAgICAia2luZCI6ICJDb25maWdNYXAiLAogICAgICAgICJtZXRhZGF0YSI6IHsKICAgICAgICAgICJuYW1lIjogIm15Y29uZmlnbWFwIgogICAgICAgIH0KICAgICAgfQogICAgXQogIH0KfQo="
type test struct {
name string
ocmResourceType string
ocmResourceName string
clusterName string
apiVersion string
fixture func(*testdatabase.Fixture, *test, string)
requestMethod string
requestBody string
wantStatusCode int
wantResponse *v20220904.SyncSet
wantError string
}
createSingleDocument := func(f *testdatabase.Fixture, tt *test, resourceKey string) {
f.AddSubscriptionDocuments(&api.SubscriptionDocument{
ID: mockSubscriptionId,
Subscription: &api.Subscription{
State: api.SubscriptionStateRegistered,
Properties: &api.SubscriptionProperties{
TenantID: tenantId,
},
},
})
f.AddOpenShiftClusterDocuments(&api.OpenShiftClusterDocument{
Key: strings.ToLower(testdatabase.GetResourcePath(mockSubscriptionId, "resourceName")),
OpenShiftCluster: &api.OpenShiftCluster{
ID: testdatabase.GetResourcePath(mockSubscriptionId, "resourceName"),
Properties: api.OpenShiftClusterProperties{
ClusterProfile: api.ClusterProfile{
ResourceGroupID: fmt.Sprintf("/subscriptions/%s/resourcegroups/%s", mockSubscriptionId, "rg01"),
},
},
},
})
f.AddClusterManagerConfigurationDocuments(
&api.ClusterManagerConfigurationDocument{
ID: mockSubscriptionId,
Key: resourceKey,
SyncSet: &api.SyncSet{
Name: tt.ocmResourceName,
Properties: api.SyncSetProperties{
Resources: resourcePayload,
},
},
},
)
}
noDocuments := func(f *testdatabase.Fixture, tt *test, resourceKey string) {
f.AddSubscriptionDocuments(&api.SubscriptionDocument{
ID: mockSubscriptionId,
Subscription: &api.Subscription{
State: api.SubscriptionStateRegistered,
Properties: &api.SubscriptionProperties{
TenantID: tenantId,
},
},
})
f.AddOpenShiftClusterDocuments(&api.OpenShiftClusterDocument{
Key: strings.ToLower(testdatabase.GetResourcePath(mockSubscriptionId, "resourceName")),
OpenShiftCluster: &api.OpenShiftCluster{
ID: testdatabase.GetResourcePath(mockSubscriptionId, "resourceName"),
Properties: api.OpenShiftClusterProperties{
ClusterProfile: api.ClusterProfile{
ResourceGroupID: fmt.Sprintf("/subscriptions/%s/resourcegroups/%s", mockSubscriptionId, tt.clusterName),
},
},
},
})
}
for _, tt := range []*test{
{
name: "single syncset - put",
ocmResourceType: "syncSet",
ocmResourceName: "putSyncSet",
clusterName: "myCluster",
apiVersion: "2022-09-04",
fixture: createSingleDocument,
requestMethod: http.MethodPut,
requestBody: modifiedPayload,
wantStatusCode: http.StatusOK,
wantResponse: &v20220904.SyncSet{
Name: "putSyncSet",
Properties: v20220904.SyncSetProperties{
Resources: modifiedPayload,
},
},
},
{
name: "single syncset - patch",
ocmResourceType: "syncSet",
ocmResourceName: "patchSyncSet",
clusterName: "myCluster",
apiVersion: "2022-09-04",
fixture: createSingleDocument,
requestMethod: http.MethodPatch,
requestBody: modifiedPayload,
wantStatusCode: http.StatusOK,
wantResponse: &v20220904.SyncSet{
Name: "patchSyncSet",
Properties: v20220904.SyncSetProperties{
Resources: modifiedPayload,
},
},
},
{
name: "single syncset - put create",
ocmResourceType: "syncSet",
ocmResourceName: "putNewSyncSet",
clusterName: "myCluster",
apiVersion: "2022-09-04",
fixture: noDocuments,
requestMethod: http.MethodPut,
requestBody: modifiedPayload,
wantStatusCode: http.StatusOK,
wantResponse: &v20220904.SyncSet{
Name: "putnewsyncset",
Type: "Microsoft.RedHatOpenShift/SyncSet",
ID: "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourcegroup/providers/microsoft.redhatopenshift/openshiftclusters/resourcename/syncSet/putNewSyncSet",
Properties: v20220904.SyncSetProperties{
Resources: modifiedPayload,
},
},
},
{
name: "patching nonexistent syncset",
ocmResourceType: "syncSet",
ocmResourceName: "patchNewSyncSet",
clusterName: "myCluster",
apiVersion: "2022-09-04",
fixture: noDocuments,
requestMethod: http.MethodPatch,
requestBody: modifiedPayload,
wantStatusCode: http.StatusNotFound,
wantError: "404: ResourceNotFound: : The Resource 'openshiftclusters/resourcename/syncset/patchnewsyncset' under resource group 'resourcegroup' was not found.",
},
{
name: "unsupported api version",
ocmResourceType: "syncset",
ocmResourceName: "patchSyncSet",
clusterName: "myCluster",
apiVersion: "2022-04-01",
fixture: createSingleDocument,
requestMethod: http.MethodPatch,
requestBody: modifiedPayload,
wantStatusCode: http.StatusBadRequest,
wantError: "400: InvalidResourceType: : the resource type 'syncset' is not valid for api version '2022-04-01'",
},
{
name: "unsupported resource type",
ocmResourceType: "unsupported",
ocmResourceName: "patchSyncSet",
clusterName: "myCluster",
apiVersion: "2022-09-04",
fixture: createSingleDocument,
requestMethod: http.MethodPatch,
requestBody: modifiedPayload,
wantStatusCode: http.StatusBadRequest,
wantError: "400: InvalidResourceType: : the resource type 'unsupported' is not valid for api version '2022-09-04'",
},
} {
t.Run(tt.name, func(t *testing.T) {
ti := newTestInfraWithFeatures(t, map[env.Feature]bool{env.FeatureRequireD2sV3Workers: false, env.FeatureDisableReadinessDelay: false, env.FeatureEnableOCMEndpoints: true}).WithClusterManagerConfigurations().WithSubscriptions().WithOpenShiftClusters()
defer ti.done()
resourceKey := fmt.Sprintf("/subscriptions/%s/resourcegroups/resourcegroup/providers/microsoft.redhatopenshift/openshiftclusters/resourcename/%s/%s",
mockSubscriptionId,
tt.ocmResourceType,
tt.ocmResourceName)
err := ti.buildFixtures(func(f *testdatabase.Fixture) { tt.fixture(f, tt, resourceKey) })
if err != nil {
t.Fatal(err)
}
f, err := NewFrontend(ctx, ti.audit, ti.log, ti.env, nil, ti.clusterManagerDatabase, ti.openShiftClustersDatabase, ti.subscriptionsDatabase, nil, nil, api.APIs, &noop.Noop{}, &noop.Noop{}, nil, nil, nil, nil, nil, nil)
if err != nil {
t.Fatal(err)
}
go f.Run(ctx, nil, nil)
resp, b, err := ti.request(tt.requestMethod,
fmt.Sprintf("https://server%s?api-version=%s",
resourceKey,
tt.apiVersion,
),
http.Header{
"Content-Type": []string{"application/json"},
}, tt.requestBody)
if err != nil {
t.Fatalf("%s: %s", err, string(b))
}
err = validateResponse(resp, b, tt.wantStatusCode, tt.wantError, tt.wantResponse)
if err != nil {
t.Errorf("%s: %s", err, string(b))
}
})
}
}
var populatedSystemData = api.SystemData{
LastModifiedAt: &time.Time{},
LastModifiedBy: "test-user",
LastModifiedByType: api.CreatedByTypeUser,
CreatedAt: &time.Time{},
CreatedBy: "test-user",
CreatedByType: api.CreatedByTypeUser,
}
func TestEnrichSyncSetSystemData(t *testing.T) {
doc := api.ClusterManagerConfigurationDocument{
SyncSet: &api.SyncSet{},
}
enrichSyncSetSystemData(&doc, &populatedSystemData)
if err := compareSystemData(*doc.SyncSet.SystemData, populatedSystemData); err != nil {
t.Fatal(err)
}
}
func TestEnrichSyncIdentityProviderSystemData(t *testing.T) {
doc := api.ClusterManagerConfigurationDocument{
SyncIdentityProvider: &api.SyncIdentityProvider{},
}
enrichSyncIdentityProviderSystemData(&doc, &populatedSystemData)
if err := compareSystemData(*doc.SyncIdentityProvider.SystemData, populatedSystemData); err != nil {
t.Fatal(err)
}
}
func TestEnrichMachinePoolSystemData(t *testing.T) {
doc := api.ClusterManagerConfigurationDocument{
MachinePool: &api.MachinePool{},
}
enrichMachinePoolSystemData(&doc, &populatedSystemData)
if err := compareSystemData(*doc.MachinePool.SystemData, populatedSystemData); err != nil {
t.Fatal(err)
}
}
func TestEnrichSecretSystemData(t *testing.T) {
doc := api.ClusterManagerConfigurationDocument{
Secret: &api.Secret{},
}
enrichSecretSystemData(&doc, &populatedSystemData)
if err := compareSystemData(*doc.Secret.SystemData, populatedSystemData); err != nil {
t.Fatal(err)
}
}
func compareSystemData(docSystemData, expectedSystemData api.SystemData) error {
if docSystemData.CreatedAt == nil || docSystemData.CreatedAt != expectedSystemData.CreatedAt {
return fmt.Errorf("CreatedAt was %q expected %q", docSystemData.CreatedAt, expectedSystemData.CreatedAt)
}
if docSystemData.CreatedBy == "" || docSystemData.CreatedBy != expectedSystemData.CreatedBy {
return fmt.Errorf("CreatedBy was %q expected %q", docSystemData.CreatedBy, expectedSystemData.CreatedBy)
}
if docSystemData.CreatedByType == "" || docSystemData.CreatedByType != expectedSystemData.CreatedByType {
return fmt.Errorf("CreatedByType was %q expected %q", docSystemData.CreatedByType, expectedSystemData.CreatedByType)
}
if docSystemData.LastModifiedAt == nil || docSystemData.LastModifiedAt != expectedSystemData.LastModifiedAt {
return fmt.Errorf("LastModifiedAt was %q expected %q", docSystemData.LastModifiedAt, expectedSystemData.LastModifiedAt)
}
if docSystemData.LastModifiedBy == "" || docSystemData.LastModifiedBy != expectedSystemData.LastModifiedBy {
return fmt.Errorf("LastModifiedBy was %q expected %q", docSystemData.LastModifiedBy, expectedSystemData.LastModifiedBy)
}
if docSystemData.LastModifiedByType == "" || docSystemData.LastModifiedByType != expectedSystemData.LastModifiedByType {
return fmt.Errorf("LastModifiedByType was %q expected %q", docSystemData.LastModifiedByType, expectedSystemData.LastModifiedByType)
}
return nil
}

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

@ -1,35 +0,0 @@
package frontend
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import (
"fmt"
)
func (f *frontend) validateOcmResourceType(apiVersion, ocmResourceType string) error {
badRequestError := fmt.Errorf("the resource type '%s' is not valid for api version '%s'", ocmResourceType, apiVersion)
switch ocmResourceType {
case "syncset":
if f.apis[apiVersion].SyncSetConverter == nil {
return badRequestError
}
case "machinepool":
if f.apis[apiVersion].MachinePoolConverter == nil {
return badRequestError
}
case "syncidentityprovider":
if f.apis[apiVersion].SyncIdentityProviderConverter == nil {
return badRequestError
}
case "secret":
if f.apis[apiVersion].SecretConverter == nil {
return badRequestError
}
default:
return badRequestError
}
return nil
}

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

@ -1,158 +0,0 @@
package frontend
// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.
import (
"testing"
"github.com/golang/mock/gomock"
"github.com/Azure/ARO-RP/pkg/api"
mock_api "github.com/Azure/ARO-RP/pkg/util/mocks/api"
)
func TestValidateOcmResourceType(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
for _, tt := range []struct {
name string
vars map[string]string
ocmResourceType string
syncSetConverter api.SyncSetConverter
machinePoolConverter api.MachinePoolConverter
syncIdentityProviderConverter api.SyncIdentityProviderConverter
secretConverter api.SecretConverter
err string
}{
{
name: "syncset - resource type is valid",
ocmResourceType: "syncset",
vars: map[string]string{
"resourceProviderNamespace": "microsoft.redhatopenshift",
},
syncSetConverter: mock_api.NewMockSyncSetConverter(controller),
err: "",
},
{
name: "syncset - resource type is invalid",
ocmResourceType: "invalid",
vars: map[string]string{
"resourceProviderNamespace": "microsoft.redhatopenshift",
},
syncSetConverter: mock_api.NewMockSyncSetConverter(controller),
err: "the resource type 'invalid' is not valid for api version '2022-09-04'",
},
{
name: "syncset - converter is nil",
ocmResourceType: "syncset",
vars: map[string]string{
"resourceProviderNamespace": "microsoft.redhatopenshift",
},
syncSetConverter: nil,
err: "the resource type 'syncset' is not valid for api version '2022-09-04'",
},
{
name: "machinepool - resource type is valid",
ocmResourceType: "machinepool",
vars: map[string]string{
"resourceProviderNamespace": "microsoft.redhatopenshift",
},
machinePoolConverter: mock_api.NewMockMachinePoolConverter(controller),
err: "",
},
{
name: "machinepool - resource type is invalid",
ocmResourceType: "invalid",
vars: map[string]string{
"resourceProviderNamespace": "microsoft.redhatopenshift",
},
machinePoolConverter: mock_api.NewMockMachinePoolConverter(controller),
err: "the resource type 'invalid' is not valid for api version '2022-09-04'",
},
{
name: "machinepool - converter is nil",
ocmResourceType: "machinepool",
vars: map[string]string{
"resourceProviderNamespace": "microsoft.redhatopenshift",
},
machinePoolConverter: nil,
err: "the resource type 'machinepool' is not valid for api version '2022-09-04'",
},
{
name: "syncidentityprovider - resource type is valid",
ocmResourceType: "syncidentityprovider",
vars: map[string]string{
"resourceProviderNamespace": "microsoft.redhatopenshift",
},
syncIdentityProviderConverter: mock_api.NewMockSyncIdentityProviderConverter(controller),
err: "",
},
{
name: "syncidentityprovider - resource type is invalid",
ocmResourceType: "invalid",
vars: map[string]string{
"resourceProviderNamespace": "microsoft.redhatopenshift",
},
syncIdentityProviderConverter: mock_api.NewMockSyncIdentityProviderConverter(controller),
err: "the resource type 'invalid' is not valid for api version '2022-09-04'",
},
{
name: "syncidentityprovider - converter is nil",
ocmResourceType: "syncidentityprovider",
vars: map[string]string{
"resourceProviderNamespace": "microsoft.redhatopenshift",
},
syncIdentityProviderConverter: nil,
err: "the resource type 'syncidentityprovider' is not valid for api version '2022-09-04'",
},
{
name: "secret - resource type is valid",
ocmResourceType: "secret",
vars: map[string]string{
"resourceProviderNamespace": "microsoft.redhatopenshift",
},
secretConverter: mock_api.NewMockSecretConverter(controller),
err: "",
},
{
name: "secret - resource type is invalid",
ocmResourceType: "invalid",
vars: map[string]string{
"resourceProviderNamespace": "microsoft.redhatopenshift",
},
secretConverter: mock_api.NewMockSecretConverter(controller),
err: "the resource type 'invalid' is not valid for api version '2022-09-04'",
},
{
name: "secret - converter is nil",
ocmResourceType: "secret",
vars: map[string]string{
"resourceProviderNamespace": "microsoft.redhatopenshift",
},
secretConverter: nil,
err: "the resource type 'secret' is not valid for api version '2022-09-04'",
},
} {
t.Run(tt.name, func(t *testing.T) {
f := &frontend{
apis: map[string]*api.Version{
"2022-09-04": {
SyncSetConverter: tt.syncSetConverter,
MachinePoolConverter: tt.machinePoolConverter,
SyncIdentityProviderConverter: tt.syncIdentityProviderConverter,
SecretConverter: tt.secretConverter,
},
},
}
err := f.validateOcmResourceType("2022-09-04", tt.ocmResourceType)
if err != nil {
if err.Error() != tt.err {
t.Errorf("wanted '%v', got '%v'", tt.err, err)
}
}
})
}
}

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

@ -100,11 +100,6 @@ type frontend struct {
now func() time.Time
systemDataClusterDocEnricher func(*api.OpenShiftClusterDocument, *api.SystemData)
systemDataSyncSetEnricher func(*api.ClusterManagerConfigurationDocument, *api.SystemData)
systemDataMachinePoolEnricher func(*api.ClusterManagerConfigurationDocument, *api.SystemData)
systemDataSyncIdentityProviderEnricher func(*api.ClusterManagerConfigurationDocument, *api.SystemData)
systemDataSecretEnricher func(*api.ClusterManagerConfigurationDocument, *api.SystemData)
streamResponder StreamResponder
}
@ -188,11 +183,6 @@ func NewFrontend(ctx context.Context,
now: time.Now,
systemDataClusterDocEnricher: enrichClusterSystemData,
systemDataSyncSetEnricher: enrichSyncSetSystemData,
systemDataMachinePoolEnricher: enrichMachinePoolSystemData,
systemDataSyncIdentityProviderEnricher: enrichSyncIdentityProviderSystemData,
systemDataSecretEnricher: enrichSecretSystemData,
streamResponder: defaultResponder{},
}
@ -245,18 +235,6 @@ func (f *frontend) chiAuthenticatedRoutes(router chi.Router) {
r.Route("/{resourceName}", func(r chi.Router) {
r.With(f.apiVersionMiddleware.ValidateAPIVersion).Route("/", func(r chi.Router) {
// With API version check
if f.env.FeatureIsSet(env.FeatureEnableOCMEndpoints) {
r.Route("/{ocmResourceType}",
func(r chi.Router) {
r.Delete("/{ocmResourceName}", f.deleteClusterManagerConfiguration)
r.Get("/{ocmResourceName}", f.getClusterManagerConfiguration)
r.Patch("/{ocmResourceName}", f.putOrPatchClusterManagerConfiguration)
r.Put("/{ocmResourceName}", f.putOrPatchClusterManagerConfiguration)
},
)
}
r.Delete("/", f.deleteOpenShiftCluster)
r.Get("/", f.getOpenShiftCluster)
r.Patch("/", f.putOrPatchOpenShiftCluster)