mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 06:27:05 +00:00
Merge pull request #78242 from feiskyer/configurable-provider
Allow setting Azure cloud provider from Kubernetes secrets instread of local configure file
This commit is contained in:
commit
bb7898ba19
@ -14,6 +14,7 @@ go_library(
|
|||||||
"azure_blobDiskController.go",
|
"azure_blobDiskController.go",
|
||||||
"azure_cache.go",
|
"azure_cache.go",
|
||||||
"azure_client.go",
|
"azure_client.go",
|
||||||
|
"azure_config.go",
|
||||||
"azure_controller_common.go",
|
"azure_controller_common.go",
|
||||||
"azure_controller_standard.go",
|
"azure_controller_standard.go",
|
||||||
"azure_controller_vmss.go",
|
"azure_controller_vmss.go",
|
||||||
@ -39,6 +40,7 @@ go_library(
|
|||||||
deps = [
|
deps = [
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||||
@ -80,6 +82,7 @@ go_test(
|
|||||||
srcs = [
|
srcs = [
|
||||||
"azure_backoff_test.go",
|
"azure_backoff_test.go",
|
||||||
"azure_cache_test.go",
|
"azure_cache_test.go",
|
||||||
|
"azure_config_test.go",
|
||||||
"azure_controller_common_test.go",
|
"azure_controller_common_test.go",
|
||||||
"azure_instances_test.go",
|
"azure_instances_test.go",
|
||||||
"azure_loadbalancer_test.go",
|
"azure_loadbalancer_test.go",
|
||||||
@ -100,6 +103,7 @@ go_test(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
|
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
|
||||||
"//staging/src/k8s.io/cloud-provider:go_default_library",
|
"//staging/src/k8s.io/cloud-provider:go_default_library",
|
||||||
"//staging/src/k8s.io/cloud-provider/service/helpers:go_default_library",
|
"//staging/src/k8s.io/cloud-provider/service/helpers:go_default_library",
|
||||||
@ -110,6 +114,7 @@ go_test(
|
|||||||
"//vendor/github.com/Azure/go-autorest/autorest:go_default_library",
|
"//vendor/github.com/Azure/go-autorest/autorest:go_default_library",
|
||||||
"//vendor/github.com/Azure/go-autorest/autorest/to:go_default_library",
|
"//vendor/github.com/Azure/go-autorest/autorest/to:go_default_library",
|
||||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||||
|
"//vendor/sigs.k8s.io/yaml:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,25 +36,25 @@ var (
|
|||||||
// AzureAuthConfig holds auth related part of cloud config
|
// AzureAuthConfig holds auth related part of cloud config
|
||||||
type AzureAuthConfig struct {
|
type AzureAuthConfig struct {
|
||||||
// The cloud environment identifier. Takes values from https://github.com/Azure/go-autorest/blob/ec5f4903f77ed9927ac95b19ab8e44ada64c1356/autorest/azure/environments.go#L13
|
// The cloud environment identifier. Takes values from https://github.com/Azure/go-autorest/blob/ec5f4903f77ed9927ac95b19ab8e44ada64c1356/autorest/azure/environments.go#L13
|
||||||
Cloud string `json:"cloud" yaml:"cloud"`
|
Cloud string `json:"cloud,omitempty" yaml:"cloud,omitempty"`
|
||||||
// The AAD Tenant ID for the Subscription that the cluster is deployed in
|
// The AAD Tenant ID for the Subscription that the cluster is deployed in
|
||||||
TenantID string `json:"tenantId" yaml:"tenantId"`
|
TenantID string `json:"tenantId,omitempty" yaml:"tenantId,omitempty"`
|
||||||
// The ClientID for an AAD application with RBAC access to talk to Azure RM APIs
|
// The ClientID for an AAD application with RBAC access to talk to Azure RM APIs
|
||||||
AADClientID string `json:"aadClientId" yaml:"aadClientId"`
|
AADClientID string `json:"aadClientId,omitempty" yaml:"aadClientId,omitempty"`
|
||||||
// The ClientSecret for an AAD application with RBAC access to talk to Azure RM APIs
|
// The ClientSecret for an AAD application with RBAC access to talk to Azure RM APIs
|
||||||
AADClientSecret string `json:"aadClientSecret" yaml:"aadClientSecret"`
|
AADClientSecret string `json:"aadClientSecret,omitempty" yaml:"aadClientSecret,omitempty"`
|
||||||
// The path of a client certificate for an AAD application with RBAC access to talk to Azure RM APIs
|
// The path of a client certificate for an AAD application with RBAC access to talk to Azure RM APIs
|
||||||
AADClientCertPath string `json:"aadClientCertPath" yaml:"aadClientCertPath"`
|
AADClientCertPath string `json:"aadClientCertPath,omitempty" yaml:"aadClientCertPath,omitempty"`
|
||||||
// The password of the client certificate for an AAD application with RBAC access to talk to Azure RM APIs
|
// The password of the client certificate for an AAD application with RBAC access to talk to Azure RM APIs
|
||||||
AADClientCertPassword string `json:"aadClientCertPassword" yaml:"aadClientCertPassword"`
|
AADClientCertPassword string `json:"aadClientCertPassword,omitempty" yaml:"aadClientCertPassword,omitempty"`
|
||||||
// Use managed service identity for the virtual machine to access Azure ARM APIs
|
// Use managed service identity for the virtual machine to access Azure ARM APIs
|
||||||
UseManagedIdentityExtension bool `json:"useManagedIdentityExtension" yaml:"useManagedIdentityExtension"`
|
UseManagedIdentityExtension bool `json:"useManagedIdentityExtension,omitempty" yaml:"useManagedIdentityExtension,omitempty"`
|
||||||
// UserAssignedIdentityID contains the Client ID of the user assigned MSI which is assigned to the underlying VMs. If empty the user assigned identity is not used.
|
// UserAssignedIdentityID contains the Client ID of the user assigned MSI which is assigned to the underlying VMs. If empty the user assigned identity is not used.
|
||||||
// More details of the user assigned identity can be found at: https://docs.microsoft.com/en-us/azure/active-directory/managed-service-identity/overview
|
// More details of the user assigned identity can be found at: https://docs.microsoft.com/en-us/azure/active-directory/managed-service-identity/overview
|
||||||
// For the user assigned identity specified here to be used, the UseManagedIdentityExtension has to be set to true.
|
// For the user assigned identity specified here to be used, the UseManagedIdentityExtension has to be set to true.
|
||||||
UserAssignedIdentityID string `json:"userAssignedIdentityID" yaml:"userAssignedIdentityID"`
|
UserAssignedIdentityID string `json:"userAssignedIdentityID,omitempty" yaml:"userAssignedIdentityID,omitempty"`
|
||||||
// The ID of the Azure Subscription that the cluster is deployed in
|
// The ID of the Azure Subscription that the cluster is deployed in
|
||||||
SubscriptionID string `json:"subscriptionId" yaml:"subscriptionId"`
|
SubscriptionID string `json:"subscriptionId,omitempty" yaml:"subscriptionId,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetServicePrincipalToken creates a new service principal token based on the configuration
|
// GetServicePrincipalToken creates a new service principal token based on the configuration
|
||||||
|
@ -36,13 +36,13 @@ import (
|
|||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
"k8s.io/client-go/util/flowcontrol"
|
"k8s.io/client-go/util/flowcontrol"
|
||||||
cloudprovider "k8s.io/cloud-provider"
|
cloudprovider "k8s.io/cloud-provider"
|
||||||
|
"k8s.io/klog"
|
||||||
"k8s.io/legacy-cloud-providers/azure/auth"
|
"k8s.io/legacy-cloud-providers/azure/auth"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute"
|
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute"
|
||||||
"github.com/Azure/go-autorest/autorest"
|
"github.com/Azure/go-autorest/autorest"
|
||||||
"github.com/Azure/go-autorest/autorest/azure"
|
"github.com/Azure/go-autorest/autorest/azure"
|
||||||
"k8s.io/klog"
|
|
||||||
"sigs.k8s.io/yaml"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -83,78 +83,81 @@ type Config struct {
|
|||||||
auth.AzureAuthConfig
|
auth.AzureAuthConfig
|
||||||
|
|
||||||
// The name of the resource group that the cluster is deployed in
|
// The name of the resource group that the cluster is deployed in
|
||||||
ResourceGroup string `json:"resourceGroup" yaml:"resourceGroup"`
|
ResourceGroup string `json:"resourceGroup,omitempty" yaml:"resourceGroup,omitempty"`
|
||||||
// The location of the resource group that the cluster is deployed in
|
// The location of the resource group that the cluster is deployed in
|
||||||
Location string `json:"location" yaml:"location"`
|
Location string `json:"location,omitempty" yaml:"location,omitempty"`
|
||||||
// The name of the VNet that the cluster is deployed in
|
// The name of the VNet that the cluster is deployed in
|
||||||
VnetName string `json:"vnetName" yaml:"vnetName"`
|
VnetName string `json:"vnetName,omitempty" yaml:"vnetName,omitempty"`
|
||||||
// The name of the resource group that the Vnet is deployed in
|
// The name of the resource group that the Vnet is deployed in
|
||||||
VnetResourceGroup string `json:"vnetResourceGroup" yaml:"vnetResourceGroup"`
|
VnetResourceGroup string `json:"vnetResourceGroup,omitempty" yaml:"vnetResourceGroup,omitempty"`
|
||||||
// The name of the subnet that the cluster is deployed in
|
// The name of the subnet that the cluster is deployed in
|
||||||
SubnetName string `json:"subnetName" yaml:"subnetName"`
|
SubnetName string `json:"subnetName,omitempty" yaml:"subnetName,omitempty"`
|
||||||
// The name of the security group attached to the cluster's subnet
|
// The name of the security group attached to the cluster's subnet
|
||||||
SecurityGroupName string `json:"securityGroupName" yaml:"securityGroupName"`
|
SecurityGroupName string `json:"securityGroupName,omitempty" yaml:"securityGroupName,omitempty"`
|
||||||
// (Optional in 1.6) The name of the route table attached to the subnet that the cluster is deployed in
|
// (Optional in 1.6) The name of the route table attached to the subnet that the cluster is deployed in
|
||||||
RouteTableName string `json:"routeTableName" yaml:"routeTableName"`
|
RouteTableName string `json:"routeTableName,omitempty" yaml:"routeTableName,omitempty"`
|
||||||
// The name of the resource group that the RouteTable is deployed in
|
// The name of the resource group that the RouteTable is deployed in
|
||||||
RouteTableResourceGroup string `json:"routeTableResourceGroup" yaml:"routeTableResourceGroup"`
|
RouteTableResourceGroup string `json:"routeTableResourceGroup,omitempty" yaml:"routeTableResourceGroup,omitempty"`
|
||||||
// (Optional) The name of the availability set that should be used as the load balancer backend
|
// (Optional) The name of the availability set that should be used as the load balancer backend
|
||||||
// If this is set, the Azure cloudprovider will only add nodes from that availability set to the load
|
// If this is set, the Azure cloudprovider will only add nodes from that availability set to the load
|
||||||
// balancer backend pool. If this is not set, and multiple agent pools (availability sets) are used, then
|
// balancer backend pool. If this is not set, and multiple agent pools (availability sets) are used, then
|
||||||
// the cloudprovider will try to add all nodes to a single backend pool which is forbidden.
|
// the cloudprovider will try to add all nodes to a single backend pool which is forbidden.
|
||||||
// In other words, if you use multiple agent pools (availability sets), you MUST set this field.
|
// In other words, if you use multiple agent pools (availability sets), you MUST set this field.
|
||||||
PrimaryAvailabilitySetName string `json:"primaryAvailabilitySetName" yaml:"primaryAvailabilitySetName"`
|
PrimaryAvailabilitySetName string `json:"primaryAvailabilitySetName,omitempty" yaml:"primaryAvailabilitySetName,omitempty"`
|
||||||
// The type of azure nodes. Candidate values are: vmss and standard.
|
// The type of azure nodes. Candidate values are: vmss and standard.
|
||||||
// If not set, it will be default to standard.
|
// If not set, it will be default to standard.
|
||||||
VMType string `json:"vmType" yaml:"vmType"`
|
VMType string `json:"vmType,omitempty" yaml:"vmType,omitempty"`
|
||||||
// The name of the scale set that should be used as the load balancer backend.
|
// The name of the scale set that should be used as the load balancer backend.
|
||||||
// If this is set, the Azure cloudprovider will only add nodes from that scale set to the load
|
// If this is set, the Azure cloudprovider will only add nodes from that scale set to the load
|
||||||
// balancer backend pool. If this is not set, and multiple agent pools (scale sets) are used, then
|
// balancer backend pool. If this is not set, and multiple agent pools (scale sets) are used, then
|
||||||
// the cloudprovider will try to add all nodes to a single backend pool which is forbidden.
|
// the cloudprovider will try to add all nodes to a single backend pool which is forbidden.
|
||||||
// In other words, if you use multiple agent pools (scale sets), you MUST set this field.
|
// In other words, if you use multiple agent pools (scale sets), you MUST set this field.
|
||||||
PrimaryScaleSetName string `json:"primaryScaleSetName" yaml:"primaryScaleSetName"`
|
PrimaryScaleSetName string `json:"primaryScaleSetName,omitempty" yaml:"primaryScaleSetName,omitempty"`
|
||||||
// Enable exponential backoff to manage resource request retries
|
// Enable exponential backoff to manage resource request retries
|
||||||
CloudProviderBackoff bool `json:"cloudProviderBackoff" yaml:"cloudProviderBackoff"`
|
CloudProviderBackoff bool `json:"cloudProviderBackoff,omitempty" yaml:"cloudProviderBackoff,omitempty"`
|
||||||
// Backoff retry limit
|
// Backoff retry limit
|
||||||
CloudProviderBackoffRetries int `json:"cloudProviderBackoffRetries" yaml:"cloudProviderBackoffRetries"`
|
CloudProviderBackoffRetries int `json:"cloudProviderBackoffRetries,omitempty" yaml:"cloudProviderBackoffRetries,omitempty"`
|
||||||
// Backoff exponent
|
// Backoff exponent
|
||||||
CloudProviderBackoffExponent float64 `json:"cloudProviderBackoffExponent" yaml:"cloudProviderBackoffExponent"`
|
CloudProviderBackoffExponent float64 `json:"cloudProviderBackoffExponent,omitempty" yaml:"cloudProviderBackoffExponent,omitempty"`
|
||||||
// Backoff duration
|
// Backoff duration
|
||||||
CloudProviderBackoffDuration int `json:"cloudProviderBackoffDuration" yaml:"cloudProviderBackoffDuration"`
|
CloudProviderBackoffDuration int `json:"cloudProviderBackoffDuration,omitempty" yaml:"cloudProviderBackoffDuration,omitempty"`
|
||||||
// Backoff jitter
|
// Backoff jitter
|
||||||
CloudProviderBackoffJitter float64 `json:"cloudProviderBackoffJitter" yaml:"cloudProviderBackoffJitter"`
|
CloudProviderBackoffJitter float64 `json:"cloudProviderBackoffJitter,omitempty" yaml:"cloudProviderBackoffJitter,omitempty"`
|
||||||
// Backoff mode, options are v2 and default.
|
// Backoff mode, options are v2 and default.
|
||||||
// * default means two-layer backoff retrying, one in the cloud provider and the other in the Azure SDK.
|
// * default means two-layer backoff retrying, one in the cloud provider and the other in the Azure SDK.
|
||||||
// * v2 means only backoff in the Azure SDK is used. In such mode, CloudProviderBackoffDuration and
|
// * v2 means only backoff in the Azure SDK is used. In such mode, CloudProviderBackoffDuration and
|
||||||
// CloudProviderBackoffJitter are omitted.
|
// CloudProviderBackoffJitter are omitted.
|
||||||
// "default" will be used if not specified.
|
// "default" will be used if not specified.
|
||||||
CloudProviderBackoffMode string `json:"cloudProviderBackoffMode" yaml:"cloudProviderBackoffMode"`
|
CloudProviderBackoffMode string `json:"cloudProviderBackoffMode,omitempty" yaml:"cloudProviderBackoffMode,omitempty"`
|
||||||
// Enable rate limiting
|
// Enable rate limiting
|
||||||
CloudProviderRateLimit bool `json:"cloudProviderRateLimit" yaml:"cloudProviderRateLimit"`
|
CloudProviderRateLimit bool `json:"cloudProviderRateLimit,omitempty" yaml:"cloudProviderRateLimit,omitempty"`
|
||||||
// Rate limit QPS (Read)
|
// Rate limit QPS (Read)
|
||||||
CloudProviderRateLimitQPS float32 `json:"cloudProviderRateLimitQPS" yaml:"cloudProviderRateLimitQPS"`
|
CloudProviderRateLimitQPS float32 `json:"cloudProviderRateLimitQPS,omitempty" yaml:"cloudProviderRateLimitQPS,omitempty"`
|
||||||
// Rate limit Bucket Size
|
// Rate limit Bucket Size
|
||||||
CloudProviderRateLimitBucket int `json:"cloudProviderRateLimitBucket" yaml:"cloudProviderRateLimitBucket"`
|
CloudProviderRateLimitBucket int `json:"cloudProviderRateLimitBucket,omitempty" yaml:"cloudProviderRateLimitBucket,omitempty"`
|
||||||
// Rate limit QPS (Write)
|
// Rate limit QPS (Write)
|
||||||
CloudProviderRateLimitQPSWrite float32 `json:"cloudProviderRateLimitQPSWrite" yaml:"cloudProviderRateLimitQPSWrite"`
|
CloudProviderRateLimitQPSWrite float32 `json:"cloudProviderRateLimitQPSWrite,omitempty" yaml:"cloudProviderRateLimitQPSWrite,omitempty"`
|
||||||
// Rate limit Bucket Size
|
// Rate limit Bucket Size
|
||||||
CloudProviderRateLimitBucketWrite int `json:"cloudProviderRateLimitBucketWrite" yaml:"cloudProviderRateLimitBucketWrite"`
|
CloudProviderRateLimitBucketWrite int `json:"cloudProviderRateLimitBucketWrite,omitempty" yaml:"cloudProviderRateLimitBucketWrite,omitempty"`
|
||||||
|
|
||||||
// Use instance metadata service where possible
|
// Use instance metadata service where possible
|
||||||
UseInstanceMetadata bool `json:"useInstanceMetadata" yaml:"useInstanceMetadata"`
|
UseInstanceMetadata bool `json:"useInstanceMetadata,omitempty" yaml:"useInstanceMetadata,omitempty"`
|
||||||
|
|
||||||
// Sku of Load Balancer and Public IP. Candidate values are: basic and standard.
|
// Sku of Load Balancer and Public IP. Candidate values are: basic and standard.
|
||||||
// If not set, it will be default to basic.
|
// If not set, it will be default to basic.
|
||||||
LoadBalancerSku string `json:"loadBalancerSku" yaml:"loadBalancerSku"`
|
LoadBalancerSku string `json:"loadBalancerSku,omitempty" yaml:"loadBalancerSku,omitempty"`
|
||||||
// ExcludeMasterFromStandardLB excludes master nodes from standard load balancer.
|
// ExcludeMasterFromStandardLB excludes master nodes from standard load balancer.
|
||||||
// If not set, it will be default to true.
|
// If not set, it will be default to true.
|
||||||
ExcludeMasterFromStandardLB *bool `json:"excludeMasterFromStandardLB" yaml:"excludeMasterFromStandardLB"`
|
ExcludeMasterFromStandardLB *bool `json:"excludeMasterFromStandardLB,omitempty" yaml:"excludeMasterFromStandardLB,omitempty"`
|
||||||
// DisableOutboundSNAT disables the outbound SNAT for public load balancer rules.
|
// DisableOutboundSNAT disables the outbound SNAT for public load balancer rules.
|
||||||
// It should only be set when loadBalancerSku is standard. If not set, it will be default to false.
|
// It should only be set when loadBalancerSku is standard. If not set, it will be default to false.
|
||||||
DisableOutboundSNAT *bool `json:"disableOutboundSNAT" yaml:"disableOutboundSNAT"`
|
DisableOutboundSNAT *bool `json:"disableOutboundSNAT,omitempty" yaml:"disableOutboundSNAT,omitempty"`
|
||||||
|
|
||||||
// Maximum allowed LoadBalancer Rule Count is the limit enforced by Azure Load balancer
|
// Maximum allowed LoadBalancer Rule Count is the limit enforced by Azure Load balancer
|
||||||
MaximumLoadBalancerRuleCount int `json:"maximumLoadBalancerRuleCount" yaml:"maximumLoadBalancerRuleCount"`
|
MaximumLoadBalancerRuleCount int `json:"maximumLoadBalancerRuleCount,omitempty" yaml:"maximumLoadBalancerRuleCount,omitempty"`
|
||||||
|
|
||||||
|
// The cloud configure type for Azure cloud provider. Supported values are file, secret and merge.
|
||||||
|
CloudConfigType cloudConfigType `json:"cloudConfigType,omitempty" yaml:"cloudConfigType,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ cloudprovider.Interface = (*Cloud)(nil)
|
var _ cloudprovider.Interface = (*Cloud)(nil)
|
||||||
@ -233,6 +236,28 @@ func NewCloud(configReader io.Reader) (cloudprovider.Interface, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
az := &Cloud{
|
||||||
|
nodeZones: map[string]sets.String{},
|
||||||
|
nodeResourceGroups: map[string]string{},
|
||||||
|
unmanagedNodes: sets.NewString(),
|
||||||
|
routeCIDRs: map[string]string{},
|
||||||
|
}
|
||||||
|
err = az.InitializeCloudFromConfig(config, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return az, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitializeCloudFromConfig initializes the Cloud from config.
|
||||||
|
func (az *Cloud) InitializeCloudFromConfig(config *Config, fromSecret bool) error {
|
||||||
|
// cloud-config not set, return nil so that it would be initialized from secret.
|
||||||
|
if config == nil {
|
||||||
|
klog.Warning("cloud-config is not provided, Azure cloud provider would be initialized from secret")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if config.RouteTableResourceGroup == "" {
|
if config.RouteTableResourceGroup == "" {
|
||||||
config.RouteTableResourceGroup = config.ResourceGroup
|
config.RouteTableResourceGroup = config.ResourceGroup
|
||||||
}
|
}
|
||||||
@ -242,21 +267,43 @@ func NewCloud(configReader io.Reader) (cloudprovider.Interface, error) {
|
|||||||
config.VMType = vmTypeStandard
|
config.VMType = vmTypeStandard
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.CloudConfigType == "" {
|
||||||
|
// The default cloud config type is cloudConfigTypeMerge.
|
||||||
|
config.CloudConfigType = cloudConfigTypeMerge
|
||||||
|
} else {
|
||||||
|
supportedCloudConfigTypes := sets.NewString(
|
||||||
|
string(cloudConfigTypeMerge),
|
||||||
|
string(cloudConfigTypeFile),
|
||||||
|
string(cloudConfigTypeSecret))
|
||||||
|
if !supportedCloudConfigTypes.Has(string(config.CloudConfigType)) {
|
||||||
|
return fmt.Errorf("cloudConfigType %v is not supported, supported values are %v", config.CloudConfigType, supportedCloudConfigTypes.List())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
env, err := auth.ParseAzureEnvironment(config.Cloud)
|
env, err := auth.ParseAzureEnvironment(config.Cloud)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
servicePrincipalToken, err := auth.GetServicePrincipalToken(&config.AzureAuthConfig, env)
|
servicePrincipalToken, err := auth.GetServicePrincipalToken(&config.AzureAuthConfig, env)
|
||||||
if err == auth.ErrorNoAuth {
|
if err == auth.ErrorNoAuth {
|
||||||
if !config.UseInstanceMetadata {
|
// Only controller-manager would lazy-initialize from secret, and credentials are required for such case.
|
||||||
// No credentials provided, useInstanceMetadata should be enabled.
|
if fromSecret {
|
||||||
return nil, fmt.Errorf("useInstanceMetadata must be enabled without Azure credentials")
|
err := fmt.Errorf("No credentials provided for Azure cloud provider")
|
||||||
|
klog.Fatalf("%v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// No credentials provided, useInstanceMetadata should be enabled for Kubelet.
|
||||||
|
// TODO(feiskyer): print different error message for Kubelet and controller-manager, as they're
|
||||||
|
// requiring different credential settings.
|
||||||
|
if !config.UseInstanceMetadata && az.Config.CloudConfigType == cloudConfigTypeFile {
|
||||||
|
return fmt.Errorf("useInstanceMetadata must be enabled without Azure credentials")
|
||||||
}
|
}
|
||||||
|
|
||||||
klog.V(2).Infof("Azure cloud provider is starting without credentials")
|
klog.V(2).Infof("Azure cloud provider is starting without credentials")
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// operationPollRateLimiter.Accept() is a no-op if rate limits are configured off.
|
// operationPollRateLimiter.Accept() is a no-op if rate limits are configured off.
|
||||||
@ -351,28 +398,22 @@ func NewCloud(configReader io.Reader) (cloudprovider.Interface, error) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if config.DisableOutboundSNAT != nil && *config.DisableOutboundSNAT {
|
if config.DisableOutboundSNAT != nil && *config.DisableOutboundSNAT {
|
||||||
return nil, fmt.Errorf("disableOutboundSNAT should only set when loadBalancerSku is standard")
|
return fmt.Errorf("disableOutboundSNAT should only set when loadBalancerSku is standard")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
az := Cloud{
|
az.Config = *config
|
||||||
Config: *config,
|
az.Environment = *env
|
||||||
Environment: *env,
|
az.resourceRequestBackoff = resourceRequestBackoff
|
||||||
nodeZones: map[string]sets.String{},
|
|
||||||
nodeResourceGroups: map[string]string{},
|
|
||||||
unmanagedNodes: sets.NewString(),
|
|
||||||
routeCIDRs: map[string]string{},
|
|
||||||
resourceRequestBackoff: resourceRequestBackoff,
|
|
||||||
}
|
|
||||||
az.metadata, err = NewInstanceMetadataService(metadataURL)
|
az.metadata, err = NewInstanceMetadataService(metadataURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// No credentials provided, InstanceMetadataService would be used for getting Azure resources.
|
// No credentials provided, InstanceMetadataService would be used for getting Azure resources.
|
||||||
// Note that this only applies to Kubelet, controller-manager should configure credentials for managing Azure resources.
|
// Note that this only applies to Kubelet, controller-manager should configure credentials for managing Azure resources.
|
||||||
if servicePrincipalToken == nil {
|
if servicePrincipalToken == nil {
|
||||||
return &az, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize Azure clients.
|
// Initialize Azure clients.
|
||||||
@ -407,52 +448,53 @@ func NewCloud(configReader io.Reader) (cloudprovider.Interface, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if strings.EqualFold(vmTypeVMSS, az.Config.VMType) {
|
if strings.EqualFold(vmTypeVMSS, az.Config.VMType) {
|
||||||
az.vmSet, err = newScaleSet(&az)
|
az.vmSet, err = newScaleSet(az)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
az.vmSet = newAvailabilitySet(&az)
|
az.vmSet = newAvailabilitySet(az)
|
||||||
}
|
}
|
||||||
|
|
||||||
az.vmCache, err = az.newVMCache()
|
az.vmCache, err = az.newVMCache()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
az.lbCache, err = az.newLBCache()
|
az.lbCache, err = az.newLBCache()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
az.nsgCache, err = az.newNSGCache()
|
az.nsgCache, err = az.newNSGCache()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
az.rtCache, err = az.newRouteTableCache()
|
az.rtCache, err = az.newRouteTableCache()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := initDiskControllers(&az); err != nil {
|
if err := initDiskControllers(az); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
return &az, nil
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseConfig returns a parsed configuration for an Azure cloudprovider config file
|
// parseConfig returns a parsed configuration for an Azure cloudprovider config file
|
||||||
func parseConfig(configReader io.Reader) (*Config, error) {
|
func parseConfig(configReader io.Reader) (*Config, error) {
|
||||||
var config Config
|
var config Config
|
||||||
|
|
||||||
if configReader == nil {
|
if configReader == nil {
|
||||||
return &config, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
configContents, err := ioutil.ReadAll(configReader)
|
configContents, err := ioutil.ReadAll(configReader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = yaml.Unmarshal(configContents, &config)
|
err = yaml.Unmarshal(configContents, &config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -470,6 +512,7 @@ func (az *Cloud) Initialize(clientBuilder cloudprovider.ControllerClientBuilder,
|
|||||||
az.eventBroadcaster = record.NewBroadcaster()
|
az.eventBroadcaster = record.NewBroadcaster()
|
||||||
az.eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: az.kubeClient.CoreV1().Events("")})
|
az.eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: az.kubeClient.CoreV1().Events("")})
|
||||||
az.eventRecorder = az.eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "azure-cloud-provider"})
|
az.eventRecorder = az.eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "azure-cloud-provider"})
|
||||||
|
az.InitializeCloudFromSecret()
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadBalancer returns a balancer interface. Also returns true if the interface is supported, false otherwise.
|
// LoadBalancer returns a balancer interface. Also returns true if the interface is supported, false otherwise.
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 The Kubernetes Authors.
|
||||||
|
|
||||||
|
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 azure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/klog"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cloudConfigNamespace = "kube-system"
|
||||||
|
cloudConfigKey = "cloud-config"
|
||||||
|
cloudConfigSecretName = "azure-cloud-provider"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The config type for Azure cloud provider secret. Supported values are:
|
||||||
|
// * file : The values are read from local cloud-config file.
|
||||||
|
// * secret : The values from secret would override all configures from local cloud-config file.
|
||||||
|
// * merge : The values from secret would override only configurations that are explicitly set in the secret. This is the default value.
|
||||||
|
type cloudConfigType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
cloudConfigTypeFile cloudConfigType = "file"
|
||||||
|
cloudConfigTypeSecret cloudConfigType = "secret"
|
||||||
|
cloudConfigTypeMerge cloudConfigType = "merge"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InitializeCloudFromSecret initializes Azure cloud provider from Kubernetes secret.
|
||||||
|
func (az *Cloud) InitializeCloudFromSecret() {
|
||||||
|
config, err := az.getConfigFromSecret()
|
||||||
|
if err != nil {
|
||||||
|
klog.Warningf("Failed to get cloud-config from secret: %v, skip initializing from secret", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if config == nil {
|
||||||
|
// Skip re-initialization if the config is not override.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := az.InitializeCloudFromConfig(config, true); err != nil {
|
||||||
|
klog.Errorf("Failed to initialize Azure cloud provider: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (az *Cloud) getConfigFromSecret() (*Config, error) {
|
||||||
|
// Read config from file and no override, return nil.
|
||||||
|
if az.Config.CloudConfigType == cloudConfigTypeFile {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
secret, err := az.kubeClient.CoreV1().Secrets(cloudConfigNamespace).Get(cloudConfigSecretName, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to get secret %s: %v", cloudConfigSecretName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cloudConfigData, ok := secret.Data[cloudConfigKey]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("cloud-config is not set in the secret (%s)", cloudConfigSecretName)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := Config{}
|
||||||
|
if az.Config.CloudConfigType == "" || az.Config.CloudConfigType == cloudConfigTypeMerge {
|
||||||
|
// Merge cloud config, set default value to existing config.
|
||||||
|
config = az.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
err = yaml.Unmarshal(cloudConfigData, &config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to parse Azure cloud-config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &config, nil
|
||||||
|
}
|
@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 The Kubernetes Authors.
|
||||||
|
|
||||||
|
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 azure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
fakeclient "k8s.io/client-go/kubernetes/fake"
|
||||||
|
"k8s.io/legacy-cloud-providers/azure/auth"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
|
"github.com/Azure/go-autorest/autorest/to"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getTestConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
AzureAuthConfig: auth.AzureAuthConfig{
|
||||||
|
TenantID: "TenantID",
|
||||||
|
SubscriptionID: "SubscriptionID",
|
||||||
|
AADClientID: "AADClientID",
|
||||||
|
AADClientSecret: "AADClientSecret",
|
||||||
|
},
|
||||||
|
ResourceGroup: "ResourceGroup",
|
||||||
|
RouteTableName: "RouteTableName",
|
||||||
|
RouteTableResourceGroup: "RouteTableResourceGroup",
|
||||||
|
Location: "Location",
|
||||||
|
SubnetName: "SubnetName",
|
||||||
|
VnetName: "VnetName",
|
||||||
|
PrimaryAvailabilitySetName: "PrimaryAvailabilitySetName",
|
||||||
|
PrimaryScaleSetName: "PrimaryScaleSetName",
|
||||||
|
LoadBalancerSku: "LoadBalancerSku",
|
||||||
|
ExcludeMasterFromStandardLB: to.BoolPtr(true),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTestCloudConfigTypeSecretConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
AzureAuthConfig: auth.AzureAuthConfig{
|
||||||
|
TenantID: "TenantID",
|
||||||
|
SubscriptionID: "SubscriptionID",
|
||||||
|
},
|
||||||
|
ResourceGroup: "ResourceGroup",
|
||||||
|
RouteTableName: "RouteTableName",
|
||||||
|
RouteTableResourceGroup: "RouteTableResourceGroup",
|
||||||
|
SecurityGroupName: "SecurityGroupName",
|
||||||
|
CloudConfigType: cloudConfigTypeSecret,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTestCloudConfigTypeMergeConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
AzureAuthConfig: auth.AzureAuthConfig{
|
||||||
|
TenantID: "TenantID",
|
||||||
|
SubscriptionID: "SubscriptionID",
|
||||||
|
},
|
||||||
|
ResourceGroup: "ResourceGroup",
|
||||||
|
RouteTableName: "RouteTableName",
|
||||||
|
RouteTableResourceGroup: "RouteTableResourceGroup",
|
||||||
|
SecurityGroupName: "SecurityGroupName",
|
||||||
|
CloudConfigType: cloudConfigTypeMerge,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTestCloudConfigTypeMergeConfigExpected() *Config {
|
||||||
|
config := getTestConfig()
|
||||||
|
config.SecurityGroupName = "SecurityGroupName"
|
||||||
|
config.CloudConfigType = cloudConfigTypeMerge
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetConfigFromSecret(t *testing.T) {
|
||||||
|
emptyConfig := &Config{}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
existingConfig *Config
|
||||||
|
secretConfig *Config
|
||||||
|
expected *Config
|
||||||
|
expectErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Azure config shouldn't be override when cloud config type is file",
|
||||||
|
existingConfig: &Config{
|
||||||
|
ResourceGroup: "ResourceGroup1",
|
||||||
|
CloudConfigType: cloudConfigTypeFile,
|
||||||
|
},
|
||||||
|
secretConfig: getTestConfig(),
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Azure config should be override when cloud config type is secret",
|
||||||
|
existingConfig: getTestCloudConfigTypeSecretConfig(),
|
||||||
|
secretConfig: getTestConfig(),
|
||||||
|
expected: getTestConfig(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Azure config should be override when cloud config type is merge",
|
||||||
|
existingConfig: getTestCloudConfigTypeMergeConfig(),
|
||||||
|
secretConfig: getTestConfig(),
|
||||||
|
expected: getTestCloudConfigTypeMergeConfigExpected(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Error should be reported when secret doesn't exists",
|
||||||
|
existingConfig: getTestCloudConfigTypeMergeConfig(),
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Error should be reported when secret exists but cloud-config data is not provided",
|
||||||
|
existingConfig: getTestCloudConfigTypeMergeConfig(),
|
||||||
|
secretConfig: emptyConfig,
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
az := &Cloud{
|
||||||
|
kubeClient: fakeclient.NewSimpleClientset(),
|
||||||
|
}
|
||||||
|
if test.existingConfig != nil {
|
||||||
|
az.Config = *test.existingConfig
|
||||||
|
}
|
||||||
|
if test.secretConfig != nil {
|
||||||
|
secret := &v1.Secret{
|
||||||
|
Type: v1.SecretTypeOpaque,
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "azure-cloud-provider",
|
||||||
|
Namespace: "kube-system",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if test.secretConfig != emptyConfig {
|
||||||
|
secretData, err := yaml.Marshal(test.secretConfig)
|
||||||
|
assert.NoError(t, err, test.name)
|
||||||
|
secret.Data = map[string][]byte{
|
||||||
|
"cloud-config": secretData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err := az.kubeClient.CoreV1().Secrets(cloudConfigNamespace).Create(secret)
|
||||||
|
assert.NoError(t, err, test.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
real, err := az.getConfigFromSecret()
|
||||||
|
if test.expectErr {
|
||||||
|
assert.Error(t, err, test.name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NoError(t, err, test.name)
|
||||||
|
assert.Equal(t, test.expected, real, test.name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user