azure: Add support for dns=none

This commit is contained in:
Ciprian Hacman 2023-07-12 21:24:52 +03:00
parent 1675f21b0a
commit 83d14d4343
16 changed files with 386 additions and 115 deletions

View File

@ -42,6 +42,7 @@ import (
nodeidentityos "k8s.io/kops/pkg/nodeidentity/openstack" nodeidentityos "k8s.io/kops/pkg/nodeidentity/openstack"
nodeidentityscw "k8s.io/kops/pkg/nodeidentity/scaleway" nodeidentityscw "k8s.io/kops/pkg/nodeidentity/scaleway"
"k8s.io/kops/upup/pkg/fi/cloudup/awsup" "k8s.io/kops/upup/pkg/fi/cloudup/awsup"
"k8s.io/kops/upup/pkg/fi/cloudup/azure"
"k8s.io/kops/upup/pkg/fi/cloudup/do" "k8s.io/kops/upup/pkg/fi/cloudup/do"
"k8s.io/kops/upup/pkg/fi/cloudup/gce/tpm/gcetpmverifier" "k8s.io/kops/upup/pkg/fi/cloudup/gce/tpm/gcetpmverifier"
"k8s.io/kops/upup/pkg/fi/cloudup/hetzner" "k8s.io/kops/upup/pkg/fi/cloudup/hetzner"
@ -153,6 +154,12 @@ func main() {
setupLog.Error(err, "unable to create verifier") setupLog.Error(err, "unable to create verifier")
os.Exit(1) os.Exit(1)
} }
} else if opt.Server.Provider.Azure != nil {
verifier, err = azure.NewAzureVerifier(ctx, opt.Server.Provider.Azure)
if err != nil {
setupLog.Error(err, "unable to create verifier")
os.Exit(1)
}
} else { } else {
klog.Fatalf("server cloud provider config not provided") klog.Fatalf("server cloud provider config not provided")
} }

View File

@ -18,6 +18,7 @@ package config
import ( import (
"k8s.io/kops/upup/pkg/fi/cloudup/awsup" "k8s.io/kops/upup/pkg/fi/cloudup/awsup"
"k8s.io/kops/upup/pkg/fi/cloudup/azure"
"k8s.io/kops/upup/pkg/fi/cloudup/do" "k8s.io/kops/upup/pkg/fi/cloudup/do"
gcetpm "k8s.io/kops/upup/pkg/fi/cloudup/gce/tpm" gcetpm "k8s.io/kops/upup/pkg/fi/cloudup/gce/tpm"
"k8s.io/kops/upup/pkg/fi/cloudup/hetzner" "k8s.io/kops/upup/pkg/fi/cloudup/hetzner"
@ -73,6 +74,7 @@ type ServerProviderOptions struct {
OpenStack *openstack.OpenStackVerifierOptions `json:"openstack,omitempty"` OpenStack *openstack.OpenStackVerifierOptions `json:"openstack,omitempty"`
DigitalOcean *do.DigitalOceanVerifierOptions `json:"do,omitempty"` DigitalOcean *do.DigitalOceanVerifierOptions `json:"do,omitempty"`
Scaleway *scaleway.ScalewayVerifierOptions `json:"scaleway,omitempty"` Scaleway *scaleway.ScalewayVerifierOptions `json:"scaleway,omitempty"`
Azure *azure.AzureVerifierOptions `json:"azure,omitempty"`
} }
// DiscoveryOptions configures our support for discovery, particularly gossip DNS (i.e. k8s.local) // DiscoveryOptions configures our support for discovery, particularly gossip DNS (i.e. k8s.local)

View File

@ -29,6 +29,7 @@ import (
"k8s.io/kops/pkg/wellknownports" "k8s.io/kops/pkg/wellknownports"
"k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/awsup" "k8s.io/kops/upup/pkg/fi/cloudup/awsup"
"k8s.io/kops/upup/pkg/fi/cloudup/azure"
"k8s.io/kops/upup/pkg/fi/cloudup/do" "k8s.io/kops/upup/pkg/fi/cloudup/do"
"k8s.io/kops/upup/pkg/fi/cloudup/gce/gcediscovery" "k8s.io/kops/upup/pkg/fi/cloudup/gce/gcediscovery"
"k8s.io/kops/upup/pkg/fi/cloudup/gce/tpm/gcetpmsigner" "k8s.io/kops/upup/pkg/fi/cloudup/gce/tpm/gcetpmsigner"
@ -93,6 +94,12 @@ func (b BootstrapClientBuilder) Build(c *fi.NodeupModelBuilderContext) error {
return err return err
} }
authenticator = a authenticator = a
case kops.CloudProviderAzure:
a, err := azure.NewAzureAuthenticator()
if err != nil {
return err
}
authenticator = a
default: default:
return fmt.Errorf("unsupported cloud provider for authenticator %q", b.CloudProvider()) return fmt.Errorf("unsupported cloud provider for authenticator %q", b.CloudProvider())

View File

@ -24,7 +24,7 @@ import (
// UseKopsControllerForNodeBootstrap is true if nodeup should use kops-controller for bootstrapping. // UseKopsControllerForNodeBootstrap is true if nodeup should use kops-controller for bootstrapping.
func UseKopsControllerForNodeBootstrap(cloudProvider kops.CloudProviderID) bool { func UseKopsControllerForNodeBootstrap(cloudProvider kops.CloudProviderID) bool {
return cloudProvider != kops.CloudProviderAzure return true
} }
// UseChallengeCallback is true if we should use a callback challenge during node provisioning with kops-controller. // UseChallengeCallback is true if we should use a callback challenge during node provisioning with kops-controller.

View File

@ -521,17 +521,7 @@ func validateTopology(c *kops.Cluster, topology *kops.TopologySpec, fieldPath *f
} }
if topology.DNS != "" { if topology.DNS != "" {
cloud := c.Spec.GetCloudProvider()
allErrs = append(allErrs, IsValidValue(fieldPath.Child("dns", "type"), &topology.DNS, kops.SupportedDnsTypes)...) allErrs = append(allErrs, IsValidValue(fieldPath.Child("dns", "type"), &topology.DNS, kops.SupportedDnsTypes)...)
if topology.DNS == kops.DNSTypeNone {
switch cloud {
case kops.CloudProviderOpenstack, kops.CloudProviderHetzner, kops.CloudProviderAWS, kops.CloudProviderGCE, kops.CloudProviderDO, kops.CloudProviderScaleway:
// ok
default:
allErrs = append(allErrs, field.Invalid(fieldPath.Child("dns", "type"), topology.DNS, fmt.Sprintf("not supported for %q", c.Spec.GetCloudProvider())))
}
}
} }
return allErrs return allErrs

View File

@ -18,8 +18,10 @@ package azuremodel
import ( import (
"fmt" "fmt"
"strconv"
"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2022-05-01/network" "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2022-05-01/network"
"k8s.io/kops/pkg/wellknownports"
"k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/azuretasks" "k8s.io/kops/upup/pkg/fi/cloudup/azuretasks"
"k8s.io/utils/net" "k8s.io/utils/net"
@ -109,7 +111,7 @@ func (b *NetworkModelBuilder) Build(c *fi.CloudupModelBuilderContext) error {
SourceAddressPrefixes: &k8sAccessIPv4, SourceAddressPrefixes: &k8sAccessIPv4,
SourcePortRange: fi.PtrTo("*"), SourcePortRange: fi.PtrTo("*"),
DestinationAddressPrefix: fi.PtrTo("*"), DestinationAddressPrefix: fi.PtrTo("*"),
DestinationPortRange: fi.PtrTo("443"), DestinationPortRange: fi.PtrTo(strconv.Itoa(wellknownports.KubeAPIServer)),
}) })
} }
if len(k8sAccessIPv6) > 0 { if len(k8sAccessIPv6) > 0 {
@ -122,7 +124,32 @@ func (b *NetworkModelBuilder) Build(c *fi.CloudupModelBuilderContext) error {
SourceAddressPrefixes: &k8sAccessIPv6, SourceAddressPrefixes: &k8sAccessIPv6,
SourcePortRange: fi.PtrTo("*"), SourcePortRange: fi.PtrTo("*"),
DestinationAddressPrefix: fi.PtrTo("*"), DestinationAddressPrefix: fi.PtrTo("*"),
DestinationPortRange: fi.PtrTo("443"), DestinationPortRange: fi.PtrTo(strconv.Itoa(wellknownports.KubeAPIServer)),
})
}
if b.Cluster.UsesNoneDNS() {
// TODO: Limit access to necessary source address prefixes instead of "0.0.0.0/0" and "::/0"
nsgTask.SecurityRules = append(nsgTask.SecurityRules, &azuretasks.NetworkSecurityRule{
Name: fi.PtrTo("AllowKopsController"),
Priority: fi.PtrTo[int32](210),
Access: network.SecurityRuleAccessAllow,
Direction: network.SecurityRuleDirectionInbound,
Protocol: network.SecurityRuleProtocolTCP,
SourceAddressPrefix: fi.PtrTo("0.0.0.0/0"),
SourcePortRange: fi.PtrTo("*"),
DestinationAddressPrefix: fi.PtrTo("*"),
DestinationPortRange: fi.PtrTo(strconv.Itoa(wellknownports.KopsControllerPort)),
})
nsgTask.SecurityRules = append(nsgTask.SecurityRules, &azuretasks.NetworkSecurityRule{
Name: fi.PtrTo("AllowKopsController_v6"),
Priority: fi.PtrTo[int32](211),
Access: network.SecurityRuleAccessAllow,
Direction: network.SecurityRuleDirectionInbound,
Protocol: network.SecurityRuleProtocolTCP,
SourceAddressPrefix: fi.PtrTo("::/0"),
SourcePortRange: fi.PtrTo("*"),
DestinationAddressPrefix: fi.PtrTo("*"),
DestinationPortRange: fi.PtrTo(strconv.Itoa(wellknownports.KopsControllerPort)),
}) })
} }
var nodePortAccessIPv4, nodePortAccessIPv6 []string var nodePortAccessIPv4, nodePortAccessIPv6 []string

View File

@ -1446,13 +1446,7 @@ func (n *nodeUpConfigBuilder) BuildConfig(ig *kops.InstanceGroup, apiserverAddit
} }
} }
case kops.CloudProviderDO, kops.CloudProviderScaleway: case kops.CloudProviderDO, kops.CloudProviderScaleway, kops.CloudProviderGCE, kops.CloudProviderAzure:
// Use any IP address that is found (including public ones)
for _, additionalIP := range apiserverAdditionalIPs {
controlPlaneIPs = append(controlPlaneIPs, additionalIP)
}
case kops.CloudProviderGCE:
// Use any IP address that is found (including public ones) // Use any IP address that is found (including public ones)
for _, additionalIP := range apiserverAdditionalIPs { for _, additionalIP := range apiserverAdditionalIPs {
controlPlaneIPs = append(controlPlaneIPs, additionalIP) controlPlaneIPs = append(controlPlaneIPs, additionalIP)
@ -1460,19 +1454,7 @@ func (n *nodeUpConfigBuilder) BuildConfig(ig *kops.InstanceGroup, apiserverAddit
} }
if cluster.UsesNoneDNS() { if cluster.UsesNoneDNS() {
switch cluster.Spec.GetCloudProvider() { bootConfig.APIServerIPs = controlPlaneIPs
case kops.CloudProviderAWS, kops.CloudProviderHetzner, kops.CloudProviderOpenstack:
bootConfig.APIServerIPs = controlPlaneIPs
case kops.CloudProviderDO, kops.CloudProviderScaleway:
bootConfig.APIServerIPs = controlPlaneIPs
case kops.CloudProviderGCE:
bootConfig.APIServerIPs = controlPlaneIPs
default:
return nil, nil, fmt.Errorf("'none' DNS topology is not supported for cloud %q", cluster.Spec.GetCloudProvider())
}
} else { } else {
// If we do have a fixed IP, we use it (on some clouds, initially) // If we do have a fixed IP, we use it (on some clouds, initially)
switch cluster.Spec.GetCloudProvider() { switch cluster.Spec.GetCloudProvider() {

View File

@ -0,0 +1,104 @@
/*
Copyright 2022 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 (
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"k8s.io/kops/pkg/bootstrap"
)
const AzureAuthenticationTokenPrefix = "x-azure-id "
type azureAuthenticator struct {
}
var _ bootstrap.Authenticator = &azureAuthenticator{}
func NewAzureAuthenticator() (bootstrap.Authenticator, error) {
return &azureAuthenticator{}, nil
}
func (h *azureAuthenticator) CreateToken(body []byte) (string, error) {
m, err := queryInstanceMetadata()
if err != nil {
return "", fmt.Errorf("querying instance metadata: %w", err)
}
// The fully qualified VMSS VM resource ID format is:
// /subscriptions/SUBSCRIPTION_ID/resourceGroups/RESOURCE_GROUP_NAME/providers/Microsoft.Compute/virtualMachineScaleSets/VMSS_NAME/virtualMachines/VMSS_INDEX
r := strings.Split(m.Compute.ResourceID, "/")
if len(r) != 11 || r[7] != "virtualMachineScaleSets" || r[9] != "virtualMachines" {
return "", fmt.Errorf("unexpected resource ID format: %q", m.Compute.ResourceID)
}
vmssName := r[8]
vmssIndex := r[10]
return AzureAuthenticationTokenPrefix + vmssName + " " + vmssIndex, nil
}
type instanceComputeMetadata struct {
ResourceGroupName string `json:"resourceGroupName"`
ResourceID string `json:"resourceId"`
SubscriptionID string `json:"subscriptionId"`
}
type instanceMetadata struct {
Compute *instanceComputeMetadata `json:"compute"`
}
// queryInstanceMetadata queries Azure Instance Metadata Service (IMDS)
// https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service?tabs=linux
func queryInstanceMetadata() (*instanceMetadata, error) {
transport := &http.Transport{Proxy: nil}
client := http.Client{Transport: transport}
req, err := http.NewRequest("GET", "http://169.254.169.254/metadata/instance", nil)
if err != nil {
return nil, fmt.Errorf("creating a new request: %w", err)
}
req.Header.Add("Metadata", "True")
q := req.URL.Query()
q.Add("format", "json")
q.Add("api-version", "2021-02-01")
req.URL.RawQuery = q.Encode()
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("sending request to the instance metadata server: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("reading a response from the metadata server: %w", err)
}
metadata := &instanceMetadata{}
err = json.Unmarshal(body, metadata)
if err != nil {
return nil, fmt.Errorf("unmarshalling instance metadata: %w", err)
}
return metadata, nil
}

View File

@ -28,6 +28,7 @@ import (
type LoadBalancersClient interface { type LoadBalancersClient interface {
CreateOrUpdate(ctx context.Context, resourceGroupName, loadBalancerName string, parameters network.LoadBalancer) error CreateOrUpdate(ctx context.Context, resourceGroupName, loadBalancerName string, parameters network.LoadBalancer) error
List(ctx context.Context, resourceGroupName string) ([]network.LoadBalancer, error) List(ctx context.Context, resourceGroupName string) ([]network.LoadBalancer, error)
Get(ctx context.Context, resourceGroupName string, loadBalancerName string) (*network.LoadBalancer, error)
Delete(ctx context.Context, resourceGroupName, loadBalancerName string) error Delete(ctx context.Context, resourceGroupName, loadBalancerName string) error
} }
@ -53,6 +54,14 @@ func (c *loadBalancersClientImpl) List(ctx context.Context, resourceGroupName st
return l, nil return l, nil
} }
func (c *loadBalancersClientImpl) Get(ctx context.Context, resourceGroupName string, loadBalancerName string) (*network.LoadBalancer, error) {
l, err := c.c.Get(ctx, resourceGroupName, loadBalancerName, "frontendIpConfigurations/publicIpAddress")
if err != nil {
return nil, err
}
return &l, nil
}
func (c *loadBalancersClientImpl) Delete(ctx context.Context, resourceGroupName, loadBalancerName string) error { func (c *loadBalancersClientImpl) Delete(ctx context.Context, resourceGroupName, loadBalancerName string) error {
future, err := c.c.Delete(ctx, resourceGroupName, loadBalancerName) future, err := c.c.Delete(ctx, resourceGroupName, loadBalancerName)
if err != nil { if err != nil {

View File

@ -0,0 +1,145 @@
/*
Copyright 2020 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 (
"context"
"fmt"
"net"
"net/http"
"strconv"
"strings"
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2022-08-01/compute"
"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2022-05-01/network"
"github.com/Azure/go-autorest/autorest/azure/auth"
"k8s.io/kops/pkg/bootstrap"
"k8s.io/kops/pkg/nodeidentity/azure"
"k8s.io/kops/pkg/wellknownports"
)
type AzureVerifierOptions struct {
}
type azureVerifier struct {
client *client
}
var _ bootstrap.Verifier = &azureVerifier{}
func NewAzureVerifier(ctx context.Context, opt *AzureVerifierOptions) (bootstrap.Verifier, error) {
azureClient, err := newClient()
if err != nil {
return nil, err
}
return &azureVerifier{
client: azureClient,
}, nil
}
func (a azureVerifier) VerifyToken(ctx context.Context, rawRequest *http.Request, token string, body []byte, useInstanceIDForNodeName bool) (*bootstrap.VerifyResult, error) {
if !strings.HasPrefix(token, AzureAuthenticationTokenPrefix) {
return nil, fmt.Errorf("incorrect authorization type")
}
v := strings.Split(strings.TrimPrefix(token, AzureAuthenticationTokenPrefix), " ")
if len(v) != 2 {
return nil, fmt.Errorf("incorrect token format")
}
vmssName := v[0]
vmssIndex := v[1]
vm, err := a.client.vmsClient.Get(ctx, a.client.resourceGroup, vmssName, vmssIndex, "")
if err != nil {
return nil, fmt.Errorf("getting info for VMSS virtual machine %q #%s: %w", vmssName, vmssIndex, err)
}
if vm.OsProfile == nil || *vm.OsProfile.ComputerName == "" {
return nil, fmt.Errorf("determining ComputerName for VMSS %q virtual machine #%s", vmssName, vmssIndex)
}
ni, err := a.client.nisClient.GetVirtualMachineScaleSetNetworkInterface(ctx, a.client.resourceGroup, vmssName, vmssIndex, vmssName+"-netconfig", "")
if err != nil {
return nil, fmt.Errorf("getting info for VMSS network interface %q #%s: %w", vmssName, vmssIndex, err)
}
var addrs []string
var challengeEndpoints []string
for _, ipc := range *ni.IPConfigurations {
if ipc.PrivateIPAddress != nil {
addrs = append(addrs, *ipc.PrivateIPAddress)
challengeEndpoints = append(challengeEndpoints, net.JoinHostPort(*ipc.PrivateIPAddress, strconv.Itoa(wellknownports.NodeupChallenge)))
}
}
if len(addrs) == 0 {
return nil, fmt.Errorf("determining challenge endpoint for VMSS %q virtual machine #%s", vmssName, vmssIndex)
}
if len(challengeEndpoints) == 0 {
return nil, fmt.Errorf("determining challenge endpoint for VMSS %q virtual machine #%s", vmssName, vmssIndex)
}
result := &bootstrap.VerifyResult{
NodeName: *vm.OsProfile.ComputerName,
CertificateNames: addrs,
ChallengeEndpoint: challengeEndpoints[0],
}
for key, value := range vm.Tags {
if key == azure.InstanceGroupNameTag && value != nil {
result.InstanceGroupName = *value
}
}
return result, nil
}
// client is an Azure client.
type client struct {
resourceGroup string
nisClient *network.InterfacesClient
vmsClient *compute.VirtualMachineScaleSetVMsClient
}
// newClient returns a new Client.
func newClient() (*client, error) {
m, err := queryInstanceMetadata()
if err != nil || m == nil {
return nil, fmt.Errorf("getting instance metadata: %w", err)
}
if m.Compute == nil || m.Compute.ResourceGroupName == "" {
return nil, fmt.Errorf("empty resource group name")
}
if m.Compute == nil || m.Compute.SubscriptionID == "" {
return nil, fmt.Errorf("empty subscription name")
}
authorizer, err := auth.NewAuthorizerFromEnvironment()
if err != nil {
return nil, fmt.Errorf("creating authorizer: %w", err)
}
nisClient := network.NewInterfacesClient(m.Compute.SubscriptionID)
nisClient.Authorizer = authorizer
vmsClient := compute.NewVirtualMachineScaleSetVMsClient(m.Compute.SubscriptionID)
vmsClient.Authorizer = authorizer
return &client{
resourceGroup: m.Compute.ResourceGroupName,
nisClient: &nisClient,
vmsClient: &vmsClient,
}, nil
}

View File

@ -19,10 +19,12 @@ package azuretasks
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2022-05-01/network" "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2022-05-01/network"
"github.com/Azure/go-autorest/autorest/to" "github.com/Azure/go-autorest/autorest/to"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/kops/pkg/wellknownports"
"k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/azure" "k8s.io/kops/upup/pkg/fi/cloudup/azure"
) )
@ -58,6 +60,29 @@ func (lb *LoadBalancer) IsForAPIServer() bool {
return lb.ForAPIServer return lb.ForAPIServer
} }
func (lb *LoadBalancer) FindAddresses(c *fi.CloudupContext) ([]string, error) {
cloud := c.T.Cloud.(azure.AzureCloud)
loadbalancer, err := cloud.LoadBalancer().Get(context.TODO(), *lb.ResourceGroup.Name, *lb.Name)
if err != nil && !strings.Contains(err.Error(), "NotFound") {
return nil, err
}
if loadbalancer != nil && loadbalancer.FrontendIPConfigurations != nil && len(*loadbalancer.FrontendIPConfigurations) > 0 {
var addresses []string
for _, fipc := range *loadbalancer.FrontendIPConfigurations {
if fipc.PrivateIPAddress != nil {
addresses = append(addresses, *fipc.PrivateIPAddress)
}
if fipc.PublicIPAddress != nil && fipc.PublicIPAddress.IPAddress != nil {
addresses = append(addresses, *fipc.PublicIPAddress.IPAddress)
}
}
return addresses, nil
}
return nil, nil
}
// Find discovers the LoadBalancer in the cloud provider // Find discovers the LoadBalancer in the cloud provider
func (lb *LoadBalancer) Find(c *fi.CloudupContext) (*LoadBalancer, error) { func (lb *LoadBalancer) Find(c *fi.CloudupContext) (*LoadBalancer, error) {
cloud := c.T.Cloud.(azure.AzureCloud) cloud := c.T.Cloud.(azure.AzureCloud)
@ -151,6 +176,7 @@ func (*LoadBalancer) RenderAzure(t *azure.AzureAPITarget, a, e, changes *LoadBal
ID: to.StringPtr(fmt.Sprintf("/%s/virtualNetworks/%s/subnets/%s", idPrefix, *e.Subnet.VirtualNetwork.Name, *e.Subnet.Name)), ID: to.StringPtr(fmt.Sprintf("/%s/virtualNetworks/%s/subnets/%s", idPrefix, *e.Subnet.VirtualNetwork.Name, *e.Subnet.Name)),
} }
} }
// TODO: Move hardcoded values to the model
lb := network.LoadBalancer{ lb := network.LoadBalancer{
Location: to.StringPtr(t.Cloud.Region()), Location: to.StringPtr(t.Cloud.Region()),
Sku: &network.LoadBalancerSku{ Sku: &network.LoadBalancerSku{
@ -173,7 +199,16 @@ func (*LoadBalancer) RenderAzure(t *azure.AzureAPITarget, a, e, changes *LoadBal
Name: to.StringPtr("Health-TCP-443"), Name: to.StringPtr("Health-TCP-443"),
ProbePropertiesFormat: &network.ProbePropertiesFormat{ ProbePropertiesFormat: &network.ProbePropertiesFormat{
Protocol: network.ProbeProtocolTCP, Protocol: network.ProbeProtocolTCP,
Port: to.Int32Ptr(443), Port: to.Int32Ptr(wellknownports.KubeAPIServer),
IntervalInSeconds: to.Int32Ptr(15),
NumberOfProbes: to.Int32Ptr(4),
},
},
{
Name: to.StringPtr("Health-TCP-3988"),
ProbePropertiesFormat: &network.ProbePropertiesFormat{
Protocol: network.ProbeProtocolTCP,
Port: to.Int32Ptr(wellknownports.KopsControllerPort),
IntervalInSeconds: to.Int32Ptr(15), IntervalInSeconds: to.Int32Ptr(15),
NumberOfProbes: to.Int32Ptr(4), NumberOfProbes: to.Int32Ptr(4),
}, },
@ -184,8 +219,8 @@ func (*LoadBalancer) RenderAzure(t *azure.AzureAPITarget, a, e, changes *LoadBal
Name: to.StringPtr("TCP-443"), Name: to.StringPtr("TCP-443"),
LoadBalancingRulePropertiesFormat: &network.LoadBalancingRulePropertiesFormat{ LoadBalancingRulePropertiesFormat: &network.LoadBalancingRulePropertiesFormat{
Protocol: network.TransportProtocolTCP, Protocol: network.TransportProtocolTCP,
FrontendPort: to.Int32Ptr(443), FrontendPort: to.Int32Ptr(wellknownports.KubeAPIServer),
BackendPort: to.Int32Ptr(443), BackendPort: to.Int32Ptr(wellknownports.KubeAPIServer),
IdleTimeoutInMinutes: to.Int32Ptr(4), IdleTimeoutInMinutes: to.Int32Ptr(4),
EnableFloatingIP: to.BoolPtr(false), EnableFloatingIP: to.BoolPtr(false),
LoadDistribution: network.LoadDistributionDefault, LoadDistribution: network.LoadDistributionDefault,
@ -200,6 +235,26 @@ func (*LoadBalancer) RenderAzure(t *azure.AzureAPITarget, a, e, changes *LoadBal
}, },
}, },
}, },
{
Name: to.StringPtr("TCP-3988"),
LoadBalancingRulePropertiesFormat: &network.LoadBalancingRulePropertiesFormat{
Protocol: network.TransportProtocolTCP,
FrontendPort: to.Int32Ptr(wellknownports.KopsControllerPort),
BackendPort: to.Int32Ptr(wellknownports.KopsControllerPort),
IdleTimeoutInMinutes: to.Int32Ptr(4),
EnableFloatingIP: to.BoolPtr(false),
LoadDistribution: network.LoadDistributionDefault,
FrontendIPConfiguration: &network.SubResource{
ID: to.StringPtr(fmt.Sprintf("/%s/loadbalancers/%s/frontendIPConfigurations/%s", idPrefix, *e.Name, *to.StringPtr("LoadBalancerFrontEnd"))),
},
BackendAddressPool: &network.SubResource{
ID: to.StringPtr(fmt.Sprintf("/%s/loadbalancers/%s/backendAddressPools/%s", idPrefix, *e.Name, *to.StringPtr("LoadBalancerBackEnd"))),
},
Probe: &network.SubResource{
ID: to.StringPtr(fmt.Sprintf("/%s/loadbalancers/%s/probes/%s", idPrefix, *e.Name, *to.StringPtr("Health-TCP-3988"))),
},
},
},
}, },
}, },
Tags: e.Tags, Tags: e.Tags,

View File

@ -576,6 +576,16 @@ func (c *MockLoadBalancersClient) List(ctx context.Context, resourceGroupName st
return l, nil return l, nil
} }
// Get returns a loadbalancer.
func (c *MockLoadBalancersClient) Get(ctx context.Context, resourceGroupName string, loadBalancerName string) (*network.LoadBalancer, error) {
for _, lb := range c.LBs {
if *lb.Name == loadBalancerName {
return nil, nil
}
}
return nil, nil
}
// Delete deletes a specified loadbalancer. // Delete deletes a specified loadbalancer.
func (c *MockLoadBalancersClient) Delete(ctx context.Context, scope, lbName string) error { func (c *MockLoadBalancersClient) Delete(ctx context.Context, scope, lbName string) error {
// Ignore scope for simplicity. // Ignore scope for simplicity.

View File

@ -30,84 +30,6 @@ func TestPrecreateDNSNames(t *testing.T) {
cluster *kops.Cluster cluster *kops.Cluster
expected []recordKey expected []recordKey
}{ }{
{
cluster: &kops.Cluster{
Spec: kops.ClusterSpec{
CloudProvider: kops.CloudProviderSpec{
Azure: &kops.AzureSpec{},
},
},
},
expected: []recordKey{
{"api.cluster1.example.com", rrstype.A},
{"api.internal.cluster1.example.com", rrstype.A},
},
},
{
cluster: &kops.Cluster{
Spec: kops.ClusterSpec{
CloudProvider: kops.CloudProviderSpec{
Azure: &kops.AzureSpec{},
},
Networking: kops.NetworkingSpec{
NonMasqueradeCIDR: "::/0",
},
},
},
expected: []recordKey{
{"api.cluster1.example.com", rrstype.A},
{"api.cluster1.example.com", rrstype.AAAA},
{"api.internal.cluster1.example.com", rrstype.AAAA},
},
},
{
cluster: &kops.Cluster{
Spec: kops.ClusterSpec{
API: kops.APISpec{
LoadBalancer: &kops.LoadBalancerAccessSpec{},
},
CloudProvider: kops.CloudProviderSpec{
Azure: &kops.AzureSpec{},
},
},
},
expected: []recordKey{
{"api.internal.cluster1.example.com", rrstype.A},
},
},
{
cluster: &kops.Cluster{
Spec: kops.ClusterSpec{
API: kops.APISpec{
LoadBalancer: &kops.LoadBalancerAccessSpec{},
},
CloudProvider: kops.CloudProviderSpec{
Azure: &kops.AzureSpec{},
},
Networking: kops.NetworkingSpec{
NonMasqueradeCIDR: "::/0",
},
},
},
expected: []recordKey{
{"api.internal.cluster1.example.com", rrstype.AAAA},
},
},
{
cluster: &kops.Cluster{
Spec: kops.ClusterSpec{
API: kops.APISpec{
LoadBalancer: &kops.LoadBalancerAccessSpec{
UseForInternalAPI: true,
},
},
CloudProvider: kops.CloudProviderSpec{
Azure: &kops.AzureSpec{},
},
},
},
expected: nil,
},
{ {
cluster: &kops.Cluster{ cluster: &kops.Cluster{
Spec: kops.ClusterSpec{ Spec: kops.ClusterSpec{

View File

@ -1321,7 +1321,7 @@ func setupDNSTopology(opt *NewClusterOptions, cluster *api.Cluster) error {
switch strings.ToLower(opt.DNSType) { switch strings.ToLower(opt.DNSType) {
case "": case "":
switch cluster.Spec.GetCloudProvider() { switch cluster.Spec.GetCloudProvider() {
case api.CloudProviderHetzner, api.CloudProviderDO: case api.CloudProviderHetzner, api.CloudProviderDO, api.CloudProviderAzure:
// Use dns=none if not specified // Use dns=none if not specified
cluster.Spec.Networking.Topology.DNS = api.DNSTypeNone cluster.Spec.Networking.Topology.DNS = api.DNSTypeNone
default: default:

View File

@ -62,6 +62,7 @@ import (
"k8s.io/kops/pkg/wellknownports" "k8s.io/kops/pkg/wellknownports"
"k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/awsup" "k8s.io/kops/upup/pkg/fi/cloudup/awsup"
"k8s.io/kops/upup/pkg/fi/cloudup/azure"
"k8s.io/kops/upup/pkg/fi/cloudup/do" "k8s.io/kops/upup/pkg/fi/cloudup/do"
"k8s.io/kops/upup/pkg/fi/cloudup/gce" "k8s.io/kops/upup/pkg/fi/cloudup/gce"
gcetpm "k8s.io/kops/upup/pkg/fi/cloudup/gce/tpm" gcetpm "k8s.io/kops/upup/pkg/fi/cloudup/gce/tpm"
@ -739,6 +740,9 @@ func (tf *TemplateFunctions) KopsControllerConfig() (string, error) {
case kops.CloudProviderScaleway: case kops.CloudProviderScaleway:
config.Server.Provider.Scaleway = &scaleway.ScalewayVerifierOptions{} config.Server.Provider.Scaleway = &scaleway.ScalewayVerifierOptions{}
case kops.CloudProviderAzure:
config.Server.Provider.Azure = &azure.AzureVerifierOptions{}
default: default:
return "", fmt.Errorf("unsupported cloud provider %s", cluster.Spec.GetCloudProvider()) return "", fmt.Errorf("unsupported cloud provider %s", cluster.Spec.GetCloudProvider())
} }

View File

@ -54,6 +54,7 @@ import (
"k8s.io/kops/pkg/wellknownports" "k8s.io/kops/pkg/wellknownports"
"k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/awsup" "k8s.io/kops/upup/pkg/fi/cloudup/awsup"
"k8s.io/kops/upup/pkg/fi/cloudup/azure"
"k8s.io/kops/upup/pkg/fi/cloudup/do" "k8s.io/kops/upup/pkg/fi/cloudup/do"
"k8s.io/kops/upup/pkg/fi/cloudup/gce/gcediscovery" "k8s.io/kops/upup/pkg/fi/cloudup/gce/gcediscovery"
"k8s.io/kops/upup/pkg/fi/cloudup/gce/tpm/gcetpmsigner" "k8s.io/kops/upup/pkg/fi/cloudup/gce/tpm/gcetpmsigner"
@ -776,6 +777,12 @@ func getNodeConfigFromServers(ctx context.Context, bootConfig *nodeup.BootConfig
return nil, err return nil, err
} }
authenticator = a authenticator = a
case api.CloudProviderAzure:
a, err := azure.NewAzureAuthenticator()
if err != nil {
return nil, err
}
authenticator = a
default: default:
return nil, fmt.Errorf("unsupported cloud provider for node configuration %s", bootConfig.CloudProvider) return nil, fmt.Errorf("unsupported cloud provider for node configuration %s", bootConfig.CloudProvider)
} }