Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove deprecated Azure SDK's from Calamari.CloudAccounts #1356

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion source/Calamari.Azure/AppServices/AzureTargetSite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using Calamari.CloudAccounts;
using Calamari.Common.Plumbing.Logging;
using Calamari.Common.Plumbing.Variables;
using Microsoft.Azure.Management.Fluent;

namespace Calamari.Azure.AppServices
{
Expand Down
20 changes: 1 addition & 19 deletions source/Calamari.Azure/AzureClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ public static class AzureClient
/// <returns></returns>
public static ArmClient CreateArmClient(this IAzureAccount azureAccount, Action<RetryOptions> retryOptionsSetter = null)
{
if (azureAccount.AccountType == AccountType.AzureOidc)
if (azureAccount is AzureOidcAccount oidcAccount)
{
var oidcAccount = (AzureOidcAccount)azureAccount;
var (clientOptions, _) = GetArmClientOptions(azureAccount, retryOptionsSetter);
var clientAssertionCreds = new ClientAssertionCredential(oidcAccount.TenantId, oidcAccount.ClientId, () => oidcAccount.GetCredentials);
return new ArmClient(clientAssertionCreds, defaultSubscriptionId: azureAccount.SubscriptionNumber, clientOptions);
Expand Down Expand Up @@ -76,22 +75,5 @@ public static (ArmClientOptions, TokenCredentialOptions) GetArmClientOptions(thi

return (armClientOptions, tokenCredentialOptions);
}

public static async Task<string> GetAccessTokenAsync(this IAzureAccount azureAccount)
{
return azureAccount.AccountType == AccountType.AzureOidc
? await AzureOidcAccountExtensions.GetAuthorizationToken(azureAccount.TenantId,
azureAccount.ClientId,
azureAccount.GetCredentials,
azureAccount.ResourceManagementEndpointBaseUri,
azureAccount.ActiveDirectoryEndpointBaseUri,
azureAccount.AzureEnvironment,
CancellationToken.None)
: await AzureServicePrincipalAccountExtensions.GetAuthorizationToken(azureAccount.TenantId,
azureAccount.ClientId,
azureAccount.GetCredentials,
azureAccount.ResourceManagementEndpointBaseUri,
azureAccount.ActiveDirectoryEndpointBaseUri);
}
}
}
59 changes: 23 additions & 36 deletions source/Calamari.Azure/AzureKnownEnvironment.cs
Original file line number Diff line number Diff line change
@@ -1,59 +1,46 @@
using System;
using Azure.Identity;
using Azure.ResourceManager;
using Microsoft.Azure.Management.ResourceManager.Fluent;

namespace Calamari.Azure
{
public sealed class AzureKnownEnvironment
{
{
/// <param name="environment">The environment name exactly matching the names defined in Azure SDK (see here https://github.com/Azure/azure-libraries-for-net/blob/master/src/ResourceManagement/ResourceManager/AzureEnvironment.cs)
/// Other names are allowed in case this list is ever expanded/changed, but will likely result in an error at deployment time.
/// </param>
public AzureKnownEnvironment(string environment)
{
Value = environment;

if (string.IsNullOrEmpty(environment) || environment == "AzureCloud") // This environment name is defined in Sashimi.Azure.Accounts.AzureEnvironmentsListAction
Value = Global.Value; // We interpret it as the normal Azure environment for historical reasons)

azureSdkEnvironment = AzureEnvironment.FromName(Value) ??
throw new InvalidOperationException($"Unknown environment name {Value}");
if (string.IsNullOrEmpty(environment) || environment == "AzureCloud") // This environment name is defined in Sashimi.Azure.Accounts.AzureEnvironmentsListAction
{
Value = "AzureGlobalCloud"; // We interpret it as the normal Azure environment for historical reasons)
}
}

private readonly AzureEnvironment azureSdkEnvironment;
public string Value { get; }

public static readonly AzureKnownEnvironment Global = new AzureKnownEnvironment("AzureGlobalCloud");
public static readonly AzureKnownEnvironment AzureChinaCloud = new AzureKnownEnvironment("AzureChinaCloud");
public static readonly AzureKnownEnvironment AzureUSGovernment = new AzureKnownEnvironment("AzureUSGovernment");
public static readonly AzureKnownEnvironment AzureGermanCloud = new AzureKnownEnvironment("AzureGermanCloud");
Comment on lines -27 to -30
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of this was unused. The only one was Global, but we only needed the string, so let's just use it.


public AzureEnvironment AsAzureSDKEnvironment()
{
return azureSdkEnvironment;
}

public ArmEnvironment AsAzureArmEnvironment() => ToArmEnvironment(Value);

private static ArmEnvironment ToArmEnvironment(string name) => name switch
{
"AzureGlobalCloud" => ArmEnvironment.AzurePublicCloud,
"AzureChinaCloud" => ArmEnvironment.AzureChina,
"AzureGermanCloud" => ArmEnvironment.AzureGermany,
"AzureUSGovernment" => ArmEnvironment.AzureGovernment,
_ => throw new InvalidOperationException($"ARM Environment {name} is not a known Azure Environment name.")
};

static ArmEnvironment ToArmEnvironment(string name) => name switch
{
"AzureGlobalCloud" => ArmEnvironment.AzurePublicCloud,
"AzureChinaCloud" => ArmEnvironment.AzureChina,
"AzureGermanCloud" => ArmEnvironment.AzureGermany,
"AzureUSGovernment" => ArmEnvironment.AzureGovernment,
_ => throw new InvalidOperationException($"ARM Environment {name} is not a known Azure Environment name.")
};
public Uri GetAzureAuthorityHost() => ToAzureAuthorityHost(Value);

private static Uri ToAzureAuthorityHost(string name) => name switch
{
"AzureGlobalCloud" => AzureAuthorityHosts.AzurePublicCloud,
"AzureChinaCloud" => AzureAuthorityHosts.AzureChina,
"AzureGermanCloud" => AzureAuthorityHosts.AzureGermany,
"AzureUSGovernment" => AzureAuthorityHosts.AzureGovernment,
_ => throw new InvalidOperationException($"ARM Environment {name} is not a known Azure Environment name.")
};
static Uri ToAzureAuthorityHost(string name) => name switch
{
"AzureGlobalCloud" => AzureAuthorityHosts.AzurePublicCloud,
"AzureChinaCloud" => AzureAuthorityHosts.AzureChina,
"AzureGermanCloud" => AzureAuthorityHosts.AzureGermany,
"AzureUSGovernment" => AzureAuthorityHosts.AzureGovernment,
_ => throw new InvalidOperationException($"ARM Environment {name} is not a known Azure Environment name.")
};
}
}
}
5 changes: 3 additions & 2 deletions source/Calamari.Azure/Calamari.Azure.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
<TargetFrameworks>net462;net6.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.12.0" />
<PackageReference Include="Azure.ResourceManager" Version="1.12.0" />
<PackageReference Include="Azure.Identity" Version="1.13.1" />
<PackageReference Include="Azure.ResourceManager" Version="1.13.0" />
<PackageReference Include="Azure.ResourceManager.AppService" Version="1.2.0" />
<PackageReference Include="Azure.ResourceManager.ContainerService" Version="1.2.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Calamari.CloudAccounts\Calamari.CloudAccounts.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Azure;
using Azure.ResourceManager.ContainerService;
using Azure.ResourceManager.Resources;
using Calamari.CloudAccounts;
using Calamari.Common.Features.Discovery;
using Calamari.Common.Plumbing.Logging;
using Microsoft.Rest.Azure;
using Newtonsoft.Json;

namespace Calamari.Azure.Kubernetes.Discovery
Expand Down Expand Up @@ -39,38 +41,41 @@ public override IEnumerable<KubernetesCluster> DiscoverClusters(string contextJs
Log.Verbose($" Subscription ID: {account.SubscriptionNumber}");
Log.Verbose($" Tenant ID: {account.TenantId}");
Log.Verbose($" Client ID: {account.ClientId}");
var azureClient = account.CreateAzureClient();

var discoveredClusters = new List<KubernetesCluster>();
var armClient = account.CreateArmClient();

// There appears to be an issue where the azure client returns stale data
// We need to upgrade this to use the newer SDK, but we need to upgrade to .NET 4.6.2 to support that.
var resourceGroups = azureClient.ResourceGroups.List();
//we don't care about resource groups that are being deleted
foreach (var resourceGroup in resourceGroups.Where(rg => rg.ProvisioningState != "Deleting"))
var discoveredClusters = new List<KubernetesCluster>();

var subscriptionResource = armClient.GetSubscriptionResource(SubscriptionResource.CreateResourceIdentifier(account.SubscriptionNumber));

var resourceGroups = subscriptionResource.GetResourceGroups().GetAll();//.GetAll("provisioningState ne 'Deleting'");

//we don't care about resource groups that are being deleted
foreach (var resourceGroupResource in resourceGroups.Where(rg => !string.Equals(rg.Data.ResourceGroupProvisioningState,"Deleting", StringComparison.OrdinalIgnoreCase)))
{
try
{
// There appears to be an issue where the azure client returns stale data
// to mitigate this, specifically for scenario's where the resource group doesn't exist anymore
// we specifically list the clusters in each resource group
var clusters = azureClient.KubernetesClusters.ListByResourceGroup(resourceGroup.Name);
var clusters = resourceGroupResource.GetContainerServiceManagedClusters().GetAll();


discoveredClusters.AddRange(
clusters
.Select(c => KubernetesCluster.CreateForAks(
$"aks/{account.SubscriptionNumber}/{c.ResourceGroupName}/{c.Name}",
c.Name,
c.ResourceGroupName,
$"aks/{account.SubscriptionNumber}/{resourceGroupResource.Data.Name}/{c.Data.Name}",
c.Data.Name,
resourceGroupResource.Data.Name,
accountId,
c.Tags.ToTargetTags())));
c.Data.Tags.ToTargetTags())));
}
catch (CloudException ex)
catch (RequestFailedException ex)
{
Log.Verbose($"Failed to list kubernetes clusters for resource group {resourceGroup.Name}. Response message: {ex.Message}, Status code: {ex.Response.StatusCode}");
Log.Verbose($"Failed to list kubernetes clusters for resource group {resourceGroupResource.Data.Name}. Response message: {ex.Message}, Status code: {ex.Status}");

// if the resource group was not found, we don't care and move on
if (ex.Response.StatusCode == HttpStatusCode.NotFound && ex.Message.StartsWith("Resource group"))
if (ex.Status == (int)HttpStatusCode.NotFound && ex.GetRawResponse()?.Content.ToString().StartsWith("Resource group") == true)
continue;

//throw in all other scenario's
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,13 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.12.0" />
<PackageReference Include="Azure.ResourceManager.Resources" Version="1.0.0-preview.2" />
<PackageReference Include="Azure.ResourceManager.Resources" Version="1.9.0" />
<PackageReference Include="Azure.ResourceManager.Storage" Version="1.1.1" />
<PackageReference Include="NSubstitute" Version="4.2.2" />
<PackageReference Include="nunit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.4.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.0" />
<PackageReference Include="Polly" Version="8.3.1" />
<PackageReference Include="System.Text.Json" Version="7.0.2" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ public async Task Execute(RunningDeployment context)
{
uploadFile = await packageProvider.ConvertToAzureSupportedFile(packageFileInfo);
}

uploadPath = uploadFile.FullName;
uploadFileNeedsCleaning = packageFileInfo.Extension != uploadFile.Extension;

Expand All @@ -156,14 +157,26 @@ public async Task Execute(RunningDeployment context)

//Need to check if site turn off
var scmPublishEnabled = await armClient.IsScmPublishEnabled(targetSite);

if (packageProvider.SupportsAsynchronousDeployment && FeatureToggle.AsynchronousAzureZipDeployFeatureToggle.IsEnabled(context.Variables))
{
await UploadZipAndPollAsync(account, publishingProfile, scmPublishEnabled, uploadPath, targetSite.ScmSiteAndSlot, packageProvider, pollingTimeout, asyncZipDeployTimeoutPolicy);
await UploadZipAndPollAsync(account,
publishingProfile,
scmPublishEnabled,
uploadPath,
targetSite.ScmSiteAndSlot,
packageProvider,
pollingTimeout,
asyncZipDeployTimeoutPolicy);
}
else
{
await UploadZipAsync(account, publishingProfile, scmPublishEnabled, uploadPath, targetSite.ScmSiteAndSlot, packageProvider);
await UploadZipAsync(account,
publishingProfile,
scmPublishEnabled,
uploadPath,
targetSite.ScmSiteAndSlot,
packageProvider);
}
}
finally
Expand Down Expand Up @@ -237,7 +250,7 @@ private async Task UploadZipAsync(IAzureAccount azureAccount,
#endif
using var streamContent = new StreamContent(fileStream);
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

//we have to create a new request message each time
var request = new HttpRequestMessage(HttpMethod.Post, zipUploadUrl)
{
Expand All @@ -259,7 +272,7 @@ private async Task UploadZipAsync(IAzureAccount azureAccount,
Log.Verbose("Finished deploying");
}

static async Task<AuthenticationHeaderValue> GetAuthenticationHeaderValue(IAzureAccount azureAccount, PublishingProfile publishingProfile, bool scmPublishEnabled)
async Task<AuthenticationHeaderValue> GetAuthenticationHeaderValue(IAzureAccount azureAccount, PublishingProfile publishingProfile, bool scmPublishEnabled)
{
AuthenticationHeaderValue authenticationHeader;
if (scmPublishEnabled)
Expand All @@ -268,7 +281,7 @@ static async Task<AuthenticationHeaderValue> GetAuthenticationHeaderValue(IAzure
}
else
{
var accessToken = await azureAccount.GetAccessTokenAsync();
var accessToken = await azureAccount.GetAuthorizationToken(Log, CancellationToken.None);
authenticationHeader = new AuthenticationHeaderValue("Bearer", accessToken);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
<PackageReference Include="Polly" Version="8.3.1" />
<PackageReference Include="SharpCompress" Version="0.37.2" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
<PackageReference Include="System.Text.Json" Version="6.0.1" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="Octopus.Data">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Rest.ClientRuntime" Version="2.3.23"/>
<PackageReference Include="Microsoft.Identity.Client" Version="4.65.0" />
<PackageReference Include="Microsoft.Azure.Management.ResourceManager" Version="3.9.0-preview"/>
<PackageReference Include="Azure.Identity" Version="1.12.0" />
<PackageReference Include="Azure.ResourceManager.Resources" Version="1.7.0" />
<PackageReference Include="Microsoft.Azure.Management.ResourceManager" Version="3.17.4-preview" />
<PackageReference Include="Microsoft.Rest.ClientRuntime" Version="2.3.24"/>
<PackageReference Include="Azure.ResourceManager.Resources" Version="1.9.0" />
<PackageReference Include="Microsoft.DotNet.Analyzers.Compatibility" Version="0.2.12-alpha">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
Expand Down
Loading