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

Add support for other clouds (pt. 2) #3452

Merged
merged 28 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
59cc0e1
Add cloud injection functionality
danieljurek Feb 28, 2024
a0ccbfd
Add cloud-awareness into auth
danieljurek Feb 28, 2024
2bc4d7a
Eliminate most hardcoded usage of URLs
danieljurek Mar 1, 2024
4f3c45f
Register cloud.PortalUrlBase
danieljurek Mar 1, 2024
8471b01
Blob fix
danieljurek Mar 1, 2024
2f51c1c
Add template changes
danieljurek Mar 1, 2024
da49f42
Fix issues in portal URL generation
danieljurek Mar 1, 2024
99790d2
Fix tests
danieljurek Mar 1, 2024
3df655e
Add error with suggestion
danieljurek Mar 1, 2024
2473231
Formatting
danieljurek Mar 1, 2024
283f0ee
cspell
danieljurek Mar 1, 2024
fe31a8f
Fix blob URL
danieljurek Mar 2, 2024
38cc483
Fix monitor URLs
danieljurek Mar 2, 2024
9e96551
Fix user_profile scopes
danieljurek Mar 2, 2024
bef6704
Remove TODO, this will be part of another refactor
danieljurek Mar 4, 2024
d34209e
Remove redundant WithPerCallPolicy from NewClientOptionsBuilder
danieljurek Mar 4, 2024
38d500b
Fix issue with ACA templates
danieljurek Mar 5, 2024
018338f
Log in if container registry is hosted in Azure
danieljurek Mar 5, 2024
2c35149
Integrate OneAuth changes
danieljurek Mar 5, 2024
0dd67ce
Formatting
danieljurek Mar 5, 2024
b5e37c7
Feedback from other PR
danieljurek Mar 5, 2024
926f686
Revert templates/, that will be covered in another PR
danieljurek Mar 5, 2024
22a33c3
Revert schema changes
danieljurek Mar 5, 2024
6543105
Review feedback
danieljurek Mar 5, 2024
f662178
More review feedback
danieljurek Mar 6, 2024
5517ac3
Review feedback
danieljurek Mar 6, 2024
fc4e533
Updates recording for devcenter test
wbreza Mar 6, 2024
ad54544
Auth review feedback
danieljurek Mar 6, 2024
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
3 changes: 3 additions & 0 deletions cli/azd/.vscode/cspell.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ words:
- nosec
- Retryable
- runcontext
- azcloud
- usgovcloudapi
- chinacloudapi
languageSettings:
- languageId: go
ignoreRegExpList:
Expand Down
2 changes: 1 addition & 1 deletion cli/azd/cmd/auth_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ const cLoginSuccessMessage = "Logged in to Azure."

func (la *loginAction) Run(ctx context.Context) (*actions.ActionResult, error) {
if len(la.flags.scopes) == 0 {
la.flags.scopes = auth.LoginScopes
la.flags.scopes = la.authManager.LoginScopes()
}

if la.annotations[loginCmdParentAnnotation] == "" {
Expand Down
6 changes: 5 additions & 1 deletion cli/azd/cmd/auth_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/azure/azure-dev/cli/azd/internal"
"github.com/azure/azure-dev/cli/azd/pkg/account"
"github.com/azure/azure-dev/cli/azd/pkg/auth"
"github.com/azure/azure-dev/cli/azd/pkg/cloud"
"github.com/azure/azure-dev/cli/azd/pkg/contracts"
"github.com/azure/azure-dev/cli/azd/pkg/environment"
"github.com/azure/azure-dev/cli/azd/pkg/output"
Expand Down Expand Up @@ -57,6 +58,7 @@ type authTokenAction struct {
envResolver environment.EnvironmentResolver
subResolver account.SubscriptionTenantResolver
flags *authTokenFlags
cloud *cloud.Cloud
}

func newAuthTokenAction(
Expand All @@ -66,6 +68,7 @@ func newAuthTokenAction(
flags *authTokenFlags,
envResolver environment.EnvironmentResolver,
subResolver account.SubscriptionTenantResolver,
cloud *cloud.Cloud,
) actions.Action {
return &authTokenAction{
credentialProvider: credentialProvider,
Expand All @@ -74,6 +77,7 @@ func newAuthTokenAction(
formatter: formatter,
writer: writer,
flags: flags,
cloud: cloud,
}
}

Expand Down Expand Up @@ -125,7 +129,7 @@ func getTenantIdFromEnv(

func (a *authTokenAction) Run(ctx context.Context) (*actions.ActionResult, error) {
if len(a.flags.scopes) == 0 {
a.flags.scopes = auth.LoginScopes
a.flags.scopes = auth.LoginScopes(a.cloud)
}

var cred azcore.TokenCredential
Expand Down
24 changes: 17 additions & 7 deletions cli/azd/cmd/auth_token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/azure/azure-dev/cli/azd/internal"
"github.com/azure/azure-dev/cli/azd/pkg/auth"
"github.com/azure/azure-dev/cli/azd/pkg/azure"
"github.com/azure/azure-dev/cli/azd/pkg/cloud"
"github.com/azure/azure-dev/cli/azd/pkg/contracts"
"github.com/azure/azure-dev/cli/azd/pkg/environment"
"github.com/azure/azure-dev/cli/azd/pkg/output"
"github.com/stretchr/testify/require"
)

const cManagementScope = "https://management.azure.com//.default"
danieljurek marked this conversation as resolved.
Show resolved Hide resolved

func TestAuthToken(t *testing.T) {
wasCalled := false
buf := &bytes.Buffer{}
Expand All @@ -32,7 +34,7 @@ func TestAuthToken(t *testing.T) {
wasCalled = true

// Default value when explicit scopes are not provided to the command.
require.ElementsMatch(t, []string{azure.ManagementScope}, options.Scopes)
require.ElementsMatch(t, []string{cManagementScope}, options.Scopes)

return azcore.AccessToken{
Token: "ABC123",
Expand All @@ -49,6 +51,7 @@ func TestAuthToken(t *testing.T) {
return nil, fmt.Errorf("not an azd env directory")
},
&mockSubscriptionTenantResolver{},
cloud.AzurePublic(),
)

_, err := a.Run(context.Background())
Expand All @@ -67,7 +70,7 @@ func TestAuthTokenSysEnv(t *testing.T) {
buf := &bytes.Buffer{}

token := authTokenFn(func(ctx context.Context, options policy.TokenRequestOptions) (azcore.AccessToken, error) {
require.ElementsMatch(t, []string{azure.ManagementScope}, options.Scopes)
require.ElementsMatch(t, []string{cManagementScope}, options.Scopes)
return azcore.AccessToken{
Token: "ABC123",
ExpiresOn: time.Unix(1669153000, 0).UTC(),
Expand All @@ -91,6 +94,7 @@ func TestAuthTokenSysEnv(t *testing.T) {
&mockSubscriptionTenantResolver{
TenantId: expectedTenant,
},
cloud.AzurePublic(),
)

_, err := a.Run(context.Background())
Expand All @@ -108,7 +112,7 @@ func TestAuthTokenSysEnvError(t *testing.T) {
buf := &bytes.Buffer{}

token := authTokenFn(func(ctx context.Context, options policy.TokenRequestOptions) (azcore.AccessToken, error) {
require.ElementsMatch(t, []string{azure.ManagementScope}, options.Scopes)
require.ElementsMatch(t, []string{cManagementScope}, options.Scopes)
return azcore.AccessToken{
Token: "ABC123",
ExpiresOn: time.Unix(1669153000, 0).UTC(),
Expand Down Expand Up @@ -138,6 +142,7 @@ func TestAuthTokenSysEnvError(t *testing.T) {
&mockSubscriptionTenantResolver{
Err: fmt.Errorf(expectedError),
},
cloud.AzurePublic(),
)

_, err := a.Run(context.Background())
Expand All @@ -155,7 +160,7 @@ func TestAuthTokenAzdEnvError(t *testing.T) {
buf := &bytes.Buffer{}

token := authTokenFn(func(ctx context.Context, options policy.TokenRequestOptions) (azcore.AccessToken, error) {
require.ElementsMatch(t, []string{azure.ManagementScope}, options.Scopes)
require.ElementsMatch(t, []string{cManagementScope}, options.Scopes)
return azcore.AccessToken{
Token: "ABC123",
ExpiresOn: time.Unix(1669153000, 0).UTC(),
Expand All @@ -181,6 +186,7 @@ func TestAuthTokenAzdEnvError(t *testing.T) {
&mockSubscriptionTenantResolver{
Err: fmt.Errorf(expectedError),
},
cloud.AzurePublic(),
)

_, err := a.Run(context.Background())
Expand All @@ -198,7 +204,7 @@ func TestAuthTokenAzdEnv(t *testing.T) {
buf := &bytes.Buffer{}

token := authTokenFn(func(ctx context.Context, options policy.TokenRequestOptions) (azcore.AccessToken, error) {
require.ElementsMatch(t, []string{azure.ManagementScope}, options.Scopes)
require.ElementsMatch(t, []string{cManagementScope}, options.Scopes)
return azcore.AccessToken{
Token: "ABC123",
ExpiresOn: time.Unix(1669153000, 0).UTC(),
Expand All @@ -221,6 +227,7 @@ func TestAuthTokenAzdEnv(t *testing.T) {
&mockSubscriptionTenantResolver{
TenantId: expectedTenant,
},
cloud.AzurePublic(),
)

_, err := a.Run(context.Background())
Expand All @@ -238,7 +245,7 @@ func TestAuthTokenAzdEnvWithEmpty(t *testing.T) {
buf := &bytes.Buffer{}

token := authTokenFn(func(ctx context.Context, options policy.TokenRequestOptions) (azcore.AccessToken, error) {
require.ElementsMatch(t, []string{azure.ManagementScope}, options.Scopes)
require.ElementsMatch(t, []string{cManagementScope}, options.Scopes)
return azcore.AccessToken{
Token: "ABC123",
ExpiresOn: time.Unix(1669153000, 0).UTC(),
Expand All @@ -261,6 +268,7 @@ func TestAuthTokenAzdEnvWithEmpty(t *testing.T) {
&mockSubscriptionTenantResolver{
TenantId: expectedTenant,
},
cloud.AzurePublic(),
)

_, err := a.Run(context.Background())
Expand Down Expand Up @@ -297,6 +305,7 @@ func TestAuthTokenCustomScopes(t *testing.T) {
return nil, fmt.Errorf("not an azd env directory")
},
&mockSubscriptionTenantResolver{},
cloud.AzurePublic(),
)

_, err := a.Run(context.Background())
Expand All @@ -318,6 +327,7 @@ func TestAuthTokenFailure(t *testing.T) {
return nil, fmt.Errorf("not an azd env directory")
},
&mockSubscriptionTenantResolver{},
cloud.AzurePublic(),
)

_, err := a.Run(context.Background())
Expand Down
94 changes: 93 additions & 1 deletion cli/azd/cmd/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/azure/azure-dev/cli/azd/pkg/azapi"
"github.com/azure/azure-dev/cli/azd/pkg/azd"
"github.com/azure/azure-dev/cli/azd/pkg/azsdk"
"github.com/azure/azure-dev/cli/azd/pkg/cloud"
"github.com/azure/azure-dev/cli/azd/pkg/config"
"github.com/azure/azure-dev/cli/azd/pkg/containerapps"
"github.com/azure/azure-dev/cli/azd/pkg/devcenter"
Expand Down Expand Up @@ -378,11 +379,102 @@ func registerCommonDependencies(container *ioc.NestedContainer) {
},
)

container.MustRegisterSingleton(func(
ctx context.Context,
userConfigManager config.UserConfigManager,
lazyProjectConfig *lazy.Lazy[*project.ProjectConfig],
lazyAzdContext *lazy.Lazy[*azdcontext.AzdContext],
lazyLocalEnvStore *lazy.Lazy[environment.LocalDataStore],
) (*cloud.Cloud, error) {

// Precedence for cloud configuration:
// 1. Local environment config (.azure/<environment>/config.json)
// 2. Project config (azure.yaml)
// 3. User config (~/.azure/config.json)
// Default if no cloud configured: Azure Public Cloud

validClouds := fmt.Sprintf(
"Valid cloud names are '%s', '%s', '%s'.",
cloud.AzurePublicName,
cloud.AzureChinaCloudName,
cloud.AzureUSGovernmentName,
)

// Local Environment Configuration (.azure/<environment>/config.json)
localEnvStore, _ := lazyLocalEnvStore.GetValue()
if azdCtx, err := lazyAzdContext.GetValue(); err == nil {
if azdCtx != nil && localEnvStore != nil {
if defaultEnvName, err := azdCtx.GetDefaultEnvironmentName(); err == nil {
if env, err := localEnvStore.Get(ctx, defaultEnvName); err == nil {
if cloudConfigurationNode, exists := env.Config.Get(cloud.ConfigPath); exists {
if value, err := cloud.ParseCloudConfig(cloudConfigurationNode); err == nil {
cloudConfig, err := cloud.NewCloud(value)
if err == nil {
return cloudConfig, nil
}

return nil, &azcli.ErrorWithSuggestion{
Err: err,
Suggestion: fmt.Sprintf(
"Set the cloud configuration by editing the 'cloud' node in the config.json file for the %s environment\n%s",
defaultEnvName,
validClouds,
),
}
}
}
}
}
}
}

// Project Configuration (azure.yaml)
projConfig, err := lazyProjectConfig.GetValue()
if err == nil && projConfig != nil && projConfig.Cloud != nil {
if value, err := cloud.ParseCloudConfig(projConfig.Cloud); err == nil {
if cloudConfig, err := cloud.ParseCloudConfig(value); err == nil {
if cloud, err := cloud.NewCloud(cloudConfig); err == nil {
return cloud, nil
} else {
return nil, &azcli.ErrorWithSuggestion{
Err: err,
//nolint:lll
Suggestion: fmt.Sprintf("Set the cloud configuration by editing the 'cloud' node in the project YAML file\n%s", validClouds),
}
}
}
}
}

// User Configuration (~/.azure/config.json)
if azdConfig, err := userConfigManager.Load(); err == nil {
if cloudConfigNode, exists := azdConfig.Get(cloud.ConfigPath); exists {
if value, err := cloud.ParseCloudConfig(cloudConfigNode); err == nil {
if cloud, err := cloud.NewCloud(value); err == nil {
return cloud, nil
} else {
return nil, &azcli.ErrorWithSuggestion{
Err: err,
Suggestion: fmt.Sprintf("Set the cloud configuration using 'azd config set cloud.name <name>'.\n%s", validClouds),
}
}
}
}
}

return cloud.NewCloud(&cloud.Config{Name: cloud.AzurePublicName})
})

container.MustRegisterSingleton(func(cloud *cloud.Cloud) cloud.PortalUrlBase {
return cloud.PortalUrlBase
})

container.MustRegisterSingleton(func(
httpClient httputil.HttpClient,
userAgent httputil.UserAgent,
cloud *cloud.Cloud,
) *azsdk.ClientOptionsBuilderFactory {
return azsdk.NewClientOptionsBuilderFactory(httpClient, string(userAgent))
return azsdk.NewClientOptionsBuilderFactory(httpClient, string(userAgent), cloud)
})

container.MustRegisterSingleton(func(
Expand Down
10 changes: 7 additions & 3 deletions cli/azd/cmd/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/azure/azure-dev/cli/azd/pkg/account"
"github.com/azure/azure-dev/cli/azd/pkg/azapi"
"github.com/azure/azure-dev/cli/azd/pkg/azure"
"github.com/azure/azure-dev/cli/azd/pkg/cloud"
"github.com/azure/azure-dev/cli/azd/pkg/environment"
"github.com/azure/azure-dev/cli/azd/pkg/environment/azdcontext"
"github.com/azure/azure-dev/cli/azd/pkg/infra"
Expand Down Expand Up @@ -66,6 +67,7 @@ type monitorAction struct {
deploymentOperations azapi.DeploymentOperations
console input.Console
flags *monitorFlags
portalUrlBase string
}

func newMonitorAction(
Expand All @@ -76,6 +78,7 @@ func newMonitorAction(
deploymentOperations azapi.DeploymentOperations,
console input.Console,
flags *monitorFlags,
portalUrlBase cloud.PortalUrlBase,
) actions.Action {
return &monitorAction{
azdCtx: azdCtx,
Expand All @@ -85,6 +88,7 @@ func newMonitorAction(
console: console,
flags: flags,
subResolver: subResolver,
portalUrlBase: string(portalUrlBase),
}
}

Expand Down Expand Up @@ -142,19 +146,19 @@ func (m *monitorAction) Run(ctx context.Context) (*actions.ActionResult, error)
for _, insightsResource := range insightsResources {
if m.flags.monitorLive {
openWithDefaultBrowser(ctx, m.console,
fmt.Sprintf("https://app.azure.com/%s%s/quickPulse", tenantId, insightsResource.Id))
fmt.Sprintf("%s/#@%s/resource%s/quickPulse", m.portalUrlBase, tenantId, insightsResource.Id))
}

if m.flags.monitorLogs {
openWithDefaultBrowser(ctx, m.console,
fmt.Sprintf("https://app.azure.com/%s%s/logs", tenantId, insightsResource.Id))
fmt.Sprintf("%s/#@%s/resource%s/logs", m.portalUrlBase, tenantId, insightsResource.Id))
}
}

for _, portalResource := range portalResources {
if m.flags.monitorOverview {
openWithDefaultBrowser(ctx, m.console,
fmt.Sprintf("https://portal.azure.com/#@%s/dashboard/arm%s", tenantId, portalResource.Id))
fmt.Sprintf("%s/#@%s/dashboard/arm%s", m.portalUrlBase, tenantId, portalResource.Id))
}
}

Expand Down
Loading
Loading