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

Track2 sdk: fetch acr login server and storage suffix from env/arm metadata service #7784

Merged
Merged
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
6 changes: 5 additions & 1 deletion pkg/azclient/arm_conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ type ARMClientConfig struct {
CloudProviderBackoffRetries int32 `json:"cloudProviderBackoffRetries,omitempty" yaml:"cloudProviderBackoffRetries,omitempty"`
// Backoff duration
CloudProviderBackoffDuration int `json:"cloudProviderBackoffDuration,omitempty" yaml:"cloudProviderBackoffDuration,omitempty"`
//Storage suffix
StorageSuffix *string `json:"storageSuffix,omitempty" yaml:"storageSuffix,omitempty"`
//ACRLoginServer
ACRLoginServer *string `json:"acrLoginServer,omitempty" yaml:"containerRegistrySuffix,omitempty"`
}

func (config *ARMClientConfig) GetTenantID() string {
Expand All @@ -65,7 +69,7 @@ func GetAzCoreClientOption(armConfig *ARMClientConfig) (*policy.ClientOptions, e
azCoreClientConfig.PerCallPolicies = append(azCoreClientConfig.PerCallPolicies, useragent.NewCustomUserAgentPolicy(userAgent))
}
//set cloud
cloudConfig, err := GetAzureCloudConfig(armConfig)
cloudConfig, err := GetAzureCloudConfigAndBackfillARMClientConfig(armConfig)
if err != nil {
return nil, err
}
Expand Down
1 change: 1 addition & 0 deletions pkg/azclient/backendaddresspoolclient/custom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func init() {
_, err = pollerResp.PollUntilDone(ctx, nil)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
pippollerResp, err := publicIPClient.BeginDelete(ctx, resourceGroupName, publicIPName, nil)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
_, err = pippollerResp.PollUntilDone(ctx, nil)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
}
Expand Down
12 changes: 6 additions & 6 deletions pkg/azclient/client-gen/generator/clientfactory.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ var AbstractClientFactoryImplTemplate = template.Must(template.New("object-facto
`
type ClientFactoryImpl struct {
armConfig *ARMClientConfig
facotryConfig *ClientFactoryConfig
factoryConfig *ClientFactoryConfig
cred azcore.TokenCredential
clientOptionsMutFn []func(option *arm.ClientOptions)
{{range $key, $client := . -}}
Expand All @@ -211,7 +211,7 @@ func NewClientFactory(config *ClientFactoryConfig, armConfig *ARMClientConfig, c

factory := &ClientFactoryImpl{
armConfig: armConfig,
facotryConfig: config,
factoryConfig: config,
cred: cred,
clientOptionsMutFn: clientOptionsMutFn,
}
Expand Down Expand Up @@ -240,7 +240,7 @@ func NewClientFactory(config *ClientFactoryConfig, armConfig *ARMClientConfig, c
{{- end }}
func (factory *ClientFactoryImpl) create{{$resource}}Client(subscription string)({{.PkgAlias}}.{{.InterfaceTypeName}},error) {
//initialize {{.PkgAlias}}
options, err := GetDefaultResourceClientOption(factory.armConfig, factory.facotryConfig)
options, err := GetDefaultResourceClientOption(factory.armConfig, factory.factoryConfig)
if err != nil {
return nil, err
}
Expand All @@ -251,7 +251,7 @@ func (factory *ClientFactoryImpl) create{{$resource}}Client(subscription string)
{{- end }}
{{with $client.RateLimitKey}}
//add ratelimit policy
ratelimitOption := factory.facotryConfig.GetRateLimitConfig("{{.}}")
ratelimitOption := factory.factoryConfig.GetRateLimitConfig("{{.}}")
rateLimitPolicy := ratelimit.NewRateLimitPolicy(ratelimitOption)
if rateLimitPolicy != nil {
options.ClientOptions.PerCallPolicies = append(options.ClientOptions.PerCallPolicies, rateLimitPolicy)
Expand All @@ -266,12 +266,12 @@ func (factory *ClientFactoryImpl) create{{$resource}}Client(subscription string)
}
{{ if $client.CrossSubFactory }}
func (factory *ClientFactoryImpl) Get{{$resource}}Client(){{.PkgAlias}}.{{.InterfaceTypeName}} {
clientImp,_:= factory.{{ $key }}.Load(strings.ToLower(factory.facotryConfig.SubscriptionID))
clientImp,_:= factory.{{ $key }}.Load(strings.ToLower(factory.factoryConfig.SubscriptionID))
return clientImp.({{.PkgAlias}}.{{.InterfaceTypeName}})
}
func (factory *ClientFactoryImpl) Get{{$resource}}ClientForSub(subscriptionID string)({{.PkgAlias}}.{{.InterfaceTypeName}},error) {
if subscriptionID == "" {
subscriptionID = factory.facotryConfig.SubscriptionID
subscriptionID = factory.factoryConfig.SubscriptionID
}
clientImp,loaded:= factory.{{ $key }}.Load(strings.ToLower(subscriptionID))
if loaded {
Expand Down
166 changes: 81 additions & 85 deletions pkg/azclient/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,67 +52,80 @@ func AzureCloudConfigFromName(cloudName string) *cloud.Configuration {
return nil
}

// AzureCloudConfigFromURL returns cloud config from url
// OverrideAzureCloudConfigFromMetadataService returns cloud config from url
// track2 sdk will add this one in the near future https://github.com/Azure/azure-sdk-for-go/issues/20959
func AzureCloudConfigFromURL(endpoint string) (*cloud.Configuration, error) {
managementEndpoint := fmt.Sprintf("%s%s", strings.TrimSuffix(endpoint, "/"), "/metadata/endpoints?api-version=2019-05-01")
func OverrideAzureCloudConfigFromMetadataService(armconfig *ARMClientConfig, config *cloud.Configuration) error {
if armconfig == nil || armconfig.ResourceManagerEndpoint == "" {
return nil
}

managementEndpoint := fmt.Sprintf("%s%s", strings.TrimSuffix(armconfig.ResourceManagerEndpoint, "/"), "/metadata/endpoints?api-version=2019-05-01")
res, err := http.Get(managementEndpoint) //nolint
if err != nil {
return nil, err
return err
}
body, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
return err
}
metadata := []struct {
Authentication struct {
Audiences []string
LoginEndpoint string
}
Name, ResourceManager string
Name string `json:"name"`
ResourceManager string `json:"resourceManager,omitempty"`
Authentication struct {
Audiences []string `json:"audiences"`
LoginEndpoint string `json:"loginEndpoint,omitempty"`
} `json:"authentication"`
Suffixes struct {
AcrLoginServer *string `json:"acrLoginServer,omitempty"`
Storage *string `json:"storage,omitempty"`
} `json:"suffixes,omitempty"`
}{}
err = json.Unmarshal(body, &metadata)
if err != nil {
return nil, err
return err
}

if len(metadata) > 0 {
// We use the endpoint to build our config, but on ASH the config returned
// does not contain the endpoint, and this is not accounted for. This
// ultimately unsets it for the returned config, causing the bootstrap of
// the provider to fail. Instead, check if the endpoint is returned, and if
// It is not then set it.
if len(metadata[0].ResourceManager) == 0 {
metadata[0].ResourceManager = endpoint
for _, item := range metadata {
if armconfig.Cloud == "" || strings.EqualFold(item.Name, armconfig.Cloud) {
// We use the endpoint to build our config, but on ASH the config returned
// does not contain the endpoint, and this is not accounted for. This
// ultimately unsets it for the returned config, causing the bootstrap of
// the provider to fail. Instead, check if the endpoint is returned, and if
// It is not then set it.
if item.ResourceManager == "" {
item.ResourceManager = armconfig.ResourceManagerEndpoint
}
config.Services[cloud.ResourceManager] = cloud.ServiceConfiguration{
Endpoint: item.ResourceManager,
Audience: item.Authentication.Audiences[0],
}
if item.Authentication.LoginEndpoint != "" {
config.ActiveDirectoryAuthorityHost = item.Authentication.LoginEndpoint
}
if item.Suffixes.Storage != nil && armconfig.StorageSuffix == nil {
armconfig.StorageSuffix = item.Suffixes.Storage
}
if item.Suffixes.AcrLoginServer != nil && armconfig.ACRLoginServer == nil {
armconfig.ACRLoginServer = item.Suffixes.AcrLoginServer
}
return nil
}
return &cloud.Configuration{
ActiveDirectoryAuthorityHost: metadata[0].Authentication.LoginEndpoint,
Services: map[cloud.ServiceName]cloud.ServiceConfiguration{
cloud.ResourceManager: {
Endpoint: metadata[0].ResourceManager,
Audience: metadata[0].Authentication.Audiences[0],
},
},
}, nil
}
return nil, nil
}
return nil
}

func AzureCloudConfigOverrideFromEnv(config *cloud.Configuration) (*cloud.Configuration, error) {
if config == nil {
config = &cloud.AzurePublic
}
func OverrideAzureCloudConfigFromEnv(armconfig *ARMClientConfig, config *cloud.Configuration) error {
envFilePath, ok := os.LookupEnv(EnvironmentFilepathName)
if !ok {
return config, nil
return nil
}
content, err := os.ReadFile(envFilePath)
if err != nil {
return nil, err
return err
}
var envConfig Environment
if err = json.Unmarshal(content, &envConfig); err != nil {
return nil, err
return err
}
if len(envConfig.ActiveDirectoryEndpoint) > 0 {
config.ActiveDirectoryAuthorityHost = envConfig.ActiveDirectoryEndpoint
Expand All @@ -123,61 +136,44 @@ func AzureCloudConfigOverrideFromEnv(config *cloud.Configuration) (*cloud.Config
Audience: envConfig.TokenAudience,
}
}
return config, nil
if len(envConfig.StorageEndpointSuffix) > 0 {
armconfig.StorageSuffix = &envConfig.StorageEndpointSuffix
}
if len(envConfig.ContainerRegistryDNSSuffix) > 0 {
armconfig.ACRLoginServer = &envConfig.ContainerRegistryDNSSuffix
}
return nil
}

// GetAzureCloudConfig returns the cloud configuration for the given ARMClientConfig.
func GetAzureCloudConfig(armConfig *ARMClientConfig) (*cloud.Configuration, error) {
// GetAzureCloudConfigAndBackfillArmClientConfig retrieves the Azure cloud configuration based on the provided ARM client configuration.
// If the ARM client configuration is nil, it returns the default Azure public cloud configuration.
// It attempts to override the cloud configuration using metadata service and environment variables.
//
// Parameters:
// - armConfig: A pointer to an ARMClientConfig struct containing the ARM client configuration.
//
// Returns:
// - A pointer to a cloud.Configuration struct representing the Azure cloud configuration.
// - An error if there is an issue overriding the cloud configuration from metadata service or environment variables.
func GetAzureCloudConfigAndBackfillARMClientConfig(armConfig *ARMClientConfig) (*cloud.Configuration, error) {
config := &cloud.AzurePublic
if armConfig == nil {
return &cloud.AzurePublic, nil
return config, nil
}
if armConfig.ResourceManagerEndpoint != "" {
return AzureCloudConfigFromURL(armConfig.ResourceManagerEndpoint)
config = AzureCloudConfigFromName(armConfig.Cloud)
if err := OverrideAzureCloudConfigFromMetadataService(armConfig, config); err != nil {
return nil, err
}

return AzureCloudConfigOverrideFromEnv(AzureCloudConfigFromName(armConfig.Cloud))
err := OverrideAzureCloudConfigFromEnv(armConfig, config)
return config, err
}

// Environment represents a set of endpoints for each of Azure's Clouds.
type Environment struct {
Name string `json:"name"`
ManagementPortalURL string `json:"managementPortalURL"`
PublishSettingsURL string `json:"publishSettingsURL"`
ServiceManagementEndpoint string `json:"serviceManagementEndpoint"`
ResourceManagerEndpoint string `json:"resourceManagerEndpoint"`
ActiveDirectoryEndpoint string `json:"activeDirectoryEndpoint"`
GalleryEndpoint string `json:"galleryEndpoint"`
KeyVaultEndpoint string `json:"keyVaultEndpoint"`
ManagedHSMEndpoint string `json:"managedHSMEndpoint"`
GraphEndpoint string `json:"graphEndpoint"`
ServiceBusEndpoint string `json:"serviceBusEndpoint"`
BatchManagementEndpoint string `json:"batchManagementEndpoint"`
MicrosoftGraphEndpoint string `json:"microsoftGraphEndpoint"`
StorageEndpointSuffix string `json:"storageEndpointSuffix"`
CosmosDBDNSSuffix string `json:"cosmosDBDNSSuffix"`
MariaDBDNSSuffix string `json:"mariaDBDNSSuffix"`
MySQLDatabaseDNSSuffix string `json:"mySqlDatabaseDNSSuffix"`
PostgresqlDatabaseDNSSuffix string `json:"postgresqlDatabaseDNSSuffix"`
SQLDatabaseDNSSuffix string `json:"sqlDatabaseDNSSuffix"`
TrafficManagerDNSSuffix string `json:"trafficManagerDNSSuffix"`
KeyVaultDNSSuffix string `json:"keyVaultDNSSuffix"`
ManagedHSMDNSSuffix string `json:"managedHSMDNSSuffix"`
ServiceBusEndpointSuffix string `json:"serviceBusEndpointSuffix"`
ServiceManagementVMDNSSuffix string `json:"serviceManagementVMDNSSuffix"`
ResourceManagerVMDNSSuffix string `json:"resourceManagerVMDNSSuffix"`
ContainerRegistryDNSSuffix string `json:"containerRegistryDNSSuffix"`
TokenAudience string `json:"tokenAudience"`
APIManagementHostNameSuffix string `json:"apiManagementHostNameSuffix"`
SynapseEndpointSuffix string `json:"synapseEndpointSuffix"`
DatalakeSuffix string `json:"datalakeSuffix"`
ResourceIdentifiers ResourceIdentifier `json:"resourceIdentifiers"`
}

// ResourceIdentifier contains a set of Azure resource IDs.
type ResourceIdentifier struct {
Graph string `json:"graph"`
KeyVault string `json:"keyVault"`
Datalake string `json:"datalake"`
Batch string `json:"batch"`
OperationalInsights string `json:"operationalInsights"`
Name string `json:"name"`
ResourceManagerEndpoint string `json:"resourceManagerEndpoint"`
ActiveDirectoryEndpoint string `json:"activeDirectoryEndpoint"`
StorageEndpointSuffix string `json:"storageEndpointSuffix"`
ContainerRegistryDNSSuffix string `json:"containerRegistryDNSSuffix"`
TokenAudience string `json:"tokenAudience"`
}
34 changes: 24 additions & 10 deletions pkg/azclient/cloud_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,9 @@ var _ = ginkgo.Describe("Cloud", func() {
gomega.Expect(err).ToNot(gomega.HaveOccurred())
}))
defer server.Close()

cloudConfig, err := azclient.AzureCloudConfigFromURL(server.URL)
armconfig := &azclient.ARMClientConfig{}
cloudConfig := &cloud.AzurePublic
err := azclient.OverrideAzureCloudConfigFromMetadataService(armconfig, cloudConfig)
gomega.Expect(err).ToNot(gomega.HaveOccurred())
gomega.Expect(cloudConfig).ToNot(gomega.BeNil())
gomega.Expect(cloudConfig.ActiveDirectoryAuthorityHost).To(gomega.Equal("https://login.microsoftonline.com/"))
Expand Down Expand Up @@ -129,8 +130,13 @@ var _ = ginkgo.Describe("Cloud", func() {
gomega.Expect(err).ToNot(gomega.HaveOccurred())
}))
defer server.Close()

cloudConfig, err := azclient.AzureCloudConfigFromURL(server.URL)
armconfig := &azclient.ARMClientConfig{
ResourceManagerEndpoint: server.URL,
}
cloudConfig := &cloud.Configuration{
ActiveDirectoryAuthorityHost: "https://login.microsoftonline.com/", Services: map[cloud.ServiceName]cloud.ServiceConfiguration{},
}
err := azclient.OverrideAzureCloudConfigFromMetadataService(armconfig, cloudConfig)
gomega.Expect(err).ToNot(gomega.HaveOccurred())
gomega.Expect(cloudConfig).ToNot(gomega.BeNil())
gomega.Expect(cloudConfig.ActiveDirectoryAuthorityHost).To(gomega.Equal("https://login.microsoftonline.com/"))
Expand Down Expand Up @@ -170,7 +176,9 @@ var _ = ginkgo.Describe("Cloud", func() {
ginkgo.Context("AzureCloudFromEnvironment", func() {
ginkgo.When("the environment is empty", func() {
ginkgo.It("should return the default cloud", func() {
cloudConfig, err := azclient.AzureCloudConfigOverrideFromEnv(nil)
armconfig := &azclient.ARMClientConfig{}
cloudConfig := &cloud.AzurePublic
err := azclient.OverrideAzureCloudConfigFromEnv(armconfig, cloudConfig)
gomega.Expect(err).ToNot(gomega.HaveOccurred())
gomega.Expect(cloudConfig).ToNot(gomega.BeNil())
gomega.Expect(cloudConfig.ActiveDirectoryAuthorityHost).To(gomega.Equal("https://login.microsoftonline.com/"))
Expand All @@ -182,18 +190,20 @@ var _ = ginkgo.Describe("Cloud", func() {
ginkgo.When("the environment is set,file is not found", func() {
ginkgo.It("should return error", func() {
os.Setenv(azclient.EnvironmentFilepathName, "notfound")
cloudConfig, err := azclient.AzureCloudConfigOverrideFromEnv(nil)
armconfig := &azclient.ARMClientConfig{}
cloudConfig := &cloud.AzurePublic
err := azclient.OverrideAzureCloudConfigFromEnv(armconfig, cloudConfig)
gomega.Expect(err).To(gomega.HaveOccurred())
gomega.Expect(cloudConfig).To(gomega.BeNil())
os.Unsetenv(azclient.EnvironmentFilepathName)
})
})
ginkgo.When("the environment is set,file is empty", func() {
ginkgo.It("should return error", func() {
os.Setenv(azclient.EnvironmentFilepathName, "notfound")
cloudConfig, err := azclient.AzureCloudConfigOverrideFromEnv(nil)
armconfig := &azclient.ARMClientConfig{}
cloudConfig := &cloud.AzurePublic
err := azclient.OverrideAzureCloudConfigFromEnv(armconfig, cloudConfig)
gomega.Expect(err).To(gomega.HaveOccurred())
gomega.Expect(cloudConfig).To(gomega.BeNil())
os.Unsetenv(azclient.EnvironmentFilepathName)
})
})
Expand All @@ -211,7 +221,11 @@ var _ = ginkgo.Describe("Cloud", func() {
}`), 0600)
gomega.Expect(err).ToNot(gomega.HaveOccurred())
os.Setenv(azclient.EnvironmentFilepathName, configFile.Name())
cloudConfig, err := azclient.AzureCloudConfigOverrideFromEnv(&cloud.AzureGovernment)
armconfig := &azclient.ARMClientConfig{
Cloud: "AzureGovernment",
}
cloudConfig := &cloud.AzureGovernment
err = azclient.OverrideAzureCloudConfigFromEnv(armconfig, cloudConfig)
gomega.Expect(err).ToNot(gomega.HaveOccurred())
gomega.Expect(cloudConfig).ToNot(gomega.BeNil())
gomega.Expect(cloudConfig.ActiveDirectoryAuthorityHost).To(gomega.Equal("https://login.chinacloudapi.cn"))
Expand Down
Loading
Loading