зеркало из https://github.com/Azure/ARO-RP.git
ARO-4373 Enable Web Endpoint for the OIDC Storage Account
This commit is contained in:
Родитель
90fde763c3
Коммит
17805e9120
|
@ -45,4 +45,6 @@ feature flags defined in pkg/env/env.go. At the time of writing these include:
|
|||
* EnableOCMEndpoints: Register the OCM endpoints in the frontend. Otherwise the
|
||||
endpoints are not available at all.
|
||||
|
||||
* EnablePublicOIDCBlobAccess: Allow the Public access to the OIDC blob in case the environment needs a decoupling from an AFD endpoint. Production will always use AFD endpoint so no public access for the production.
|
||||
* RequireOIDCStorageWebEndpoint: Since Azure Front Door is only present for INT and PROD, there is a need to determine the web endpoint of the OIDC Storage Account after its creation.
|
||||
Format of web endpoint(It uses Azure DNS Zone endpoint):- **https://[storage-account].z[00-99].web.storage.azure.net** .
|
||||
Used in development only.
|
|
@ -429,7 +429,7 @@ az ad app credential reset \
|
|||
PARENT_DOMAIN_RESOURCEGROUP='$PARENT_DOMAIN_RESOURCEGROUP'
|
||||
export DOMAIN_NAME="\$LOCATION.\$PARENT_DOMAIN_NAME"
|
||||
export AZURE_ENVIRONMENT='AzurePublicCloud'
|
||||
export STORAGE_ACCOUNT_DOMAIN="${RESOURCEGROUP//-}.blob.core.windows.net"
|
||||
export OIDC_STORAGE_ACCOUNT_NAME="${RESOURCEGROUP//-}oic"
|
||||
EOF
|
||||
```
|
||||
|
||||
|
|
|
@ -57,7 +57,9 @@ deploy_oic_dev() {
|
|||
--template-file pkg/deploy/assets/rp-oic.json \
|
||||
--parameters \
|
||||
"rpServicePrincipalId=$(az ad sp list --filter "appId eq '$AZURE_RP_CLIENT_ID'" --query '[].id' -o tsv)" \
|
||||
"storageAccountDomain=$(echo $STORAGE_ACCOUNT_DOMAIN)" >/dev/null
|
||||
"oidcStorageAccountName=$(echo $OIDC_STORAGE_ACCOUNT_NAME)" >/dev/null
|
||||
echo "########## Enabling Static Website for OIDC storage account in RG $RESOURCEGROUP ##########"
|
||||
az storage blob service-properties update --static-website true --account-name ${OIDC_STORAGE_ACCOUNT_NAME} --auth-mode login >/dev/null
|
||||
}
|
||||
|
||||
deploy_rp_managed_identity() {
|
||||
|
@ -98,7 +100,9 @@ deploy_oic_for_dedicated_rp() {
|
|||
--template-file pkg/deploy/assets/rp-oic.json \
|
||||
--parameters \
|
||||
"rpServicePrincipalId=$(az identity show -g $RESOURCEGROUP -n aro-rp-$LOCATION | jq -r '.["principalId"]')" \
|
||||
"storageAccountDomain=$(yq '.rps[].configuration.storageAccountDomain' dev-config.yaml)"
|
||||
"oidcStorageAccountName=$(yq '.rps[].configuration.oidcStorageAccountName' dev-config.yaml)" >/dev/null
|
||||
echo "########## Enabling Static Website for OIDC storage account in RG $RESOURCEGROUP ##########"
|
||||
az storage blob service-properties update --static-website true --account-name ${yq '.rps[].configuration.oidcStorageAccountName' dev-config.yaml} --auth-mode login >/dev/null
|
||||
}
|
||||
|
||||
deploy_env_dev_override() {
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
|
||||
mgmtnetwork "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2020-08-01/network"
|
||||
mgmtfeatures "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2019-07-01/features"
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
|
@ -25,6 +26,7 @@ import (
|
|||
"github.com/Azure/ARO-RP/pkg/util/azureclient"
|
||||
"github.com/Azure/ARO-RP/pkg/util/azureerrors"
|
||||
"github.com/Azure/ARO-RP/pkg/util/dns"
|
||||
"github.com/Azure/ARO-RP/pkg/util/oidcbuilder"
|
||||
"github.com/Azure/ARO-RP/pkg/util/rbac"
|
||||
"github.com/Azure/ARO-RP/pkg/util/stringutils"
|
||||
)
|
||||
|
@ -443,7 +445,12 @@ func (m *manager) Delete(ctx context.Context) error {
|
|||
|
||||
if m.doc.OpenShiftCluster.Properties.ServicePrincipalProfile == nil && m.doc.OpenShiftCluster.Properties.PlatformWorkloadIdentityProfile != nil {
|
||||
m.log.Printf("deleting OIDC configuration")
|
||||
err = m.rpBlob.DeleteBlobContainer(ctx, m.env.ResourceGroup(), m.env.OIDCStorageAccountName(), env.OIDCBlobContainerPrefix+m.doc.ID)
|
||||
blobContainerURL := oidcbuilder.GenerateBlobContainerURL(m.env)
|
||||
azBlobClient, err := m.rpBlob.GetAZBlobClient(blobContainerURL, &azblob.ClientOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = oidcbuilder.DeleteOidcFolder(ctx, env.OIDCBlobDirectoryPrefix+m.doc.ID, azBlobClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
azstorage "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
|
||||
mgmtnetwork "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2020-08-01/network"
|
||||
mgmtfeatures "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2019-07-01/features"
|
||||
|
@ -44,19 +43,20 @@ func (m *manager) createOIDC(ctx context.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
blobContainerName := env.OIDCBlobContainerPrefix + m.doc.ID
|
||||
|
||||
publicAccess := azstorage.PublicAccessNone
|
||||
// Public access on OIDC Container needed for development environments because of no AFD availability
|
||||
if m.env.FeatureIsSet(env.FeatureEnablePublicOIDCBlobAccess) {
|
||||
publicAccess = azstorage.PublicAccessBlob
|
||||
}
|
||||
err := m.rpBlob.CreateBlobContainer(ctx, m.env.ResourceGroup(), m.env.OIDCStorageAccountName(), blobContainerName, publicAccess)
|
||||
if err != nil {
|
||||
return err
|
||||
// OIDC Storage Web Endpoint need to be determined for Development environments
|
||||
var oidcEndpoint string
|
||||
if m.env.FeatureIsSet(env.FeatureRequireOIDCStorageWebEndpoint) {
|
||||
properties, err := m.rpBlob.GetContainerProperties(ctx, m.env.ResourceGroup(), m.env.OIDCStorageAccountName(), oidcbuilder.WebContainer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oidcEndpoint = *properties.Properties.PrimaryEndpoints.Web
|
||||
} else {
|
||||
// For Production Azure Front Door Endpoint will be the OIDC Endpoint
|
||||
oidcEndpoint = m.env.OIDCEndpoint()
|
||||
}
|
||||
|
||||
oidcBuilder, err := oidcbuilder.NewOIDCBuilder(m.env.Environment().StorageEndpointSuffix, m.env.OIDCEndpoint(), m.env.OIDCStorageAccountName(), blobContainerName)
|
||||
oidcBuilder, err := oidcbuilder.NewOIDCBuilder(m.env, oidcEndpoint, env.OIDCBlobDirectoryPrefix+m.doc.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ func (m *manager) createOIDC(ctx context.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
err = oidcBuilder.EnsureOIDCDocs(ctx, blobContainerName, azBlobClient)
|
||||
err = oidcBuilder.EnsureOIDCDocs(ctx, azBlobClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1407,10 +1407,20 @@ func TestCreateOIDC(t *testing.T) {
|
|||
resourceGroupName := "fakeResourceGroup"
|
||||
oidcStorageAccountName := "eastusoic"
|
||||
afdEndpoint := "fake.oic.aro.test.net"
|
||||
storageEndpointForDev := oidcStorageAccountName + ".blob." + azureclient.PublicCloud.StorageEndpointSuffix
|
||||
storageWebEndpointForDev := oidcStorageAccountName + ".web." + azureclient.PublicCloud.StorageEndpointSuffix
|
||||
resourceID := "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/resourceGroup/providers/Microsoft.RedHatOpenShift/openShiftClusters/resourceName"
|
||||
prodOIDCIssuer := fmt.Sprintf("https://%s/%s%s", afdEndpoint, env.OIDCBlobContainerPrefix, clusterID)
|
||||
devOIDCIssuer := fmt.Sprintf("https://%s/%s%s", storageEndpointForDev, env.OIDCBlobContainerPrefix, clusterID)
|
||||
blobContainerURL := fmt.Sprintf("https://%s.blob.%s/%s", oidcStorageAccountName, azureclient.PublicCloud.StorageEndpointSuffix, oidcbuilder.WebContainer)
|
||||
prodOIDCIssuer := fmt.Sprintf("https://%s/%s%s", afdEndpoint, env.OIDCBlobDirectoryPrefix, clusterID)
|
||||
devOIDCIssuer := fmt.Sprintf("https://%s/%s%s", storageWebEndpointForDev, env.OIDCBlobDirectoryPrefix, clusterID)
|
||||
containerProperties := azstorage.AccountsClientGetPropertiesResponse{
|
||||
Account: azstorage.Account{
|
||||
Properties: &azstorage.AccountProperties{
|
||||
PrimaryEndpoints: &azstorage.Endpoints{
|
||||
Web: to.StringPtr(storageWebEndpointForDev),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
|
@ -1470,15 +1480,13 @@ func TestCreateOIDC(t *testing.T) {
|
|||
},
|
||||
},
|
||||
mocks: func(blob *mock_azblob.MockManager, menv *mock_env.MockInterface, azblobClient *mock_azblob.MockAZBlobClient) {
|
||||
menv.EXPECT().OIDCStorageAccountName().AnyTimes().Return(oidcStorageAccountName)
|
||||
menv.EXPECT().FeatureIsSet(env.FeatureEnablePublicOIDCBlobAccess).Return(false)
|
||||
menv.EXPECT().ResourceGroup().Return(resourceGroupName)
|
||||
menv.EXPECT().Environment().Return(&azureclient.PublicCloud)
|
||||
menv.EXPECT().FeatureIsSet(env.FeatureRequireOIDCStorageWebEndpoint).Return(false)
|
||||
menv.EXPECT().OIDCEndpoint().Return(afdEndpoint)
|
||||
blob.EXPECT().CreateBlobContainer(gomock.Any(), resourceGroupName, oidcStorageAccountName, gomock.Any(), azstorage.PublicAccessNone).Return(nil)
|
||||
azblobClient.EXPECT().UploadBuffer(gomock.Any(), "", oidcbuilder.DiscoveryDocumentKey, gomock.Any()).Return(nil)
|
||||
azblobClient.EXPECT().UploadBuffer(gomock.Any(), "", oidcbuilder.JWKSKey, gomock.Any()).Return(nil)
|
||||
blob.EXPECT().GetAZBlobClient(gomock.Any(), &azblob.ClientOptions{}).Return(azblobClient, nil)
|
||||
menv.EXPECT().OIDCStorageAccountName().Return(oidcStorageAccountName)
|
||||
menv.EXPECT().Environment().Return(&azureclient.PublicCloud)
|
||||
blob.EXPECT().GetAZBlobClient(blobContainerURL, &azblob.ClientOptions{}).Return(azblobClient, nil)
|
||||
azblobClient.EXPECT().UploadBuffer(gomock.Any(), "", oidcbuilder.DocumentKey(env.OIDCBlobDirectoryPrefix+clusterID, oidcbuilder.DiscoveryDocumentKey), gomock.Any()).Return(nil)
|
||||
azblobClient.EXPECT().UploadBuffer(gomock.Any(), "", oidcbuilder.DocumentKey(env.OIDCBlobDirectoryPrefix+clusterID, oidcbuilder.JWKSKey), gomock.Any()).Return(nil)
|
||||
},
|
||||
wantedOIDCIssuer: pointerutils.ToPtr(api.OIDCIssuer(prodOIDCIssuer)),
|
||||
wantBoundServiceAccountSigningKey: true,
|
||||
|
@ -1498,21 +1506,20 @@ func TestCreateOIDC(t *testing.T) {
|
|||
},
|
||||
},
|
||||
mocks: func(blob *mock_azblob.MockManager, menv *mock_env.MockInterface, azblobClient *mock_azblob.MockAZBlobClient) {
|
||||
menv.EXPECT().OIDCStorageAccountName().AnyTimes().Return(oidcStorageAccountName)
|
||||
menv.EXPECT().FeatureIsSet(env.FeatureEnablePublicOIDCBlobAccess).Return(true)
|
||||
menv.EXPECT().FeatureIsSet(env.FeatureRequireOIDCStorageWebEndpoint).Return(true)
|
||||
menv.EXPECT().ResourceGroup().Return(resourceGroupName)
|
||||
menv.EXPECT().OIDCStorageAccountName().AnyTimes().Return(oidcStorageAccountName)
|
||||
blob.EXPECT().GetContainerProperties(gomock.Any(), resourceGroupName, oidcStorageAccountName, oidcbuilder.WebContainer).Return(containerProperties, nil)
|
||||
menv.EXPECT().Environment().Return(&azureclient.PublicCloud)
|
||||
menv.EXPECT().OIDCEndpoint().Return(storageEndpointForDev)
|
||||
blob.EXPECT().CreateBlobContainer(gomock.Any(), resourceGroupName, oidcStorageAccountName, gomock.Any(), azstorage.PublicAccessBlob).Return(nil)
|
||||
azblobClient.EXPECT().UploadBuffer(gomock.Any(), "", oidcbuilder.DiscoveryDocumentKey, gomock.Any()).Return(nil)
|
||||
azblobClient.EXPECT().UploadBuffer(gomock.Any(), "", oidcbuilder.JWKSKey, gomock.Any()).Return(nil)
|
||||
blob.EXPECT().GetAZBlobClient(gomock.Any(), &azblob.ClientOptions{}).Return(azblobClient, nil)
|
||||
blob.EXPECT().GetAZBlobClient(blobContainerURL, &azblob.ClientOptions{}).Return(azblobClient, nil)
|
||||
azblobClient.EXPECT().UploadBuffer(gomock.Any(), "", oidcbuilder.DocumentKey(env.OIDCBlobDirectoryPrefix+clusterID, oidcbuilder.DiscoveryDocumentKey), gomock.Any()).Return(nil)
|
||||
azblobClient.EXPECT().UploadBuffer(gomock.Any(), "", oidcbuilder.DocumentKey(env.OIDCBlobDirectoryPrefix+clusterID, oidcbuilder.JWKSKey), gomock.Any()).Return(nil)
|
||||
},
|
||||
wantedOIDCIssuer: pointerutils.ToPtr(api.OIDCIssuer(devOIDCIssuer)),
|
||||
wantBoundServiceAccountSigningKey: true,
|
||||
},
|
||||
{
|
||||
name: "Fail - Create Blob Container throws error",
|
||||
name: "Fail - Get Container Properties throws error",
|
||||
oc: &api.OpenShiftClusterDocument{
|
||||
Key: strings.ToLower(resourceID),
|
||||
ID: clusterID,
|
||||
|
@ -1526,10 +1533,10 @@ func TestCreateOIDC(t *testing.T) {
|
|||
},
|
||||
},
|
||||
mocks: func(blob *mock_azblob.MockManager, menv *mock_env.MockInterface, azblob *mock_azblob.MockAZBlobClient) {
|
||||
menv.EXPECT().OIDCStorageAccountName().AnyTimes().Return(oidcStorageAccountName)
|
||||
menv.EXPECT().FeatureIsSet(env.FeatureEnablePublicOIDCBlobAccess).Return(false)
|
||||
menv.EXPECT().FeatureIsSet(env.FeatureRequireOIDCStorageWebEndpoint).Return(true)
|
||||
menv.EXPECT().ResourceGroup().Return(resourceGroupName)
|
||||
blob.EXPECT().CreateBlobContainer(gomock.Any(), resourceGroupName, oidcStorageAccountName, gomock.Any(), azstorage.PublicAccessNone).Return(errors.New("generic error"))
|
||||
menv.EXPECT().OIDCStorageAccountName().AnyTimes().Return(oidcStorageAccountName)
|
||||
blob.EXPECT().GetContainerProperties(gomock.Any(), resourceGroupName, oidcStorageAccountName, oidcbuilder.WebContainer).Return(containerProperties, errors.New("generic error"))
|
||||
},
|
||||
wantBoundServiceAccountSigningKey: false,
|
||||
wantErr: "generic error",
|
||||
|
@ -1549,13 +1556,11 @@ func TestCreateOIDC(t *testing.T) {
|
|||
},
|
||||
},
|
||||
mocks: func(blob *mock_azblob.MockManager, menv *mock_env.MockInterface, azblobClient *mock_azblob.MockAZBlobClient) {
|
||||
menv.EXPECT().OIDCStorageAccountName().AnyTimes().Return(oidcStorageAccountName)
|
||||
menv.EXPECT().FeatureIsSet(env.FeatureEnablePublicOIDCBlobAccess).Return(false)
|
||||
menv.EXPECT().ResourceGroup().Return(resourceGroupName)
|
||||
menv.EXPECT().Environment().Return(&azureclient.PublicCloud)
|
||||
menv.EXPECT().FeatureIsSet(env.FeatureRequireOIDCStorageWebEndpoint).Return(false)
|
||||
menv.EXPECT().OIDCEndpoint().Return(afdEndpoint)
|
||||
blob.EXPECT().CreateBlobContainer(gomock.Any(), resourceGroupName, oidcStorageAccountName, gomock.Any(), azstorage.PublicAccessNone).Return(nil)
|
||||
blob.EXPECT().GetAZBlobClient(gomock.Any(), &azblob.ClientOptions{}).Return(azblobClient, errors.New("generic error"))
|
||||
menv.EXPECT().OIDCStorageAccountName().Return(oidcStorageAccountName)
|
||||
menv.EXPECT().Environment().Return(&azureclient.PublicCloud)
|
||||
blob.EXPECT().GetAZBlobClient(blobContainerURL, &azblob.ClientOptions{}).Return(azblobClient, errors.New("generic error"))
|
||||
},
|
||||
wantBoundServiceAccountSigningKey: false,
|
||||
wantErr: "generic error",
|
||||
|
@ -1575,15 +1580,12 @@ func TestCreateOIDC(t *testing.T) {
|
|||
},
|
||||
},
|
||||
mocks: func(blob *mock_azblob.MockManager, menv *mock_env.MockInterface, azblobClient *mock_azblob.MockAZBlobClient) {
|
||||
menv.EXPECT().OIDCStorageAccountName().AnyTimes().Return(oidcStorageAccountName)
|
||||
menv.EXPECT().FeatureIsSet(env.FeatureEnablePublicOIDCBlobAccess).Return(false)
|
||||
menv.EXPECT().ResourceGroup().Return(resourceGroupName)
|
||||
menv.EXPECT().Environment().Return(&azureclient.PublicCloud)
|
||||
menv.EXPECT().FeatureIsSet(env.FeatureRequireOIDCStorageWebEndpoint).Return(false)
|
||||
menv.EXPECT().OIDCEndpoint().Return(afdEndpoint)
|
||||
blob.EXPECT().CreateBlobContainer(gomock.Any(), resourceGroupName, oidcStorageAccountName, gomock.Any(), azstorage.PublicAccessNone).Return(nil)
|
||||
azblobClient.EXPECT().UploadBuffer(gomock.Any(), "", oidcbuilder.DiscoveryDocumentKey, gomock.Any()).Return(nil)
|
||||
azblobClient.EXPECT().UploadBuffer(gomock.Any(), "", oidcbuilder.JWKSKey, gomock.Any()).Return(errors.New("generic error"))
|
||||
blob.EXPECT().GetAZBlobClient(gomock.Any(), &azblob.ClientOptions{}).Return(azblobClient, nil)
|
||||
menv.EXPECT().OIDCStorageAccountName().Return(oidcStorageAccountName)
|
||||
menv.EXPECT().Environment().Return(&azureclient.PublicCloud)
|
||||
blob.EXPECT().GetAZBlobClient(blobContainerURL, &azblob.ClientOptions{}).Return(azblobClient, nil)
|
||||
azblobClient.EXPECT().UploadBuffer(gomock.Any(), "", oidcbuilder.DocumentKey(env.OIDCBlobDirectoryPrefix+clusterID, oidcbuilder.DiscoveryDocumentKey), gomock.Any()).Return(errors.New("generic error"))
|
||||
},
|
||||
wantBoundServiceAccountSigningKey: false,
|
||||
wantErr: "generic error",
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"rpServicePrincipalId": {
|
||||
"oidcStorageAccountName": {
|
||||
"type": "string"
|
||||
},
|
||||
"storageAccountDomain": {
|
||||
"rpServicePrincipalId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
|
@ -26,22 +26,22 @@
|
|||
"Az.Sec.AnonymousBlobAccessEnforcement::Skip": "PublicRelease"
|
||||
},
|
||||
"location": "[resourceGroup().location]",
|
||||
"name": "[concat(take(substring(parameters('storageAccountDomain'), 0, indexOf(parameters('storageAccountDomain'), '.')), 21), 'oic')]",
|
||||
"name": "[parameters('oidcStorageAccountName')]",
|
||||
"type": "Microsoft.Storage/storageAccounts",
|
||||
"apiVersion": "2021-09-01"
|
||||
},
|
||||
{
|
||||
"name": "[concat(concat(take(substring(parameters('storageAccountDomain'), 0, indexOf(parameters('storageAccountDomain'), '.')), 21), 'oic'), '/Microsoft.Authorization/', guid(resourceId('Microsoft.Storage/storageAccounts', concat(take(substring(parameters('storageAccountDomain'), 0, indexOf(parameters('storageAccountDomain'), '.')), 21), 'oic'))))]",
|
||||
"name": "[concat(parameters('oidcStorageAccountName'), '/Microsoft.Authorization/', guid(resourceId('Microsoft.Storage/storageAccounts', parameters('oidcStorageAccountName'))))]",
|
||||
"type": "Microsoft.Storage/storageAccounts/providers/roleAssignments",
|
||||
"properties": {
|
||||
"scope": "[resourceId('Microsoft.Storage/storageAccounts', concat(take(substring(parameters('storageAccountDomain'), 0, indexOf(parameters('storageAccountDomain'), '.')), 21), 'oic'))]",
|
||||
"scope": "[resourceId('Microsoft.Storage/storageAccounts', parameters('oidcStorageAccountName'))]",
|
||||
"roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]",
|
||||
"principalId": "[parameters('rpServicePrincipalId')]",
|
||||
"principalType": "ServicePrincipal"
|
||||
},
|
||||
"apiVersion": "2018-09-01-preview",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Storage/storageAccounts', concat(take(substring(parameters('storageAccountDomain'), 0, indexOf(parameters('storageAccountDomain'), '.')), 21), 'oic'))]"
|
||||
"[resourceId('Microsoft.Storage/storageAccounts', parameters('oidcStorageAccountName'))]"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -97,6 +97,7 @@ type Configuration struct {
|
|||
SubscriptionResourceGroupLocation *string `json:"subscriptionResourceGroupLocation,omitempty" value:"required"`
|
||||
VMSize *string `json:"vmSize,omitempty" value:"required"`
|
||||
VMSSCleanupEnabled *bool `json:"vmssCleanupEnabled,omitempty"`
|
||||
OIDCStorageAccountName *string `json:"oidcStorageAccountName,omitempty" value:"required"`
|
||||
|
||||
// TODO: Replace with Live Service Configuration in KeyVault
|
||||
InstallViaHive *string `json:"clustersInstallViaHive,omitempty"`
|
||||
|
|
|
@ -87,6 +87,12 @@ func DevConfig(_env env.Core) (*Config, error) {
|
|||
keyvaultPrefix = keyvaultPrefix[:20]
|
||||
}
|
||||
|
||||
oidcStorageAccountName := os.Getenv("USER") + _env.Location()
|
||||
if len(oidcStorageAccountName) >= 21 {
|
||||
oidcStorageAccountName = oidcStorageAccountName[:21]
|
||||
}
|
||||
oidcStorageAccountName = oidcStorageAccountName + "oic"
|
||||
|
||||
return &Config{
|
||||
RPs: []RPConfig{
|
||||
{
|
||||
|
@ -101,6 +107,7 @@ func DevConfig(_env env.Core) (*Config, error) {
|
|||
KeyvaultDNSSuffix: &_env.Environment().KeyVaultDNSSuffix,
|
||||
KeyvaultPrefix: &keyvaultPrefix,
|
||||
StorageAccountDomain: to.StringPtr(os.Getenv("USER") + "aro" + _env.Location() + ".blob." + _env.Environment().StorageEndpointSuffix),
|
||||
OIDCStorageAccountName: to.StringPtr(oidcStorageAccountName),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -175,7 +182,7 @@ func DevConfig(_env env.Core) (*Config, error) {
|
|||
"RequireD2sV3Workers",
|
||||
"DisableReadinessDelay",
|
||||
"EnableOCMEndpoints",
|
||||
"EnablePublicOIDCBlobAccess",
|
||||
"RequireOIDCStorageWebEndpoint",
|
||||
},
|
||||
// TODO update this to support FF
|
||||
RPImagePrefix: to.StringPtr(os.Getenv("USER") + "aro.azurecr.io/aro"),
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
var (
|
||||
// Storage accounts must not contain dashes or be more than 24 characters
|
||||
// Append "oic" to the pre-existing storage account prefix.
|
||||
storageAccountName string = "concat(take(substring(parameters('storageAccountDomain'), 0, indexOf(parameters('storageAccountDomain'), '.')), 21), 'oic')"
|
||||
storageAccountName string = "parameters('oidcStorageAccountName')"
|
||||
resourceTypeStorageAccount string = "Microsoft.Storage/storageAccounts"
|
||||
)
|
||||
|
||||
|
|
|
@ -394,7 +394,7 @@ func (g *generator) rpVMSS() *arm.Resource {
|
|||
"rpMdsdConfigVersion",
|
||||
"rpMdsdNamespace",
|
||||
"rpParentDomainName",
|
||||
"storageAccountDomain",
|
||||
"oidcStorageAccountName",
|
||||
|
||||
// TODO: Replace with Live Service Configuration in KeyVault
|
||||
"clustersInstallViaHive",
|
||||
|
|
|
@ -290,8 +290,8 @@ ARO_INSTALL_VIA_HIVE='$CLUSTERSINSTALLVIAHIVE'
|
|||
ARO_HIVE_DEFAULT_INSTALLER_PULLSPEC='$CLUSTERDEFAULTINSTALLERPULLSPEC'
|
||||
ARO_ADOPT_BY_HIVE='$CLUSTERSADOPTBYHIVE'
|
||||
USE_CHECKACCESS='$USECHECKACCESS'
|
||||
STORAGE_ACCOUNT_DOMAIN='$STORAGEACCOUNTDOMAIN'
|
||||
OIDC_AFD_ENDPOINT='$LOCATION.oic.$RPPARENTDOMAINNAME'
|
||||
OIDC_STORAGE_ACCOUNT_NAME='$OIDCSTORAGEACCOUNTNAME'
|
||||
EOF
|
||||
|
||||
cat >/etc/systemd/system/aro-rp.service <<'EOF'
|
||||
|
@ -330,8 +330,8 @@ ExecStart=/usr/bin/docker run \
|
|||
-e ARO_HIVE_DEFAULT_INSTALLER_PULLSPEC \
|
||||
-e ARO_ADOPT_BY_HIVE \
|
||||
-e USE_CHECKACCESS \
|
||||
-e STORAGE_ACCOUNT_DOMAIN \
|
||||
-e OIDC_AFD_ENDPOINT \
|
||||
-e OIDC_STORAGE_ACCOUNT_NAME \
|
||||
-m 2g \
|
||||
-p 443:8443 \
|
||||
-v /etc/aro-rp:/etc/aro-rp \
|
||||
|
|
|
@ -18,7 +18,7 @@ func (g *generator) oicTemplate() *arm.Template {
|
|||
"rpServicePrincipalId": {
|
||||
Type: "string",
|
||||
},
|
||||
"storageAccountDomain": {
|
||||
"oidcStorageAccountName": {
|
||||
Type: "string",
|
||||
},
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ func newDev(ctx context.Context, log *logrus.Entry, component ServiceComponent)
|
|||
FeatureDisableSignedCertificates,
|
||||
FeatureRequireD2sV3Workers,
|
||||
FeatureDisableReadinessDelay,
|
||||
FeatureEnablePublicOIDCBlobAccess,
|
||||
FeatureRequireOIDCStorageWebEndpoint,
|
||||
} {
|
||||
d.features[feature] = true
|
||||
}
|
||||
|
@ -98,7 +98,3 @@ func (d *dev) FPNewClientCertificateCredential(tenantID string) (*azidentity.Cli
|
|||
|
||||
return credential, nil
|
||||
}
|
||||
|
||||
func (d *dev) OIDCEndpoint() string {
|
||||
return fmt.Sprintf("%s.blob.%s", d.OIDCStorageAccountName(), d.Environment().StorageEndpointSuffix)
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ const (
|
|||
FeatureRequireD2sV3Workers
|
||||
FeatureDisableReadinessDelay
|
||||
FeatureEnableOCMEndpoints
|
||||
FeatureEnablePublicOIDCBlobAccess
|
||||
FeatureRequireOIDCStorageWebEndpoint
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -57,7 +57,7 @@ const (
|
|||
ServiceKeyvaultSuffix = "-svc"
|
||||
RPPrivateEndpointPrefix = "rp-pe-"
|
||||
ProxyHostName = "PROXY_HOSTNAME"
|
||||
OIDCBlobContainerPrefix = "oic-"
|
||||
OIDCBlobDirectoryPrefix = "oic-"
|
||||
)
|
||||
|
||||
// Interface is clunky and somewhat legacy and only used in the RP codebase (not
|
||||
|
|
|
@ -31,9 +31,9 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
KeyvaultPrefix = "KEYVAULT_PREFIX"
|
||||
StorageAccountDomain = "STORAGE_ACCOUNT_DOMAIN"
|
||||
OIDCAFDEndpoint = "OIDC_AFD_ENDPOINT"
|
||||
KeyvaultPrefix = "KEYVAULT_PREFIX"
|
||||
OIDCAFDEndpoint = "OIDC_AFD_ENDPOINT"
|
||||
OIDCStorageAccountName = "OIDC_STORAGE_ACCOUNT_NAME"
|
||||
)
|
||||
|
||||
type prod struct {
|
||||
|
@ -205,11 +205,11 @@ func newProd(ctx context.Context, log *logrus.Entry, component ServiceComponent)
|
|||
p.gatewayDomains = append(p.gatewayDomains, p.acrDomain, acrDataDomain)
|
||||
}
|
||||
|
||||
if err := ValidateVars(StorageAccountDomain); err != nil {
|
||||
if err := ValidateVars(OIDCStorageAccountName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !p.IsLocalDevelopmentMode() {
|
||||
if !p.FeatureIsSet(FeatureRequireOIDCStorageWebEndpoint) {
|
||||
if err := ValidateVars(OIDCAFDEndpoint); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -274,16 +274,11 @@ func (p *prod) ACRDomain() string {
|
|||
}
|
||||
|
||||
func (p *prod) OIDCStorageAccountName() string {
|
||||
storageAccountDomain := os.Getenv("STORAGE_ACCOUNT_DOMAIN")
|
||||
idx := strings.Index(storageAccountDomain, ".")
|
||||
if idx >= 21 {
|
||||
return storageAccountDomain[:21] + "oic"
|
||||
}
|
||||
return storageAccountDomain[:idx] + "oic"
|
||||
return os.Getenv(OIDCStorageAccountName)
|
||||
}
|
||||
|
||||
func (p *prod) OIDCEndpoint() string {
|
||||
return os.Getenv("OIDC_AFD_ENDPOINT")
|
||||
return fmt.Sprintf("https://%s/", os.Getenv("OIDC_AFD_ENDPOINT"))
|
||||
}
|
||||
|
||||
func (p *prod) AROOperatorImage() string {
|
||||
|
|
|
@ -6,9 +6,9 @@ import (
|
|||
"fmt"
|
||||
)
|
||||
|
||||
const _FeatureName = "FeatureDisableDenyAssignmentsFeatureDisableSignedCertificatesFeatureEnableDevelopmentAuthorizerFeatureRequireD2sV3WorkersFeatureDisableReadinessDelayFeatureEnableOCMEndpointsFeatureEnablePublicOIDCBlobAccess"
|
||||
const _FeatureName = "FeatureDisableDenyAssignmentsFeatureDisableSignedCertificatesFeatureEnableDevelopmentAuthorizerFeatureRequireD2sV3WorkersFeatureDisableReadinessDelayFeatureEnableOCMEndpointsFeatureRequireOIDCStorageWebEndpoint"
|
||||
|
||||
var _FeatureIndex = [...]uint8{0, 29, 61, 95, 121, 149, 174, 207}
|
||||
var _FeatureIndex = [...]uint8{0, 29, 61, 95, 121, 149, 174, 210}
|
||||
|
||||
func (i Feature) String() string {
|
||||
if i < 0 || i >= Feature(len(_FeatureIndex)-1) {
|
||||
|
@ -26,7 +26,7 @@ var _FeatureNameToValueMap = map[string]Feature{
|
|||
_FeatureName[95:121]: 3,
|
||||
_FeatureName[121:149]: 4,
|
||||
_FeatureName[149:174]: 5,
|
||||
_FeatureName[174:207]: 6,
|
||||
_FeatureName[174:210]: 6,
|
||||
}
|
||||
|
||||
// FeatureString retrieves an enum value from the enum constants string name.
|
||||
|
|
|
@ -7,89 +7,36 @@ import (
|
|||
"context"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
||||
azstorage "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror"
|
||||
|
||||
"github.com/Azure/ARO-RP/pkg/util/azureclient"
|
||||
"github.com/Azure/ARO-RP/pkg/util/azureclient/azuresdk/armstorage"
|
||||
)
|
||||
|
||||
type Manager interface {
|
||||
CreateBlobContainer(ctx context.Context, resourceGroup string, account string, container string, publicAccess azstorage.PublicAccess) error
|
||||
DeleteBlobContainer(ctx context.Context, resourceGroupName string, accountName string, containerName string) error
|
||||
GetContainerProperties(ctx context.Context, resourceGroupName string, accountName string, containerName string) (azstorage.AccountsClientGetPropertiesResponse, error)
|
||||
GetAZBlobClient(blobContainerURL string, options *azblob.ClientOptions) (AZBlobClient, error)
|
||||
}
|
||||
|
||||
type manager struct {
|
||||
cred azcore.TokenCredential
|
||||
blobContainer armstorage.BlobContainersClient
|
||||
cred azcore.TokenCredential
|
||||
account armstorage.AccountsClient
|
||||
}
|
||||
|
||||
func NewManager(environment *azureclient.AROEnvironment, subscriptionID string, credential azcore.TokenCredential) (Manager, error) {
|
||||
client, err := armstorage.NewBlobContainersClient(environment, subscriptionID, credential)
|
||||
accountsClient, err := armstorage.NewAccountsClient(environment, subscriptionID, credential)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &manager{
|
||||
cred: credential,
|
||||
blobContainer: client,
|
||||
cred: credential,
|
||||
account: accountsClient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *manager) CreateBlobContainer(ctx context.Context, resourceGroup string, accountName string, containerName string, publicAccess azstorage.PublicAccess) error {
|
||||
_, err := m.blobContainer.Get(
|
||||
ctx,
|
||||
resourceGroup,
|
||||
accountName,
|
||||
containerName,
|
||||
&azstorage.BlobContainersClientGetOptions{},
|
||||
)
|
||||
if err != nil && !bloberror.HasCode(err, bloberror.ContainerNotFound) {
|
||||
return err
|
||||
} else if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = m.blobContainer.Create(
|
||||
ctx,
|
||||
resourceGroup,
|
||||
accountName,
|
||||
containerName,
|
||||
azstorage.BlobContainer{
|
||||
ContainerProperties: &azstorage.ContainerProperties{
|
||||
PublicAccess: to.Ptr(publicAccess),
|
||||
},
|
||||
},
|
||||
&azstorage.BlobContainersClientCreateOptions{},
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *manager) DeleteBlobContainer(ctx context.Context, resourceGroupName string, accountName string, containerName string) error {
|
||||
_, err := m.blobContainer.Get(
|
||||
ctx,
|
||||
resourceGroupName,
|
||||
accountName,
|
||||
containerName,
|
||||
&azstorage.BlobContainersClientGetOptions{},
|
||||
)
|
||||
if err != nil {
|
||||
if bloberror.HasCode(err, bloberror.ContainerNotFound) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
_, err = m.blobContainer.Delete(
|
||||
ctx,
|
||||
resourceGroupName,
|
||||
accountName,
|
||||
containerName,
|
||||
&azstorage.BlobContainersClientDeleteOptions{},
|
||||
)
|
||||
return err
|
||||
func (m *manager) GetContainerProperties(ctx context.Context, resourceGroupName string, accountName string, containerName string) (azstorage.AccountsClientGetPropertiesResponse, error) {
|
||||
return m.account.GetProperties(ctx, resourceGroupName, accountName, &azstorage.AccountsClientGetPropertiesOptions{})
|
||||
}
|
||||
|
||||
func (m *manager) GetAZBlobClient(blobContainerURL string, options *azblob.ClientOptions) (AZBlobClient, error) {
|
||||
|
@ -98,6 +45,7 @@ func (m *manager) GetAZBlobClient(blobContainerURL string, options *azblob.Clien
|
|||
|
||||
type AZBlobClient interface {
|
||||
UploadBuffer(ctx context.Context, containerName string, blobName string, buffer []byte) error
|
||||
DeleteBlob(ctx context.Context, containerName string, directoryName string) error
|
||||
}
|
||||
|
||||
type azBlobClient struct {
|
||||
|
@ -116,3 +64,8 @@ func (azBlobClient *azBlobClient) UploadBuffer(ctx context.Context, containerNam
|
|||
_, err := azBlobClient.client.UploadBuffer(ctx, containerName, blobName, buffer, &azblob.UploadBufferOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (azBlobClient *azBlobClient) DeleteBlob(ctx context.Context, containerName string, directoryName string) error {
|
||||
_, err := azBlobClient.client.DeleteBlob(ctx, containerName, directoryName, &azblob.DeleteBlobOptions{})
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,205 +0,0 @@
|
|||
package azblob
|
||||
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the Apache License 2.0.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
|
||||
azstorage "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror"
|
||||
"github.com/golang/mock/gomock"
|
||||
|
||||
mock_armstorage "github.com/Azure/ARO-RP/pkg/util/mocks/azureclient/azuresdk/armstorage"
|
||||
utilerror "github.com/Azure/ARO-RP/test/util/error"
|
||||
)
|
||||
|
||||
type fakeTokenCredential struct{}
|
||||
|
||||
func (c fakeTokenCredential) GetToken(ctx context.Context, options policy.TokenRequestOptions) (azcore.AccessToken, error) {
|
||||
return azcore.AccessToken{}, nil
|
||||
}
|
||||
|
||||
type fakeReadCloser struct {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
func (fakeReadCloser) Close() error { return nil }
|
||||
|
||||
func TestCreateBlobContainer(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
resourceGroupName := "fakeResourceGroup"
|
||||
containerName := "fakeContainer"
|
||||
|
||||
container := azstorage.BlobContainer{
|
||||
ContainerProperties: &azstorage.ContainerProperties{
|
||||
PublicAccess: to.Ptr(azstorage.PublicAccessNone),
|
||||
},
|
||||
}
|
||||
respErrContainerNotFound := &azcore.ResponseError{
|
||||
ErrorCode: string(bloberror.ContainerNotFound),
|
||||
}
|
||||
respErrGeneric := &azcore.ResponseError{
|
||||
ErrorCode: string("Generic Error"),
|
||||
RawResponse: &http.Response{
|
||||
Request: &http.Request{
|
||||
Method: "FAKE",
|
||||
URL: &url.URL{},
|
||||
},
|
||||
Body: fakeReadCloser{bytes.NewBufferString("Generic Error")},
|
||||
},
|
||||
StatusCode: 400,
|
||||
}
|
||||
genericErrorMessage := `FAKE ://
|
||||
--------------------------------------------------------------------------------
|
||||
RESPONSE 0:
|
||||
ERROR CODE: Generic Error
|
||||
--------------------------------------------------------------------------------
|
||||
Generic Error
|
||||
--------------------------------------------------------------------------------
|
||||
`
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
mocks func(*mock_armstorage.MockBlobContainersClient)
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "Success - Create the container",
|
||||
mocks: func(blobContainer *mock_armstorage.MockBlobContainersClient) {
|
||||
blobContainer.EXPECT().Get(ctx, resourceGroupName, "", containerName, &azstorage.BlobContainersClientGetOptions{}).Return(azstorage.BlobContainersClientGetResponse{}, respErrContainerNotFound)
|
||||
blobContainer.EXPECT().Create(ctx, resourceGroupName, "", containerName, container, &azstorage.BlobContainersClientCreateOptions{}).Return(azstorage.BlobContainersClientCreateResponse{}, nil)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Success - Container already exists, so not creating",
|
||||
mocks: func(blobContainer *mock_armstorage.MockBlobContainersClient) {
|
||||
blobContainer.EXPECT().Get(ctx, resourceGroupName, "", containerName, &azstorage.BlobContainersClientGetOptions{}).Return(azstorage.BlobContainersClientGetResponse{}, nil)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Fail - Get Container fails with generic error",
|
||||
mocks: func(blobContainer *mock_armstorage.MockBlobContainersClient) {
|
||||
blobContainer.EXPECT().Get(ctx, resourceGroupName, "", containerName, &azstorage.BlobContainersClientGetOptions{}).Return(azstorage.BlobContainersClientGetResponse{}, respErrGeneric)
|
||||
},
|
||||
wantErr: genericErrorMessage,
|
||||
},
|
||||
{
|
||||
name: "Fail - Create Container fails with generic error",
|
||||
mocks: func(blobContainer *mock_armstorage.MockBlobContainersClient) {
|
||||
blobContainer.EXPECT().Get(ctx, resourceGroupName, "", containerName, &azstorage.BlobContainersClientGetOptions{}).Return(azstorage.BlobContainersClientGetResponse{}, respErrContainerNotFound)
|
||||
blobContainer.EXPECT().Create(ctx, resourceGroupName, "", containerName, container, &azstorage.BlobContainersClientCreateOptions{}).Return(azstorage.BlobContainersClientCreateResponse{}, respErrGeneric)
|
||||
},
|
||||
wantErr: genericErrorMessage,
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
blobContainer := mock_armstorage.NewMockBlobContainersClient(controller)
|
||||
|
||||
if tt.mocks != nil {
|
||||
tt.mocks(blobContainer)
|
||||
}
|
||||
|
||||
m := &manager{
|
||||
cred: fakeTokenCredential{},
|
||||
blobContainer: blobContainer,
|
||||
}
|
||||
|
||||
err := m.CreateBlobContainer(ctx, resourceGroupName, "", containerName, azstorage.PublicAccessNone)
|
||||
utilerror.AssertErrorMessage(t, err, tt.wantErr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteBlobContainer(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
resourceGroupName := "fakeResourceGroup"
|
||||
containerName := "fakeContainer"
|
||||
respErrContainerNotFound := &azcore.ResponseError{
|
||||
ErrorCode: string(bloberror.ContainerNotFound),
|
||||
}
|
||||
respErrGeneric := &azcore.ResponseError{
|
||||
ErrorCode: string("Generic Error"),
|
||||
RawResponse: &http.Response{
|
||||
Request: &http.Request{
|
||||
Method: "FAKE",
|
||||
URL: &url.URL{},
|
||||
},
|
||||
Body: fakeReadCloser{bytes.NewBufferString("Generic Error")},
|
||||
},
|
||||
StatusCode: 400,
|
||||
}
|
||||
genericErrorMessage := `FAKE ://
|
||||
--------------------------------------------------------------------------------
|
||||
RESPONSE 0:
|
||||
ERROR CODE: Generic Error
|
||||
--------------------------------------------------------------------------------
|
||||
Generic Error
|
||||
--------------------------------------------------------------------------------
|
||||
`
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
mocks func(*mock_armstorage.MockBlobContainersClient)
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "Success - Delete the container",
|
||||
mocks: func(blobContainer *mock_armstorage.MockBlobContainersClient) {
|
||||
blobContainer.EXPECT().Get(ctx, resourceGroupName, "", containerName, &azstorage.BlobContainersClientGetOptions{}).Return(azstorage.BlobContainersClientGetResponse{}, nil)
|
||||
blobContainer.EXPECT().Delete(ctx, resourceGroupName, "", containerName, &azstorage.BlobContainersClientDeleteOptions{}).Return(azstorage.BlobContainersClientDeleteResponse{}, nil)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Success - Container does not exist, so not deleting",
|
||||
mocks: func(blobContainer *mock_armstorage.MockBlobContainersClient) {
|
||||
blobContainer.EXPECT().Get(ctx, resourceGroupName, "", containerName, &azstorage.BlobContainersClientGetOptions{}).Return(azstorage.BlobContainersClientGetResponse{}, respErrContainerNotFound)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Success - Get Container fails with generic error, still attempt container deletion",
|
||||
mocks: func(blobContainer *mock_armstorage.MockBlobContainersClient) {
|
||||
blobContainer.EXPECT().Get(ctx, resourceGroupName, "", containerName, &azstorage.BlobContainersClientGetOptions{}).Return(azstorage.BlobContainersClientGetResponse{}, respErrGeneric)
|
||||
blobContainer.EXPECT().Delete(ctx, resourceGroupName, "", containerName, &azstorage.BlobContainersClientDeleteOptions{}).Return(azstorage.BlobContainersClientDeleteResponse{}, nil)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Fail - Delete Container fails with generic error",
|
||||
mocks: func(blobContainer *mock_armstorage.MockBlobContainersClient) {
|
||||
blobContainer.EXPECT().Get(ctx, resourceGroupName, "", containerName, &azstorage.BlobContainersClientGetOptions{}).Return(azstorage.BlobContainersClientGetResponse{}, nil)
|
||||
blobContainer.EXPECT().Delete(ctx, resourceGroupName, "", containerName, &azstorage.BlobContainersClientDeleteOptions{}).Return(azstorage.BlobContainersClientDeleteResponse{}, respErrGeneric)
|
||||
},
|
||||
wantErr: genericErrorMessage,
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
blobContainer := mock_armstorage.NewMockBlobContainersClient(controller)
|
||||
|
||||
if tt.mocks != nil {
|
||||
tt.mocks(blobContainer)
|
||||
}
|
||||
|
||||
m := &manager{
|
||||
cred: fakeTokenCredential{},
|
||||
blobContainer: blobContainer,
|
||||
}
|
||||
|
||||
err := m.DeleteBlobContainer(ctx, resourceGroupName, "", containerName)
|
||||
utilerror.AssertErrorMessage(t, err, tt.wantErr)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package armstorage
|
||||
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the Apache License 2.0.
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
|
||||
|
||||
"github.com/Azure/ARO-RP/pkg/util/azureclient"
|
||||
)
|
||||
|
||||
// AccountsClient is a minimal interface for Azure AccountsClient
|
||||
type AccountsClient interface {
|
||||
GetProperties(ctx context.Context, resourceGroupName string, accountName string, options *armstorage.AccountsClientGetPropertiesOptions) (armstorage.AccountsClientGetPropertiesResponse, error)
|
||||
}
|
||||
|
||||
type accountsClient struct {
|
||||
*armstorage.AccountsClient
|
||||
}
|
||||
|
||||
var _ AccountsClient = &accountsClient{}
|
||||
|
||||
// NewAccountsClient creates a new AccountsClient
|
||||
func NewAccountsClient(environment *azureclient.AROEnvironment, subscriptionID string, credential azcore.TokenCredential) (AccountsClient, error) {
|
||||
options := arm.ClientOptions{
|
||||
ClientOptions: azcore.ClientOptions{
|
||||
Cloud: environment.Cloud,
|
||||
},
|
||||
}
|
||||
clientFactory, err := armstorage.NewClientFactory(subscriptionID, credential, &options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &accountsClient{AccountsClient: clientFactory.NewAccountsClient()}, nil
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package armstorage
|
||||
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the Apache License 2.0.
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
|
||||
|
||||
"github.com/Azure/ARO-RP/pkg/util/azureclient"
|
||||
)
|
||||
|
||||
// BlobContainersClient is a minimal interface for Azure BlobContainersClient
|
||||
type BlobContainersClient interface {
|
||||
Create(ctx context.Context, resourceGroupName string, accountName string, containerName string, blobContainer armstorage.BlobContainer, options *armstorage.BlobContainersClientCreateOptions) (armstorage.BlobContainersClientCreateResponse, error)
|
||||
Get(ctx context.Context, resourceGroupName string, accountName string, containerName string, options *armstorage.BlobContainersClientGetOptions) (armstorage.BlobContainersClientGetResponse, error)
|
||||
Delete(ctx context.Context, resourceGroupName string, accountName string, containerName string, options *armstorage.BlobContainersClientDeleteOptions) (armstorage.BlobContainersClientDeleteResponse, error)
|
||||
}
|
||||
|
||||
type blobContainersClient struct {
|
||||
*armstorage.BlobContainersClient
|
||||
}
|
||||
|
||||
var _ BlobContainersClient = &blobContainersClient{}
|
||||
|
||||
// NewBlobContainersClient creates a new BlobContainersClient
|
||||
func NewBlobContainersClient(environment *azureclient.AROEnvironment, subscriptionID string, credential azcore.TokenCredential) (BlobContainersClient, error) {
|
||||
options := arm.ClientOptions{
|
||||
ClientOptions: azcore.ClientOptions{
|
||||
Cloud: environment.Cloud,
|
||||
},
|
||||
}
|
||||
clientFactory, err := armstorage.NewClientFactory(subscriptionID, credential, &options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &blobContainersClient{BlobContainersClient: clientFactory.NewBlobContainersClient()}, nil
|
||||
}
|
|
@ -4,5 +4,5 @@ package armstorage
|
|||
// Licensed under the Apache License 2.0.
|
||||
|
||||
//go:generate rm -rf ../../../../util/mocks/$GOPACKAGE
|
||||
//go:generate go run ../../../../../vendor/github.com/golang/mock/mockgen -destination=../../../../util/mocks/azureclient/azuresdk/$GOPACKAGE/$GOPACKAGE.go github.com/Azure/ARO-RP/pkg/util/azureclient/azuresdk/$GOPACKAGE BlobContainersClient
|
||||
//go:generate go run ../../../../../vendor/github.com/golang/mock/mockgen -destination=../../../../util/mocks/azureclient/azuresdk/$GOPACKAGE/$GOPACKAGE.go github.com/Azure/ARO-RP/pkg/util/azureclient/azuresdk/$GOPACKAGE AccountsClient
|
||||
//go:generate go run ../../../../../vendor/golang.org/x/tools/cmd/goimports -local=github.com/Azure/ARO-RP -e -w ../../../../util/mocks/azureclient/azuresdk/$GOPACKAGE/$GOPACKAGE.go
|
||||
|
|
|
@ -38,34 +38,6 @@ func (m *MockManager) EXPECT() *MockManagerMockRecorder {
|
|||
return m.recorder
|
||||
}
|
||||
|
||||
// CreateBlobContainer mocks base method.
|
||||
func (m *MockManager) CreateBlobContainer(arg0 context.Context, arg1, arg2, arg3 string, arg4 armstorage.PublicAccess) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CreateBlobContainer", arg0, arg1, arg2, arg3, arg4)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// CreateBlobContainer indicates an expected call of CreateBlobContainer.
|
||||
func (mr *MockManagerMockRecorder) CreateBlobContainer(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateBlobContainer", reflect.TypeOf((*MockManager)(nil).CreateBlobContainer), arg0, arg1, arg2, arg3, arg4)
|
||||
}
|
||||
|
||||
// DeleteBlobContainer mocks base method.
|
||||
func (m *MockManager) DeleteBlobContainer(arg0 context.Context, arg1, arg2, arg3 string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DeleteBlobContainer", arg0, arg1, arg2, arg3)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DeleteBlobContainer indicates an expected call of DeleteBlobContainer.
|
||||
func (mr *MockManagerMockRecorder) DeleteBlobContainer(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteBlobContainer", reflect.TypeOf((*MockManager)(nil).DeleteBlobContainer), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// GetAZBlobClient mocks base method.
|
||||
func (m *MockManager) GetAZBlobClient(arg0 string, arg1 *azblob0.ClientOptions) (azblob.AZBlobClient, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -81,6 +53,21 @@ func (mr *MockManagerMockRecorder) GetAZBlobClient(arg0, arg1 interface{}) *gomo
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAZBlobClient", reflect.TypeOf((*MockManager)(nil).GetAZBlobClient), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetContainerProperties mocks base method.
|
||||
func (m *MockManager) GetContainerProperties(arg0 context.Context, arg1, arg2, arg3 string) (armstorage.AccountsClientGetPropertiesResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetContainerProperties", arg0, arg1, arg2, arg3)
|
||||
ret0, _ := ret[0].(armstorage.AccountsClientGetPropertiesResponse)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetContainerProperties indicates an expected call of GetContainerProperties.
|
||||
func (mr *MockManagerMockRecorder) GetContainerProperties(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetContainerProperties", reflect.TypeOf((*MockManager)(nil).GetContainerProperties), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// MockAZBlobClient is a mock of AZBlobClient interface.
|
||||
type MockAZBlobClient struct {
|
||||
ctrl *gomock.Controller
|
||||
|
@ -104,6 +91,20 @@ func (m *MockAZBlobClient) EXPECT() *MockAZBlobClientMockRecorder {
|
|||
return m.recorder
|
||||
}
|
||||
|
||||
// DeleteBlob mocks base method.
|
||||
func (m *MockAZBlobClient) DeleteBlob(arg0 context.Context, arg1, arg2 string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DeleteBlob", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DeleteBlob indicates an expected call of DeleteBlob.
|
||||
func (mr *MockAZBlobClientMockRecorder) DeleteBlob(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteBlob", reflect.TypeOf((*MockAZBlobClient)(nil).DeleteBlob), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// UploadBuffer mocks base method.
|
||||
func (m *MockAZBlobClient) UploadBuffer(arg0 context.Context, arg1, arg2 string, arg3 []byte) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/Azure/ARO-RP/pkg/util/azureclient/azuresdk/armstorage (interfaces: BlobContainersClient)
|
||||
// Source: github.com/Azure/ARO-RP/pkg/util/azureclient/azuresdk/armstorage (interfaces: AccountsClient)
|
||||
|
||||
// Package mock_armstorage is a generated GoMock package.
|
||||
package mock_armstorage
|
||||
|
@ -12,70 +12,40 @@ import (
|
|||
gomock "github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
// MockBlobContainersClient is a mock of BlobContainersClient interface.
|
||||
type MockBlobContainersClient struct {
|
||||
// MockAccountsClient is a mock of AccountsClient interface.
|
||||
type MockAccountsClient struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockBlobContainersClientMockRecorder
|
||||
recorder *MockAccountsClientMockRecorder
|
||||
}
|
||||
|
||||
// MockBlobContainersClientMockRecorder is the mock recorder for MockBlobContainersClient.
|
||||
type MockBlobContainersClientMockRecorder struct {
|
||||
mock *MockBlobContainersClient
|
||||
// MockAccountsClientMockRecorder is the mock recorder for MockAccountsClient.
|
||||
type MockAccountsClientMockRecorder struct {
|
||||
mock *MockAccountsClient
|
||||
}
|
||||
|
||||
// NewMockBlobContainersClient creates a new mock instance.
|
||||
func NewMockBlobContainersClient(ctrl *gomock.Controller) *MockBlobContainersClient {
|
||||
mock := &MockBlobContainersClient{ctrl: ctrl}
|
||||
mock.recorder = &MockBlobContainersClientMockRecorder{mock}
|
||||
// NewMockAccountsClient creates a new mock instance.
|
||||
func NewMockAccountsClient(ctrl *gomock.Controller) *MockAccountsClient {
|
||||
mock := &MockAccountsClient{ctrl: ctrl}
|
||||
mock.recorder = &MockAccountsClientMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockBlobContainersClient) EXPECT() *MockBlobContainersClientMockRecorder {
|
||||
func (m *MockAccountsClient) EXPECT() *MockAccountsClientMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Create mocks base method.
|
||||
func (m *MockBlobContainersClient) Create(arg0 context.Context, arg1, arg2, arg3 string, arg4 armstorage.BlobContainer, arg5 *armstorage.BlobContainersClientCreateOptions) (armstorage.BlobContainersClientCreateResponse, error) {
|
||||
// GetProperties mocks base method.
|
||||
func (m *MockAccountsClient) GetProperties(arg0 context.Context, arg1, arg2 string, arg3 *armstorage.AccountsClientGetPropertiesOptions) (armstorage.AccountsClientGetPropertiesResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Create", arg0, arg1, arg2, arg3, arg4, arg5)
|
||||
ret0, _ := ret[0].(armstorage.BlobContainersClientCreateResponse)
|
||||
ret := m.ctrl.Call(m, "GetProperties", arg0, arg1, arg2, arg3)
|
||||
ret0, _ := ret[0].(armstorage.AccountsClientGetPropertiesResponse)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Create indicates an expected call of Create.
|
||||
func (mr *MockBlobContainersClientMockRecorder) Create(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call {
|
||||
// GetProperties indicates an expected call of GetProperties.
|
||||
func (mr *MockAccountsClientMockRecorder) GetProperties(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockBlobContainersClient)(nil).Create), arg0, arg1, arg2, arg3, arg4, arg5)
|
||||
}
|
||||
|
||||
// Delete mocks base method.
|
||||
func (m *MockBlobContainersClient) Delete(arg0 context.Context, arg1, arg2, arg3 string, arg4 *armstorage.BlobContainersClientDeleteOptions) (armstorage.BlobContainersClientDeleteResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Delete", arg0, arg1, arg2, arg3, arg4)
|
||||
ret0, _ := ret[0].(armstorage.BlobContainersClientDeleteResponse)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Delete indicates an expected call of Delete.
|
||||
func (mr *MockBlobContainersClientMockRecorder) Delete(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockBlobContainersClient)(nil).Delete), arg0, arg1, arg2, arg3, arg4)
|
||||
}
|
||||
|
||||
// Get mocks base method.
|
||||
func (m *MockBlobContainersClient) Get(arg0 context.Context, arg1, arg2, arg3 string, arg4 *armstorage.BlobContainersClientGetOptions) (armstorage.BlobContainersClientGetResponse, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Get", arg0, arg1, arg2, arg3, arg4)
|
||||
ret0, _ := ret[0].(armstorage.BlobContainersClientGetResponse)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Get indicates an expected call of Get.
|
||||
func (mr *MockBlobContainersClientMockRecorder) Get(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockBlobContainersClient)(nil).Get), arg0, arg1, arg2, arg3, arg4)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProperties", reflect.TypeOf((*MockAccountsClient)(nil).GetProperties), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
|
|
@ -7,12 +7,16 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror"
|
||||
|
||||
"github.com/Azure/ARO-RP/pkg/env"
|
||||
utilazblob "github.com/Azure/ARO-RP/pkg/util/azblob"
|
||||
)
|
||||
|
||||
const (
|
||||
DiscoveryDocumentKey = ".well-known/openid-configuration"
|
||||
JWKSKey = "openid/v1/jwks"
|
||||
WebContainer = "$web"
|
||||
)
|
||||
|
||||
type OIDCBuilder struct {
|
||||
|
@ -20,9 +24,10 @@ type OIDCBuilder struct {
|
|||
publicKey []byte
|
||||
blobContainerURL string
|
||||
endpointURL string
|
||||
directory string
|
||||
}
|
||||
|
||||
func NewOIDCBuilder(storageEndpointSuffix string, storageEndpoint string, accountName, containerName string) (*OIDCBuilder, error) {
|
||||
func NewOIDCBuilder(env env.Interface, oidcEndpoint string, directoryName string) (*OIDCBuilder, error) {
|
||||
privateKey, publicKey, err := CreateKeyPair()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -31,12 +36,17 @@ func NewOIDCBuilder(storageEndpointSuffix string, storageEndpoint string, accoun
|
|||
return &OIDCBuilder{
|
||||
privateKey: privateKey,
|
||||
publicKey: publicKey,
|
||||
blobContainerURL: fmt.Sprintf("https://%s.blob.%s/%s", accountName, storageEndpointSuffix, containerName),
|
||||
endpointURL: fmt.Sprintf("https://%s/%s", storageEndpoint, containerName),
|
||||
blobContainerURL: GenerateBlobContainerURL(env),
|
||||
endpointURL: fmt.Sprintf("%s%s", oidcEndpoint, directoryName),
|
||||
directory: directoryName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *OIDCBuilder) EnsureOIDCDocs(ctx context.Context, oidcContainerName string, azBlobClient utilazblob.AZBlobClient) error {
|
||||
func GenerateBlobContainerURL(env env.Interface) string {
|
||||
return fmt.Sprintf("https://%s.blob.%s/%s", env.OIDCStorageAccountName(), env.Environment().StorageEndpointSuffix, WebContainer)
|
||||
}
|
||||
|
||||
func (b *OIDCBuilder) EnsureOIDCDocs(ctx context.Context, azBlobClient utilazblob.AZBlobClient) error {
|
||||
// Create the OIDC configuration
|
||||
discoveryDocument := GenerateDiscoveryDocument(b.endpointURL)
|
||||
|
||||
|
@ -46,7 +56,7 @@ func (b *OIDCBuilder) EnsureOIDCDocs(ctx context.Context, oidcContainerName stri
|
|||
return err
|
||||
}
|
||||
|
||||
return populateOidcFolder(ctx, discoveryDocument, jwks, azBlobClient)
|
||||
return populateOidcFolder(ctx, b.directory, discoveryDocument, jwks, azBlobClient)
|
||||
}
|
||||
|
||||
func (b *OIDCBuilder) GetEndpointUrl() string {
|
||||
|
@ -61,11 +71,11 @@ func (b *OIDCBuilder) GetBlobContainerURL() string {
|
|||
return b.blobContainerURL
|
||||
}
|
||||
|
||||
func populateOidcFolder(ctx context.Context, discoveryDocument string, jwks []byte, azBlobClient utilazblob.AZBlobClient) error {
|
||||
func populateOidcFolder(ctx context.Context, directory string, discoveryDocument string, jwks []byte, azBlobClient utilazblob.AZBlobClient) error {
|
||||
err := azBlobClient.UploadBuffer(
|
||||
ctx,
|
||||
"",
|
||||
DiscoveryDocumentKey,
|
||||
DocumentKey(directory, DiscoveryDocumentKey),
|
||||
[]byte(discoveryDocument),
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -75,7 +85,21 @@ func populateOidcFolder(ctx context.Context, discoveryDocument string, jwks []by
|
|||
return azBlobClient.UploadBuffer(
|
||||
ctx,
|
||||
"",
|
||||
JWKSKey,
|
||||
DocumentKey(directory, JWKSKey),
|
||||
jwks,
|
||||
)
|
||||
}
|
||||
|
||||
func DeleteOidcFolder(ctx context.Context, directory string, azBlobClient utilazblob.AZBlobClient) error {
|
||||
for _, key := range []string{DiscoveryDocumentKey, JWKSKey} {
|
||||
err := azBlobClient.DeleteBlob(ctx, "", DocumentKey(directory, key))
|
||||
if err != nil && !bloberror.HasCode(err, bloberror.BlobNotFound) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DocumentKey(directory string, blobKey string) string {
|
||||
return fmt.Sprintf("%s/%s", directory, blobKey)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package oidcbuilder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
|
@ -9,12 +10,20 @@ import (
|
|||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror"
|
||||
"github.com/golang/mock/gomock"
|
||||
|
||||
"github.com/Azure/ARO-RP/pkg/util/azureclient"
|
||||
mock_azblob "github.com/Azure/ARO-RP/pkg/util/mocks/azblob"
|
||||
mock_env "github.com/Azure/ARO-RP/pkg/util/mocks/env"
|
||||
utilerror "github.com/Azure/ARO-RP/test/util/error"
|
||||
)
|
||||
|
||||
|
@ -23,7 +32,7 @@ import (
|
|||
|
||||
func TestEnsureOIDCDocs(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
fakeContainerName := "fakeContainer"
|
||||
directoryName := "fakeDirectory"
|
||||
blobContainerURL := "fakeBlobContainerURL"
|
||||
endpointURL := "fakeEndPointURL"
|
||||
|
||||
|
@ -64,14 +73,15 @@ func TestEnsureOIDCDocs(t *testing.T) {
|
|||
privateKey: priKey,
|
||||
publicKey: pubKey,
|
||||
blobContainerURL: blobContainerURL,
|
||||
directory: directoryName,
|
||||
endpointURL: endpointURL,
|
||||
},
|
||||
mocks: func(azblobClient *mock_azblob.MockAZBlobClient) {
|
||||
azblobClient.EXPECT().
|
||||
UploadBuffer(gomock.Any(), "", DiscoveryDocumentKey, gomock.Any()).
|
||||
UploadBuffer(gomock.Any(), "", DocumentKey(directoryName, DiscoveryDocumentKey), gomock.Any()).
|
||||
Return(nil)
|
||||
azblobClient.EXPECT().
|
||||
UploadBuffer(gomock.Any(), "", JWKSKey, gomock.Any()).
|
||||
UploadBuffer(gomock.Any(), "", DocumentKey(directoryName, JWKSKey), gomock.Any()).
|
||||
Return(nil)
|
||||
},
|
||||
},
|
||||
|
@ -82,6 +92,7 @@ func TestEnsureOIDCDocs(t *testing.T) {
|
|||
publicKey: invalidKey,
|
||||
blobContainerURL: blobContainerURL,
|
||||
endpointURL: endpointURL,
|
||||
directory: directoryName,
|
||||
},
|
||||
wantErr: "Failed to decode PEM file",
|
||||
},
|
||||
|
@ -92,6 +103,7 @@ func TestEnsureOIDCDocs(t *testing.T) {
|
|||
publicKey: incorrectlyEncodedPublicKey,
|
||||
blobContainerURL: blobContainerURL,
|
||||
endpointURL: endpointURL,
|
||||
directory: directoryName,
|
||||
},
|
||||
wantErr: "Failed to parse key content: x509: failed to parse public key (use ParsePKCS1PublicKey instead for this key format)",
|
||||
},
|
||||
|
@ -102,10 +114,11 @@ func TestEnsureOIDCDocs(t *testing.T) {
|
|||
publicKey: pubKey,
|
||||
blobContainerURL: blobContainerURL,
|
||||
endpointURL: endpointURL,
|
||||
directory: directoryName,
|
||||
},
|
||||
mocks: func(azblobClient *mock_azblob.MockAZBlobClient) {
|
||||
azblobClient.EXPECT().
|
||||
UploadBuffer(gomock.Any(), "", DiscoveryDocumentKey, gomock.Any()).
|
||||
UploadBuffer(gomock.Any(), "", DocumentKey(directoryName, DiscoveryDocumentKey), gomock.Any()).
|
||||
Return(errors.New("generic error"))
|
||||
},
|
||||
wantErr: "generic error",
|
||||
|
@ -117,13 +130,14 @@ func TestEnsureOIDCDocs(t *testing.T) {
|
|||
publicKey: pubKey,
|
||||
blobContainerURL: blobContainerURL,
|
||||
endpointURL: endpointURL,
|
||||
directory: directoryName,
|
||||
},
|
||||
mocks: func(azblobClient *mock_azblob.MockAZBlobClient) {
|
||||
azblobClient.EXPECT().
|
||||
UploadBuffer(gomock.Any(), "", DiscoveryDocumentKey, gomock.Any()).
|
||||
UploadBuffer(gomock.Any(), "", DocumentKey(directoryName, DiscoveryDocumentKey), gomock.Any()).
|
||||
Return(nil)
|
||||
azblobClient.EXPECT().
|
||||
UploadBuffer(gomock.Any(), "", JWKSKey, gomock.Any()).
|
||||
UploadBuffer(gomock.Any(), "", DocumentKey(directoryName, JWKSKey), gomock.Any()).
|
||||
Return(errors.New("generic error"))
|
||||
},
|
||||
wantErr: "generic error",
|
||||
|
@ -135,6 +149,7 @@ func TestEnsureOIDCDocs(t *testing.T) {
|
|||
publicKey: nonRSAEncodedPublicKey,
|
||||
blobContainerURL: blobContainerURL,
|
||||
endpointURL: endpointURL,
|
||||
directory: directoryName,
|
||||
},
|
||||
wantErr: "Public key is not of type RSA",
|
||||
},
|
||||
|
@ -149,7 +164,7 @@ func TestEnsureOIDCDocs(t *testing.T) {
|
|||
tt.mocks(azBlobClient)
|
||||
}
|
||||
|
||||
err = tt.oidcbuilder.EnsureOIDCDocs(ctx, fakeContainerName, azBlobClient)
|
||||
err = tt.oidcbuilder.EnsureOIDCDocs(ctx, azBlobClient)
|
||||
utilerror.AssertErrorMessage(t, err, tt.wantErr)
|
||||
|
||||
if tt.oidcbuilder.GetEndpointUrl() != tt.oidcbuilder.endpointURL {
|
||||
|
@ -166,3 +181,119 @@ func TestEnsureOIDCDocs(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
type fakeReadCloser struct {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
func (fakeReadCloser) Close() error { return nil }
|
||||
func TestDeleteOidcFolder(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
directoryName := "fakeDirectory"
|
||||
respErrBlobNotFound := &azcore.ResponseError{
|
||||
ErrorCode: string(bloberror.BlobNotFound),
|
||||
}
|
||||
respErrGeneric := &azcore.ResponseError{
|
||||
ErrorCode: string("Generic Error"),
|
||||
RawResponse: &http.Response{
|
||||
Request: &http.Request{
|
||||
Method: "FAKE",
|
||||
URL: &url.URL{},
|
||||
},
|
||||
Body: fakeReadCloser{bytes.NewBufferString("Generic Error")},
|
||||
},
|
||||
StatusCode: 400,
|
||||
}
|
||||
genericErrorMessage := `FAKE ://
|
||||
--------------------------------------------------------------------------------
|
||||
RESPONSE 0:
|
||||
ERROR CODE: Generic Error
|
||||
--------------------------------------------------------------------------------
|
||||
Generic Error
|
||||
--------------------------------------------------------------------------------
|
||||
`
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
mocks func(*mock_azblob.MockAZBlobClient)
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "Success",
|
||||
mocks: func(azblobClient *mock_azblob.MockAZBlobClient) {
|
||||
azblobClient.EXPECT().DeleteBlob(ctx, "", DocumentKey(directoryName, DiscoveryDocumentKey)).Return(nil)
|
||||
azblobClient.EXPECT().DeleteBlob(ctx, "", DocumentKey(directoryName, JWKSKey)).Return(nil)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Fail - Generic Error when deleting DiscoveryDocument",
|
||||
mocks: func(azblobClient *mock_azblob.MockAZBlobClient) {
|
||||
azblobClient.EXPECT().DeleteBlob(ctx, "", DocumentKey(directoryName, DiscoveryDocumentKey)).Return(respErrGeneric)
|
||||
},
|
||||
wantErr: genericErrorMessage,
|
||||
},
|
||||
{
|
||||
name: "Fail - Generic Error when deleting JWKS",
|
||||
mocks: func(azblobClient *mock_azblob.MockAZBlobClient) {
|
||||
azblobClient.EXPECT().DeleteBlob(ctx, "", DocumentKey(directoryName, DiscoveryDocumentKey)).Return(respErrBlobNotFound)
|
||||
azblobClient.EXPECT().DeleteBlob(ctx, "", DocumentKey(directoryName, JWKSKey)).Return(respErrGeneric)
|
||||
},
|
||||
wantErr: genericErrorMessage,
|
||||
},
|
||||
{
|
||||
name: "Success - One Blob exists and other doesn't",
|
||||
mocks: func(azblobClient *mock_azblob.MockAZBlobClient) {
|
||||
azblobClient.EXPECT().DeleteBlob(ctx, "", DocumentKey(directoryName, DiscoveryDocumentKey)).Return(respErrBlobNotFound)
|
||||
azblobClient.EXPECT().DeleteBlob(ctx, "", DocumentKey(directoryName, JWKSKey)).Return(nil)
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
azBlobClient := mock_azblob.NewMockAZBlobClient(controller)
|
||||
|
||||
if tt.mocks != nil {
|
||||
tt.mocks(azBlobClient)
|
||||
}
|
||||
|
||||
err := DeleteOidcFolder(ctx, directoryName, azBlobClient)
|
||||
utilerror.AssertErrorMessage(t, err, tt.wantErr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateBlobContainerURL(t *testing.T) {
|
||||
oidcStorageAccountName := "eastusoic"
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
mocks func(*mock_env.MockInterface)
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "Success: Working as Expected",
|
||||
mocks: func(menv *mock_env.MockInterface) {
|
||||
menv.EXPECT().OIDCStorageAccountName().Return(oidcStorageAccountName)
|
||||
menv.EXPECT().Environment().Return(&azureclient.PublicCloud)
|
||||
},
|
||||
expected: fmt.Sprintf("https://%s.blob.%s/%s", oidcStorageAccountName, azureclient.PublicCloud.StorageEndpointSuffix, WebContainer),
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
controller := gomock.NewController(t)
|
||||
defer controller.Finish()
|
||||
|
||||
env := mock_env.NewMockInterface(controller)
|
||||
|
||||
if tt.mocks != nil {
|
||||
tt.mocks(env)
|
||||
}
|
||||
|
||||
result := GenerateBlobContainerURL(env)
|
||||
if result != tt.expected {
|
||||
t.Fatalf("Expected %s, but received %s", tt.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче