Merge pull request #12207 from swordqiu/automated-cherry-pick-of-#12206-upstream-release-3.8

Automated cherry pick of #12206: fix: AWS asume role with optional name
This commit is contained in:
Zexi Li
2021-09-15 01:15:14 +08:00
committed by GitHub
20 changed files with 148 additions and 98 deletions
+1 -2
View File
@@ -408,8 +408,7 @@ func (self *SCloudprovider) GetProvider() (cloudprovider.ICloudProvider, error)
ProxyFunc: proxyFunc,
SHCSOEndpoints: delegate.Options.SHCSOEndpoints,
SApsaraEndpoints: delegate.Options.SApsaraEndpoints,
Options: jsonutils.Marshal(delegate.Options).(*jsonutils.JSONDict),
},
)
}
+1 -2
View File
@@ -568,8 +568,7 @@ func (account *SCloudDelegate) GetProvider() (cloudprovider.ICloudProvider, erro
Secret: passwd,
ProxyFunc: proxyFunc,
SApsaraEndpoints: account.Options.SApsaraEndpoints,
SHCSOEndpoints: account.Options.SHCSOEndpoints,
Options: jsonutils.Marshal(account.Options).(*jsonutils.JSONDict),
AccountId: account.Id,
})
+10 -22
View File
@@ -123,31 +123,19 @@ func (self *CloudReportBase) InitProviderInstance() (cloudprovider.ICloudProvide
if err != nil {
return nil, errors.Wrap(err, "getCloudAccount error")
}
endpoints := cloudprovider.SApsaraEndpoints{}
hwendpoints := cloudprovider.SHCSOEndpoints{}
options, err := cloudAccout.Get("options")
if err == nil {
err := options.Unmarshal(&endpoints)
if err != nil {
log.Errorf("Unmarshal SApsaraEndpoints err: %v", err)
}
err = options.Unmarshal(&hwendpoints)
if err != nil {
log.Errorf("Unmarshal SHCSOEndpoints err: %v", err)
}
} else {
log.Errorf("get cloudAccout options err: %v", err)
if err != nil {
log.Errorf("get cloudAccout options err:%v", err)
}
cfg := cloudprovider.ProviderConfig{
Id: self.SProvider.Id,
Name: self.SProvider.Name,
URL: self.SProvider.AccessUrl,
Account: self.SProvider.Account,
Secret: secretDe,
Vendor: self.SProvider.Provider,
ProxyFunc: proxyFunc,
SApsaraEndpoints: endpoints,
SHCSOEndpoints: hwendpoints,
Id: self.SProvider.Id,
Name: self.SProvider.Name,
URL: self.SProvider.AccessUrl,
Account: self.SProvider.Account,
Secret: secretDe,
Vendor: self.SProvider.Provider,
ProxyFunc: proxyFunc,
Options: options.(*jsonutils.JSONDict),
}
return cloudprovider.GetProvider(cfg)
}
+1 -2
View File
@@ -160,8 +160,7 @@ type ProviderConfig struct {
AccountId string
SApsaraEndpoints
SHCSOEndpoints
Options *jsonutils.JSONDict
ProxyFunc httputils.TransportProxyFunc
}
+19
View File
@@ -0,0 +1,19 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cloudprovider
type SAWSExtraOptions struct {
AWSAssumeRoleName string `json:"aws_assume_role_name"`
}
+9 -24
View File
@@ -437,15 +437,11 @@ func (manager *SCloudaccountManager) validateCreateData(
}
var endpointOptions jsonutils.JSONObject
endpoints := cloudprovider.SApsaraEndpoints{}
if input.SCloudaccountCredential.SApsaraEndpoints != nil {
endpoints = *input.SCloudaccountCredential.SApsaraEndpoints
endpointOptions = jsonutils.Marshal(input.SCloudaccountCredential.SApsaraEndpoints)
}
hcsoEndpoints := cloudprovider.SHCSOEndpoints{}
if input.SCloudaccountCredential.SHCSOEndpoints != nil {
hcsoEndpoints = *input.SCloudaccountCredential.SHCSOEndpoints
endpointOptions = jsonutils.Marshal(input.SCloudaccountCredential.SHCSOEndpoints)
}
@@ -511,8 +507,7 @@ func (manager *SCloudaccountManager) validateCreateData(
Secret: input.Secret,
ProxyFunc: proxyFunc,
SApsaraEndpoints: endpoints,
SHCSOEndpoints: hcsoEndpoints,
Options: input.Options,
})
if err != nil {
if err == cloudprovider.ErrNoSuchProvder {
@@ -740,12 +735,12 @@ func (self *SCloudaccount) PerformUpdateCredential(ctx context.Context, userCred
}
accountId, err := cloudprovider.IsValidCloudAccount(cloudprovider.ProviderConfig{
Vendor: self.Provider,
URL: self.AccessUrl,
Account: account.Account,
Secret: account.Secret,
SHCSOEndpoints: hcsoEndpoints,
ProxyFunc: self.proxyFunc(),
Vendor: self.Provider,
URL: self.AccessUrl,
Account: account.Account,
Secret: account.Secret,
Options: self.Options,
ProxyFunc: self.proxyFunc(),
})
if err != nil {
return nil, httperrors.NewInputParameterError("invalid cloud account info error: %s", err.Error())
@@ -936,15 +931,6 @@ func (self *SCloudaccount) getProviderInternal() (cloudprovider.ICloudProvider,
if err != nil {
return nil, fmt.Errorf("Invalid password %s", err)
}
endpoints := cloudprovider.SApsaraEndpoints{}
hcsoEndpoints := cloudprovider.SHCSOEndpoints{}
if self.Options != nil {
if self.Provider == api.CLOUD_PROVIDER_HCSO {
self.Options.Unmarshal(&hcsoEndpoints)
} else if self.Provider == api.CLOUD_PROVIDER_APSARA {
self.Options.Unmarshal(&endpoints)
}
}
return cloudprovider.GetProvider(cloudprovider.ProviderConfig{
Id: self.Id,
Name: self.Name,
@@ -953,9 +939,8 @@ func (self *SCloudaccount) getProviderInternal() (cloudprovider.ICloudProvider,
Account: self.Account,
Secret: secret,
SApsaraEndpoints: endpoints,
SHCSOEndpoints: hcsoEndpoints,
ProxyFunc: self.proxyFunc(),
Options: self.Options,
ProxyFunc: self.proxyFunc(),
})
}
+1 -12
View File
@@ -874,16 +874,6 @@ func (self *SCloudprovider) GetProvider() (cloudprovider.ICloudProvider, error)
account := self.GetCloudaccount()
endpoints := cloudprovider.SApsaraEndpoints{}
hscsoEndpoints := cloudprovider.SHCSOEndpoints{}
if account.Options != nil {
if self.Provider == api.CLOUD_PROVIDER_HCSO {
account.Options.Unmarshal(&hscsoEndpoints)
} else if self.Provider == api.CLOUD_PROVIDER_APSARA {
account.Options.Unmarshal(&endpoints)
}
}
return cloudprovider.GetProvider(cloudprovider.ProviderConfig{
Id: self.Id,
Name: self.Name,
@@ -893,8 +883,7 @@ func (self *SCloudprovider) GetProvider() (cloudprovider.ICloudProvider, error)
Secret: passwd,
ProxyFunc: account.proxyFunc(),
SApsaraEndpoints: endpoints,
SHCSOEndpoints: hscsoEndpoints,
Options: account.Options,
})
}
+13 -1
View File
@@ -228,6 +228,7 @@ type SAWSCloudAccountCreateOptions struct {
OptionsBillingReportBucket string `help:"bucket that stores billing report" json:"-"`
OptionsBillingBucketAccount string `help:"id of account that can access bucket, blank if this account can access" json:"-"`
OptionsBillingFilePrefix string `help:"prefix of billing file name" json:"-"`
OptionsAssumeRoleName string `help:"assume role name" json:"-"`
}
func (opts *SAWSCloudAccountCreateOptions) Params() (jsonutils.JSONObject, error) {
@@ -242,6 +243,9 @@ func (opts *SAWSCloudAccountCreateOptions) Params() (jsonutils.JSONObject, error
if len(opts.OptionsBillingFilePrefix) > 0 {
options.Add(jsonutils.NewString(opts.OptionsBillingFilePrefix), "billing_file_prefix")
}
if len(opts.OptionsAssumeRoleName) > 0 {
options.Add(jsonutils.NewString(opts.OptionsAssumeRoleName), "aws_assume_role_name")
}
if options.Size() > 0 {
params.Add(options, "options")
}
@@ -540,7 +544,7 @@ type SCloudAccountUpdateBaseOptions struct {
Name string `help:"New name to update"`
SyncIntervalSeconds *int `help:"auto synchornize interval in seconds"`
AutoCreateProject *bool `help:"automatically create local project for new remote project"`
AutoCreateProject *bool `help:"automatically create local project for new remote project" negative:"no_auto_create_project"`
ProxySetting string `help:"proxy setting name or id" json:"proxy_setting"`
SamlAuth string `help:"Enable or disable saml auth" choices:"true|false"`
@@ -706,6 +710,8 @@ type SAWSCloudAccountUpdateOptions struct {
RemoveOptionsBillingBucketAccount bool `help:"remove id of account that can access bucket, blank if this account can access" json:"-"`
OptionsBillingFilePrefix string `help:"update prefix of billing file name" json:"-"`
RemoveOptionsBillingFilePrefix bool `help:"remove prefix of billing file name" json:"-"`
OptionsAssumeRoleName string `help:"name of assume role" json:"-"`
RemoveOptionsAssumeRoleName bool `help:"remove option of aws_assume_role_name"`
}
func (opts *SAWSCloudAccountUpdateOptions) Params() (jsonutils.JSONObject, error) {
@@ -721,6 +727,9 @@ func (opts *SAWSCloudAccountUpdateOptions) Params() (jsonutils.JSONObject, error
if len(opts.OptionsBillingFilePrefix) > 0 {
options.Add(jsonutils.NewString(opts.OptionsBillingFilePrefix), "billing_file_prefix")
}
if len(opts.OptionsAssumeRoleName) > 0 {
options.Add(jsonutils.NewString(opts.OptionsAssumeRoleName), "aws_assume_role_name")
}
if options.Size() > 0 {
params.Add(options, "options")
}
@@ -734,6 +743,9 @@ func (opts *SAWSCloudAccountUpdateOptions) Params() (jsonutils.JSONObject, error
if opts.RemoveOptionsBillingFilePrefix {
removeOptions = append(removeOptions, "billing_file_prefix")
}
if opts.RemoveOptionsAssumeRoleName {
removeOptions = append(removeOptions, "aws_assume_role_name")
}
if len(removeOptions) > 0 {
params.Add(jsonutils.NewStringArray(removeOptions), "remove_options")
}
+22 -20
View File
@@ -72,14 +72,16 @@ type ApsaraClientConfig struct {
accessKey string
accessSecret string
debug bool
endpoints cloudprovider.SApsaraEndpoints
}
func NewApsaraClientConfig(accessKey, accessSecret string, endpoint string, endpoints cloudprovider.SApsaraEndpoints) *ApsaraClientConfig {
cfg := &ApsaraClientConfig{
accessKey: accessKey,
accessSecret: accessSecret,
endpoints: endpoints,
}
cfg.cpcfg.SApsaraEndpoints = endpoints
cfg.cpcfg.URL = endpoint
return cfg
}
@@ -117,7 +119,7 @@ func NewApsaraClient(cfg *ApsaraClientConfig) (*SApsaraClient, error) {
if err != nil {
return nil, errors.Wrap(err, "fetchRegions")
}
if len(client.cpcfg.OssEndpoint) > 0 {
if len(client.endpoints.OssEndpoint) > 0 {
err = client.fetchBuckets()
if err != nil {
return nil, errors.Wrapf(err, "fetchBuckets")
@@ -132,32 +134,32 @@ func NewApsaraClient(cfg *ApsaraClientConfig) (*SApsaraClient, error) {
func (self *SApsaraClient) getDomain(product string) string {
switch product {
case APSARA_PRODUCT_ECS:
if len(self.cpcfg.EcsEndpoint) > 0 {
return self.cpcfg.EcsEndpoint
if len(self.endpoints.EcsEndpoint) > 0 {
return self.endpoints.EcsEndpoint
}
case APSARA_PRODUCT_RAM:
if len(self.cpcfg.RamEndpoint) > 0 {
return self.cpcfg.RamEndpoint
if len(self.endpoints.RamEndpoint) > 0 {
return self.endpoints.RamEndpoint
}
case APSARA_PRODUCT_RDS:
if len(self.cpcfg.RdsEndpoint) > 0 {
return self.cpcfg.RdsEndpoint
if len(self.endpoints.RdsEndpoint) > 0 {
return self.endpoints.RdsEndpoint
}
case APSARA_PRODUCT_SLB:
if len(self.cpcfg.SlbEndpoint) > 0 {
return self.cpcfg.SlbEndpoint
if len(self.endpoints.SlbEndpoint) > 0 {
return self.endpoints.SlbEndpoint
}
case APSARA_PRODUCT_STS:
if len(self.cpcfg.StsEndpoint) > 0 {
return self.cpcfg.StsEndpoint
if len(self.endpoints.StsEndpoint) > 0 {
return self.endpoints.StsEndpoint
}
case APSARA_PRODUCT_VPC:
if len(self.cpcfg.VpcEndpoint) > 0 {
return self.cpcfg.VpcEndpoint
if len(self.endpoints.VpcEndpoint) > 0 {
return self.endpoints.VpcEndpoint
}
case APSARA_PRODUCT_KVSTORE:
if len(self.cpcfg.KvsEndpoint) > 0 {
return self.cpcfg.KvsEndpoint
if len(self.endpoints.KvsEndpoint) > 0 {
return self.endpoints.KvsEndpoint
}
}
return self.cpcfg.URL
@@ -312,8 +314,8 @@ func (self *SApsaraClient) trialRequest(apiName string, params map[string]string
func (self *SApsaraClient) fetchRegions() error {
params := map[string]string{"AcceptLanguage": "zh-CN"}
if len(self.cpcfg.SApsaraEndpoints.DefaultRegion) > 0 {
params["RegionId"] = self.cpcfg.SApsaraEndpoints.DefaultRegion
if len(self.endpoints.DefaultRegion) > 0 {
params["RegionId"] = self.endpoints.DefaultRegion
}
body, err := self.ecsRequest("DescribeRegions", params)
if err != nil {
@@ -348,7 +350,7 @@ func (client *SApsaraClient) getOssClient(regionId string) (*oss.Client, error)
cliOpts := []oss.ClientOption{
oss.HTTPClient(httpClient),
}
cli, err := oss.New(client.cpcfg.OssEndpoint, client.accessKey, client.accessSecret, cliOpts...)
cli, err := oss.New(client.endpoints.OssEndpoint, client.accessKey, client.accessSecret, cliOpts...)
if err != nil {
return nil, errors.Wrap(err, "oss.New")
}
@@ -369,7 +371,7 @@ func (self *SApsaraClient) invalidateIBuckets() {
}
func (self *SApsaraClient) getIBuckets() ([]cloudprovider.ICloudBucket, error) {
if len(self.cpcfg.OssEndpoint) == 0 {
if len(self.endpoints.OssEndpoint) == 0 {
return nil, fmt.Errorf("empty oss endpoint")
}
if self.iBuckets == nil {
+1 -1
View File
@@ -87,7 +87,7 @@ func (b *SBucket) GetStorageClass() string {
func (b *SBucket) GetAccessUrls() []cloudprovider.SBucketAccessUrl {
return []cloudprovider.SBucketAccessUrl{
{
Url: fmt.Sprintf("%s.%s", b.Name, b.region.client.cpcfg.OssEndpoint),
Url: fmt.Sprintf("%s.%s", b.Name, b.region.client.endpoints.OssEndpoint),
Description: "ExtranetEndpoint",
Primary: true,
},
+5 -1
View File
@@ -79,12 +79,16 @@ func (self *SApsaraProviderFactory) ValidateUpdateCloudaccountCredential(ctx con
}
func (self *SApsaraProviderFactory) GetProvider(cfg cloudprovider.ProviderConfig) (cloudprovider.ICloudProvider, error) {
endpoints := cloudprovider.SApsaraEndpoints{}
if cfg.Options != nil {
cfg.Options.Unmarshal(&endpoints)
}
client, err := apsara.NewApsaraClient(
apsara.NewApsaraClientConfig(
cfg.Account,
cfg.Secret,
cfg.URL,
cfg.SApsaraEndpoints,
endpoints,
).CloudproviderConfig(cfg),
)
if err != nil {
+17 -1
View File
@@ -60,6 +60,8 @@ const (
AWS_CHINA_ARN_PREFIX = "arn:aws-cn:iam::aws:policy/"
DEFAULT_S3_REGION_ID = "us-east-1"
DefaultAssumeRoleName = "OrganizationAccountAccessRole"
)
var (
@@ -75,6 +77,8 @@ type AwsClientConfig struct {
accountId string
debug bool
assumeRoleName string
}
func NewAwsClientConfig(accessUrl, accessKey, accessSecret, accountId string) *AwsClientConfig {
@@ -98,6 +102,18 @@ func (cfg *AwsClientConfig) Debug(debug bool) *AwsClientConfig {
return cfg
}
func (cfg *AwsClientConfig) SetAssumeRole(roleName string) *AwsClientConfig {
cfg.assumeRoleName = roleName
return cfg
}
func (cfg *AwsClientConfig) getAssumeRoleName() string {
if len(cfg.assumeRoleName) > 0 {
return cfg.assumeRoleName
}
return DefaultAssumeRoleName
}
type SAwsClient struct {
*AwsClientConfig
@@ -261,7 +277,7 @@ func (client *SAwsClient) getAwsSession(regionId string, assumeRole bool) (*sess
default:
env = "aws-cn"
}
roleARN := fmt.Sprintf("arn:%s:iam::%s:role/OrganizationAccountAccessRole", env, client.accountId)
roleARN := fmt.Sprintf("arn:%s:iam::%s:role/%s", env, client.accountId, client.getAssumeRoleName())
creds := stscreds.NewCredentials(s, roleARN)
s = s.Copy(&aws.Config{Credentials: creds})
}
+30
View File
@@ -0,0 +1,30 @@
// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package aws
import (
"testing"
)
func TestAwsClientConfig(t *testing.T) {
cfg := NewAwsClientConfig("", "", "", "")
if cfg.getAssumeRoleName() != DefaultAssumeRoleName {
t.Errorf("getAssumeRoleName != %s", DefaultAssumeRoleName)
}
cfg = cfg.SetAssumeRole("newRole")
if cfg.getAssumeRoleName() != "newRole" {
t.Errorf("getAssumeRoleName != newRole")
}
}
+5 -1
View File
@@ -201,11 +201,15 @@ func parseAccount(account, secret string) (accessKey string, secretKey string, a
}
func (self *SAwsProviderFactory) GetProvider(cfg cloudprovider.ProviderConfig) (cloudprovider.ICloudProvider, error) {
extra := cloudprovider.SAWSExtraOptions{}
if cfg.Options != nil {
cfg.Options.Unmarshal(&extra)
}
accessKey, secret, accountId := parseAccount(cfg.Account, cfg.Secret)
client, err := aws.NewAwsClient(
aws.NewAwsClientConfig(
cfg.URL, accessKey, secret, accountId,
).CloudproviderConfig(cfg),
).SetAssumeRole(extra.AWSAssumeRoleName).CloudproviderConfig(cfg),
)
if err != nil {
return nil, errors.Wrap(err, "NewAwsClient")
+2 -2
View File
@@ -443,7 +443,7 @@ func NextDeviceName(curDeviceNames []string) (string, error) {
}
for i := 0; i < 25; i++ {
device := fmt.Sprintf("/dev/sd%s", string(98+i))
device := fmt.Sprintf("/dev/sd%c", byte(98+i))
found := false
for _, item := range currents {
if strings.HasPrefix(item, device) {
@@ -457,7 +457,7 @@ func NextDeviceName(curDeviceNames []string) (string, error) {
}
for i := 0; i < 25; i++ {
device := fmt.Sprintf("/dev/vxd%s", string(98+i))
device := fmt.Sprintf("/dev/vxd%c", byte(98+i))
found := false
for _, item := range currents {
if !strings.HasPrefix(item, device) {
+1 -1
View File
@@ -600,7 +600,7 @@ func (b *SBucket) GetWebsiteConf() (cloudprovider.SBucketWebsiteConf, error) {
}
result.Index = out.IndexDocument.Suffix
result.ErrorDocument = out.ErrorDocument.Key
endpoint := b.region.client.cpcfg.GetEndpoint("obs-website", b.region.GetId())
endpoint := b.region.client.endpoints.GetEndpoint("obs-website", b.region.GetId())
result.Url = fmt.Sprintf("https://%s.%s", endpoint)
return result, nil
}
+2 -2
View File
@@ -243,7 +243,7 @@ func getOBSEndpoint(regionId string) string {
}
func (client *SHuaweiClient) getOBSClient(regionId string) (*obs.ObsClient, error) {
endpoint := client.cpcfg.SHCSOEndpoints.GetEndpoint("obs", regionId)
endpoint := client.endpoints.GetEndpoint("obs", regionId)
return obs.New(client.accessKey, client.accessSecret, endpoint)
}
@@ -514,7 +514,7 @@ func (self *SHuaweiClient) GetOwnerId() (string, error) {
}
func (self *SHuaweiClient) GetSamlEntityId() string {
return fmt.Sprintf("auth.%s", self.cpcfg.EndpointDomain)
return fmt.Sprintf("auth.%s", self.endpoints.EndpointDomain)
}
func (self *SHuaweiClient) initOwner() error {
+5 -1
View File
@@ -150,10 +150,14 @@ func parseAccount(account string) (accessKey string, projectId string) {
}
func (self *SHCSOProviderFactory) GetProvider(cfg cloudprovider.ProviderConfig) (cloudprovider.ICloudProvider, error) {
hscsoEndpoints := cloudprovider.SHCSOEndpoints{}
if cfg.Options != nil {
cfg.Options.Unmarshal(&hscsoEndpoints)
}
accessKey, project_id := parseAccount(cfg.Account)
client, err := huawei.NewHuaweiClient(
huawei.NewHuaweiClientConfig(
accessKey, cfg.Secret, project_id, &cfg.SHCSOEndpoints,
accessKey, cfg.Secret, project_id, &hscsoEndpoints,
).CloudproviderConfig(cfg),
)
if err != nil {
+1 -1
View File
@@ -66,7 +66,7 @@ func (self *SAMLProvider) GetStatus() string {
}
func (self *SAMLProvider) GetAuthUrl() string {
return fmt.Sprintf("https://auth.%s/authui/federation/websso?domain_id=%s&idp=%s&protocol=saml", self.client.cpcfg.EndpointDomain, self.client.ownerId, self.Id)
return fmt.Sprintf("https://auth.%s/authui/federation/websso?domain_id=%s&idp=%s&protocol=saml", self.client.endpoints.EndpointDomain, self.client.ownerId, self.Id)
}
func (self *SAMLProvider) Delete() error {
+2 -2
View File
@@ -188,8 +188,8 @@ func (self *SRegion) createNetwork(vpcId string, name string, cidr string, desc
// hard code for hcso
// https://support.huaweicloud.com/dns_faq/dns_faq_002.html
// https://support.huaweicloud.com/api-dns/dns_api_69001.html
if self.client != nil && len(self.client.cpcfg.SHCSOEndpoints.DefaultSubnetDns) > 0 {
dns := strings.Split(self.client.cpcfg.SHCSOEndpoints.DefaultSubnetDns, ",")
if self.client != nil && len(self.client.endpoints.DefaultSubnetDns) > 0 {
dns := strings.Split(self.client.endpoints.DefaultSubnetDns, ",")
if len(dns) > 0 && len(dns[0]) > 0 {
subnetObj.Add(jsonutils.NewString(dns[0]), "primary_dns")
}