Skip to content
Open
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
13 changes: 12 additions & 1 deletion src/code/RegisterPSResourceRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ private PSRepositoryInfo RepoValidationHelper(Hashtable repo)
return null;
}

if (repo["Name"].ToString().Equals("PSGallery"))
if (repo["Name"].ToString().Equals("PSGallery", StringComparison.OrdinalIgnoreCase))
{
WriteError(new ErrorRecord(
new PSInvalidOperationException("Cannot register PSGallery with -Name parameter. Try: Register-PSResourceRepository -PSGallery"),
Expand All @@ -334,6 +334,17 @@ private PSRepositoryInfo RepoValidationHelper(Hashtable repo)
return null;
}

if (repo["Name"].ToString().Equals("MAR", StringComparison.OrdinalIgnoreCase))
{
WriteError(new ErrorRecord(
new PSInvalidOperationException("Cannot register MAR with -Name parameter. The MAR repository is automatically registered. Try: Reset-PSResourceRepository to restore default repositories."),
"MARProvidedAsNameRepoPSet",
ErrorCategory.InvalidArgument,
this));

return null;
}

if (!repo.ContainsKey("Uri") || repo["Uri"] == null || String.IsNullOrEmpty(repo["Uri"].ToString()))
{
WriteError(new ErrorRecord(
Expand Down
32 changes: 32 additions & 0 deletions src/code/RepositorySettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,12 @@ internal static class RepositorySettings
// The repository store file's location is currently only at '%LOCALAPPDATA%\PSResourceGet' for the user account.
private const string PSGalleryRepoName = "PSGallery";
private const string PSGalleryRepoUri = "https://www.powershellgallery.com/api/v2";
private const string MARRepoName = "MAR";
private const string MARRepoUri = "https://mcr.microsoft.com";
private const int DefaultPriority = 50;
private const int MARDefaultPriority = 40;
private const bool DefaultTrusted = false;
private const bool MARDefaultTrusted = true;
private const string RepositoryFileName = "PSResourceRepository.xml";
private static readonly string RepositoryPath = Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.LocalApplicationData), "PSResourceGet");
Expand Down Expand Up @@ -63,6 +67,10 @@ public static void CheckRepositoryStore()
// Add PSGallery to the newly created store
Uri psGalleryUri = new Uri(PSGalleryRepoUri);
Add(PSGalleryRepoName, psGalleryUri, DefaultPriority, DefaultTrusted, repoCredentialInfo: null, repoCredentialProvider: CredentialProviderType.None, APIVersion.V2, force: false);

// Add MAR to the newly created store
Uri marUri = new Uri(MARRepoUri);
Add(MARRepoName, marUri, MARDefaultPriority, MARDefaultTrusted, repoCredentialInfo: null, repoCredentialProvider: CredentialProviderType.None, APIVersion.ContainerRegistry, force: false);
}

// Open file (which should exist now), if cannot/is corrupted then throw error
Expand All @@ -85,6 +93,12 @@ public static PSRepositoryInfo AddRepository(string repoName, Uri repoUri, int r
return null;
}

if (repoName.Equals("MAR", StringComparison.OrdinalIgnoreCase))
{
errorMsg = "Cannot register MAR with -Name parameter. The MAR repository is automatically registered. Try: Reset-PSResourceRepository to restore default repositories.";
return null;
}

return AddToRepositoryStore(repoName, repoUri, repoPriority, repoTrusted, apiVersion, repoCredentialInfo, repoCredentialProvider, force, cmdletPassedIn, out errorMsg);
}

Expand Down Expand Up @@ -187,6 +201,20 @@ public static PSRepositoryInfo UpdateRepositoryStore(string repoName, Uri repoUr
return null;
}

// check MAR Uri is not trying to be set
if (repoName.Equals("MAR", StringComparison.OrdinalIgnoreCase) && repoUri != null)
{
errorMsg = "The MAR repository has a predefined Uri. Setting the -Uri parameter for this repository is not allowed. Please run 'Reset-PSResourceRepository' to restore default repositories.";
return null;
}

// check MAR CredentialInfo is not trying to be set
if (repoName.Equals("MAR", StringComparison.OrdinalIgnoreCase) && repoCredentialInfo != null)
{
errorMsg = "Setting the -CredentialInfo parameter for MAR is not allowed. Run 'Reset-PSResourceRepository' to restore default repositories.";
return null;
}

// determine trusted value to pass in (true/false if set, null otherwise, hence the nullable bool variable)
bool? _trustedNullable = isSet ? new bool?(repoTrusted) : new bool?();

Expand Down Expand Up @@ -908,6 +936,10 @@ public static PSRepositoryInfo Reset(out string errorMsg)
Uri psGalleryUri = new Uri(PSGalleryRepoUri);
PSRepositoryInfo psGalleryRepo = Add(PSGalleryRepoName, psGalleryUri, DefaultPriority, DefaultTrusted, repoCredentialInfo: null, repoCredentialProvider: CredentialProviderType.None, APIVersion.V2, force: false);

// Add MAR to the newly created store
Uri marUri = new Uri(MARRepoUri);
Add(MARRepoName, marUri, MARDefaultPriority, MARDefaultTrusted, repoCredentialInfo: null, repoCredentialProvider: CredentialProviderType.None, APIVersion.ContainerRegistry, force: false);

// Clean up backup file on success
if (!string.IsNullOrEmpty(backupFilePath) && File.Exists(backupFilePath))
{
Expand Down
8 changes: 4 additions & 4 deletions src/code/ResetPSResourceRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Microsoft.PowerShell.PSResourceGet.Cmdlets
/// <summary>
/// The Reset-PSResourceRepository cmdlet resets the repository store by creating a new PSRepositories.xml file.
/// This is useful when the repository store becomes corrupted.
/// It will create a new repository store with only the PSGallery repository registered.
/// It will create a new repository store with PSGallery and MAR repositories registered.
/// </summary>
[Cmdlet(VerbsCommon.Reset,
"PSResourceRepository",
Expand Down Expand Up @@ -39,8 +39,8 @@ protected override void ProcessRecord()
"PSResourceRepository.xml");

WriteVerbose($"Resetting repository store at: {repositoryStorePath}");
if (!ShouldProcess(repositoryStorePath, "Reset repository store and create new PSRepositories.xml file with PSGallery registered"))

if (!ShouldProcess(repositoryStorePath, "Reset repository store and create new PSRepositories.xml file with PSGallery and MAR registered"))
{
return;
}
Expand All @@ -57,7 +57,7 @@ protected override void ProcessRecord()
return;
}

WriteVerbose("Repository store reset successfully. PSGallery has been registered.");
WriteVerbose("Repository store reset successfully. PSGallery and MAR have been registered.");

if (PassThru)
{
Expand Down
3 changes: 2 additions & 1 deletion src/code/SetPSResourceRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,11 @@ public SwitchParameter Trusted
public object GetDynamicParameters()
{
PSRepositoryInfo repository = RepositorySettings.Read(new[] { Name }, out string[] _).FirstOrDefault();
// Dynamic parameter '-CredentialProvider' should not appear for PSGallery, or any container registry repository.
// Dynamic parameter '-CredentialProvider' should not appear for PSGallery, MAR, or any container registry repository.
// It should also not appear when using the 'Repositories' parameter set.
if (repository is not null &&
(repository.Name.Equals("PSGallery", StringComparison.OrdinalIgnoreCase) ||
repository.Name.Equals("MAR", StringComparison.OrdinalIgnoreCase) ||
ParameterSetName.Equals(RepositoriesParameterSet) ||
repository.IsContainerRegistry()))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,13 +273,6 @@ Describe 'Test HTTP Find-PSResource for ACR Server Protocol' -tags 'CI' {
}

Describe 'Test Find-PSResource for MAR Repository' -tags 'CI' {
BeforeAll {
Register-PSResourceRepository -Name "MAR" -Uri "https://mcr.microsoft.com" -ApiVersion "ContainerRegistry"
}

AfterAll {
Unregister-PSResourceRepository -Name "MAR"
}

It "Should find resource given specific Name, Version null" {
$res = Find-PSResource -Name "Az.Accounts" -Repository "MAR"
Expand Down Expand Up @@ -313,10 +306,10 @@ Describe 'Test Find-PSResource for MAR Repository' -tags 'CI' {
It "Should find version range for Az dependencies" {
# Target known version to know the output from the API won't change
$res = Find-PSResource -Repository 'MAR' -Name 'Az' -Version '14.4.0'

# Version defined by "ModuleVersion"
$res.Dependencies.Where{$_.'Name' -eq 'Az.Accounts'}.'VersionRange'.ToString() | Should -Be '[5.3.0, )'

# Version defined by "RequiredVersion"
$res.Dependencies.Where{$_.'Name' -eq 'Az.Resources'}.'VersionRange'.ToString() | Should -Be '[8.1.0, 8.1.0]'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,15 +351,6 @@ Describe 'Test Install-PSResource for Container Registry scenarios - Manual Vali
}

Describe 'Test Install-PSResource for MAR Repository' -tags 'CI' {
BeforeAll {
[Microsoft.PowerShell.PSResourceGet.UtilClasses.InternalHooks]::SetTestHook("MARPrefix", "azure-powershell/");
Register-PSResourceRepository -Name "MAR" -Uri "https://mcr.microsoft.com" -ApiVersion "ContainerRegistry"
}

AfterAll {
[Microsoft.PowerShell.PSResourceGet.UtilClasses.InternalHooks]::SetTestHook("MARPrefix", $null);
Unregister-PSResourceRepository -Name "MAR"
}

It "Should find resource given specific Name, Version null" {
try {
Expand Down
13 changes: 13 additions & 0 deletions test/PSGetTestUtils.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ $script:IsCoreCLR = $PSVersionTable.ContainsKey('PSEdition') -and $PSVersionTabl
$script:PSGalleryName = 'PSGallery'
$script:PSGalleryLocation = 'https://www.powershellgallery.com/api/v2'

$script:MARName = 'MAR'
$script:MARLocation = 'https://mcr.microsoft.com'

$script:NuGetGalleryName = 'NuGetGallery'
$script:NuGetGalleryLocation = 'https://api.nuget.org/v3/index.json'

Expand Down Expand Up @@ -153,6 +156,16 @@ function Get-PSGalleryName
function Get-PSGalleryLocation {
return $script:PSGalleryLocation
}

function Get-MARName
{
return $script:MARName
}

function Get-MARLocation {
return $script:MARLocation
}

function Get-NewTestDirs {
Param(
[string[]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -590,15 +590,6 @@ Describe "Test Publish-PSResource" -tags 'CI' {
}

Describe 'Test Publish-PSResource for MAR Repository' -tags 'CI' {
BeforeAll {
[Microsoft.PowerShell.PSResourceGet.UtilClasses.InternalHooks]::SetTestHook("MARPrefix", "azure-powershell/");
Register-PSResourceRepository -Name "MAR" -Uri "https://mcr.microsoft.com" -ApiVersion "ContainerRegistry"
}

AfterAll {
[Microsoft.PowerShell.PSResourceGet.UtilClasses.InternalHooks]::SetTestHook("MARPrefix", $null);
Unregister-PSResourceRepository -Name "MAR"
}

It "Should find resource given specific Name, Version null" {
$fileName = "NonExistent.psd1"
Expand Down
139 changes: 139 additions & 0 deletions test/ResourceRepositoryTests/MARRepository.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

$modPath = "$psscriptroot/../PSGetTestUtils.psm1"
Write-Verbose -Verbose -Message "PSGetTestUtils path: $modPath"
Import-Module $modPath -Force -Verbose

Describe "Test MAR Repository Registration" -tags 'CI' {
BeforeEach {
$MARName = Get-MARName
$MARUri = Get-MARLocation
$PSGalleryName = Get-PSGalleryName
$PSGalleryUri = Get-PSGalleryLocation
Get-NewPSResourceRepositoryFile
}
AfterEach {
Get-RevertPSResourceRepositoryFile
}

Context "MAR is auto-registered with expected values" {
It "MAR repository should be present with expected default values" {
$res = Get-PSResourceRepository -Name $MARName
$res | Should -Not -BeNullOrEmpty
$res.Name | Should -Be $MARName
$res.Uri | Should -Be "$MARUri/"
$res.Trusted | Should -Be True
$res.Priority | Should -Be 40
$res.ApiVersion | Should -Be 'ContainerRegistry'
}

It "MAR repository should have lower priority number (higher priority) than PSGallery" {
$mar = Get-PSResourceRepository -Name $MARName
$psGallery = Get-PSResourceRepository -Name $PSGalleryName
$mar.Priority | Should -BeLessThan $psGallery.Priority
}
}

Context "MAR name protection" {
It "should not allow registering MAR with -Name parameter" {
{ Register-PSResourceRepository -Name "MAR" -Uri "https://mcr.microsoft.com" -ErrorAction Stop } | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PSResourceGet.Cmdlets.RegisterPSResourceRepository"
}

It "should not allow registering MAR (case insensitive) with -Name parameter" {
{ Register-PSResourceRepository -Name "mar" -Uri "https://mcr.microsoft.com" -ErrorAction Stop } | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PSResourceGet.Cmdlets.RegisterPSResourceRepository"
}

It "should not allow registering MAR with -Name parameter in hashtable" {
Unregister-PSResourceRepository -Name $MARName
$hashtable = @{Name = "MAR"; Uri = "https://mcr.microsoft.com"}
Register-PSResourceRepository -Repository $hashtable -ErrorVariable err -ErrorAction SilentlyContinue
$err.Count | Should -BeGreaterThan 0
$err[0].FullyQualifiedErrorId | Should -BeExactly "MARProvidedAsNameRepoPSet,Microsoft.PowerShell.PSResourceGet.Cmdlets.RegisterPSResourceRepository"
}

It "should not allow setting Uri for MAR repository" {
{ Set-PSResourceRepository -Name $MARName -Uri "https://example.com" -ErrorAction Stop } | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PSResourceGet.Cmdlets.SetPSResourceRepository"
}

It "should not allow setting CredentialInfo for MAR repository" {
$randomSecret = [System.IO.Path]::GetRandomFileName()
$credentialInfo = New-Object Microsoft.PowerShell.PSResourceGet.UtilClasses.PSCredentialInfo ("testvault", $randomSecret)
{ Set-PSResourceRepository -Name $MARName -CredentialInfo $credentialInfo -ErrorAction Stop } | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PSResourceGet.Cmdlets.SetPSResourceRepository"
}

It "should allow setting Trusted for MAR repository" {
Set-PSResourceRepository -Name $MARName -Trusted:$false
$res = Get-PSResourceRepository -Name $MARName
$res.Trusted | Should -Be False
}

It "should allow setting Priority for MAR repository" {
Set-PSResourceRepository -Name $MARName -Priority 10
$res = Get-PSResourceRepository -Name $MARName
$res.Priority | Should -Be 10
}
}

Context "Reset repository store includes MAR" {
It "Reset-PSResourceRepository should register MAR alongside PSGallery" {
Reset-PSResourceRepository
$res = Get-PSResourceRepository -Name $MARName
$res | Should -Not -BeNullOrEmpty
$res.Name | Should -Be $MARName
$res.Uri | Should -Be "$MARUri/"
$res.Trusted | Should -Be True
$res.Priority | Should -Be 40
$res.ApiVersion | Should -Be 'ContainerRegistry'

$psGallery = Get-PSResourceRepository -Name $PSGalleryName
$psGallery | Should -Not -BeNullOrEmpty
}

It "Reset-PSResourceRepository should restore MAR after unregistration" {
Unregister-PSResourceRepository -Name $MARName
$res = Get-PSResourceRepository -Name $MARName -ErrorAction SilentlyContinue
$res | Should -BeNullOrEmpty

Reset-PSResourceRepository
$res = Get-PSResourceRepository -Name $MARName
$res | Should -Not -BeNullOrEmpty
$res.Name | Should -Be $MARName
$res.Uri | Should -Be "$MARUri/"
$res.Trusted | Should -Be True
$res.Priority | Should -Be 40
}

It "Reset-PSResourceRepository should restore both PSGallery and MAR" {
Unregister-PSResourceRepository -Name $MARName
Unregister-PSResourceRepository -Name $PSGalleryName
Reset-PSResourceRepository

$mar = Get-PSResourceRepository -Name $MARName
$mar | Should -Not -BeNullOrEmpty
$mar.Priority | Should -Be 40
$mar.Trusted | Should -Be True
$mar.ApiVersion | Should -Be 'ContainerRegistry'

$psGallery = Get-PSResourceRepository -Name $PSGalleryName
$psGallery | Should -Not -BeNullOrEmpty
$psGallery.Priority | Should -Be 50
}
}

Context "MAR first due to higher priority" {
It "Find-PSResource Az.Accounts module from MAR" {
$res = Find-PSResource -Name "Az.Accounts"
$res | Should -Not -BeNullOrEmpty
$res.Name | Should -Be "Az.Accounts"
$res.Repository | Should -Be $MARName
}

It 'Find-PSResource fallback to PSGallery if module not in MAR' {
$res = Find-PSResource -Name "Pscx"
$res | Should -Not -BeNullOrEmpty
$res.Name | Should -Be "Pscx"
$res.Repository | Should -Be $PSGalleryName
}
}
}
Loading
Loading