Skip to content
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: 1 addition & 5 deletions api/v1alpha3/azurecluster_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func (src *AzureCluster) ConvertTo(dstRaw conversion.Hub) error { // nolint
dst.Spec.NetworkSpec.APIServerLB.FrontendIPsCount = restored.Spec.NetworkSpec.APIServerLB.FrontendIPsCount
dst.Spec.NetworkSpec.NodeOutboundLB = restored.Spec.NetworkSpec.NodeOutboundLB
dst.Spec.CloudProviderConfigOverrides = restored.Spec.CloudProviderConfigOverrides
dst.Spec.BastionSpec = restored.Spec.BastionSpec

// Here we manually restore outbound security rules. Since v1alpha3 only supports ingress ("Inbound") rules, all v1alpha4 outbound rules are dropped when an AzureCluster
// is converted to v1alpha3. We loop through all security group rules. For all previously existing outbound rules we restore the full rule.
Expand Down Expand Up @@ -95,11 +96,6 @@ func (dst *AzureCluster) ConvertFrom(srcRaw conversion.Hub) error { // nolint
return err
}

// Preserve Hub data on down-conversion.
if err := utilconversion.MarshalData(src, dst); err != nil {
return err
}

return nil
}

Expand Down
1 change: 1 addition & 0 deletions api/v1alpha3/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 38 additions & 0 deletions api/v1alpha4/azurecluster_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ const (
DefaultControlPlaneSubnetCIDR = "10.0.0.0/16"
// DefaultNodeSubnetCIDR is the default Node Subnet CIDR
DefaultNodeSubnetCIDR = "10.1.0.0/16"
// DefaultAzureBastionSubnetCIDR is the default Subnet CIDR for AzureBastion
DefaultAzureBastionSubnetCIDR = "10.255.255.224/27"
// DefaultAzureBastionSubnetName is the default Subnet Name for AzureBastion
DefaultAzureBastionSubnetName = "AzureBastionSubnet"
// DefaultInternalLBIPAddress is the default internal load balancer ip address
DefaultInternalLBIPAddress = "10.0.0.100"
// DefaultAzureCloud is the public cloud that will be used by most users
Expand All @@ -43,6 +47,7 @@ func (c *AzureCluster) setDefaults() {

func (c *AzureCluster) setNetworkSpecDefaults() {
c.setVnetDefaults()
c.setBastionDefaults()
c.setSubnetDefaults()
c.setAPIServerLBDefaults()
c.setNodeOutboundLBDefaults()
Expand Down Expand Up @@ -204,6 +209,29 @@ func (c *AzureCluster) setNodeOutboundLBDefaults() {
}
}

func (c *AzureCluster) setBastionDefaults() {
if c.Spec.BastionSpec.AzureBastion != nil {
if c.Spec.BastionSpec.AzureBastion.Name == "" {
c.Spec.BastionSpec.AzureBastion.Name = generateAzureBastionName(c.ObjectMeta.Name)
}
// Ensure defaults for the Subnet settings.
{
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we default security group and role here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default is no security group and no role.
The fields are there because they belong to the SubnetSpec type.

if c.Spec.BastionSpec.AzureBastion.Subnet.Name == "" {
c.Spec.BastionSpec.AzureBastion.Subnet.Name = DefaultAzureBastionSubnetName
}
if len(c.Spec.BastionSpec.AzureBastion.Subnet.CIDRBlocks) == 0 {
c.Spec.BastionSpec.AzureBastion.Subnet.CIDRBlocks = []string{DefaultAzureBastionSubnetCIDR}
}
}
// Ensure defaults for the PublicIP settings.
{
if c.Spec.BastionSpec.AzureBastion.PublicIP.Name == "" {
c.Spec.BastionSpec.AzureBastion.PublicIP.Name = generateAzureBastionPublicIPName(c.ObjectMeta.Name)
}
}
}
}

// generateVnetName generates a virtual network name, based on the cluster name.
func generateVnetName(clusterName string) string {
return fmt.Sprintf("%s-%s", clusterName, "vnet")
Expand All @@ -219,6 +247,16 @@ func generateNodeSubnetName(clusterName string) string {
return fmt.Sprintf("%s-%s", clusterName, "node-subnet")
}

// generateAzureBastionName generates an azure bastion name.
func generateAzureBastionName(clusterName string) string {
return fmt.Sprintf("%s-azure-bastion", clusterName)
}

// generateAzureBastionPublicIPName generates an azure bastion public ip name.
func generateAzureBastionPublicIPName(clusterName string) string {
return fmt.Sprintf("%s-azure-bastion-pip", clusterName)
}

// generateControlPlaneSecurityGroupName generates a control plane security group name, based on the cluster name.
func generateControlPlaneSecurityGroupName(clusterName string) string {
return fmt.Sprintf("%s-%s", clusterName, "controlplane-nsg")
Expand Down
203 changes: 203 additions & 0 deletions api/v1alpha4/azurecluster_default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -910,3 +910,206 @@ func TestNodeOutboundLBDefaults(t *testing.T) {
})
}
}

func TestBastionDefault(t *testing.T) {
cases := map[string]struct {
cluster *AzureCluster
output *AzureCluster
}{
"no bastion set": {
cluster: &AzureCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: AzureClusterSpec{},
},
output: &AzureCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: AzureClusterSpec{},
},
},
"azure bastion enabled with no settings": {
cluster: &AzureCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: AzureClusterSpec{
BastionSpec: BastionSpec{
AzureBastion: &AzureBastion{},
},
},
},
output: &AzureCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: AzureClusterSpec{
BastionSpec: BastionSpec{
AzureBastion: &AzureBastion{
Name: "foo-azure-bastion",
Subnet: SubnetSpec{
Name: "AzureBastionSubnet",
CIDRBlocks: []string{DefaultAzureBastionSubnetCIDR},
},
PublicIP: PublicIPSpec{
Name: "foo-azure-bastion-pip",
},
},
},
},
},
},
"azure bastion enabled with name set": {
cluster: &AzureCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: AzureClusterSpec{
BastionSpec: BastionSpec{
AzureBastion: &AzureBastion{
Name: "my-fancy-name",
},
},
},
},
output: &AzureCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: AzureClusterSpec{
BastionSpec: BastionSpec{
AzureBastion: &AzureBastion{
Name: "my-fancy-name",
Subnet: SubnetSpec{
Name: "AzureBastionSubnet",
CIDRBlocks: []string{DefaultAzureBastionSubnetCIDR},
},
PublicIP: PublicIPSpec{
Name: "foo-azure-bastion-pip",
},
},
},
},
},
},
"azure bastion enabled with subnet partially set": {
cluster: &AzureCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: AzureClusterSpec{
BastionSpec: BastionSpec{
AzureBastion: &AzureBastion{
Subnet: SubnetSpec{},
},
},
},
},
output: &AzureCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: AzureClusterSpec{
BastionSpec: BastionSpec{
AzureBastion: &AzureBastion{
Name: "foo-azure-bastion",
Subnet: SubnetSpec{
Name: "AzureBastionSubnet",
CIDRBlocks: []string{DefaultAzureBastionSubnetCIDR},
},
PublicIP: PublicIPSpec{
Name: "foo-azure-bastion-pip",
},
},
},
},
},
},
"azure bastion enabled with subnet fully set": {
cluster: &AzureCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: AzureClusterSpec{
BastionSpec: BastionSpec{
AzureBastion: &AzureBastion{
Subnet: SubnetSpec{
Name: "my-superfancy-name",
CIDRBlocks: []string{"10.10.0.0/16"},
},
},
},
},
},
output: &AzureCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: AzureClusterSpec{
BastionSpec: BastionSpec{
AzureBastion: &AzureBastion{
Name: "foo-azure-bastion",
Subnet: SubnetSpec{
Name: "my-superfancy-name",
CIDRBlocks: []string{"10.10.0.0/16"},
},
PublicIP: PublicIPSpec{
Name: "foo-azure-bastion-pip",
},
},
},
},
},
},
"azure bastion enabled with public IP name set": {
cluster: &AzureCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: AzureClusterSpec{
BastionSpec: BastionSpec{
AzureBastion: &AzureBastion{
PublicIP: PublicIPSpec{
Name: "my-ultrafancy-pip-name",
},
},
},
},
},
output: &AzureCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: AzureClusterSpec{
BastionSpec: BastionSpec{
AzureBastion: &AzureBastion{
Name: "foo-azure-bastion",
Subnet: SubnetSpec{
Name: "AzureBastionSubnet",
CIDRBlocks: []string{DefaultAzureBastionSubnetCIDR},
},
PublicIP: PublicIPSpec{
Name: "my-ultrafancy-pip-name",
},
},
},
},
},
},
}

for name := range cases {
c := cases[name]
t.Run(name, func(t *testing.T) {
t.Parallel()
c.cluster.setBastionDefaults()
if !reflect.DeepEqual(c.cluster, c.output) {
expected, _ := json.MarshalIndent(c.output, "", "\t")
actual, _ := json.MarshalIndent(c.cluster, "", "\t")
t.Errorf("Expected %s, got %s", string(expected), string(actual))
}
})
}
}
4 changes: 4 additions & 0 deletions api/v1alpha4/azurecluster_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ type AzureClusterSpec struct {
// +optional
AzureEnvironment string `json:"azureEnvironment,omitempty"`

// BastionSpec encapsulates all things related to the Bastions in the cluster.
// +optional
BastionSpec BastionSpec `json:"bastionSpec,omitempty"`

// CloudProviderConfigOverrides is an optional set of configuration values that can be overridden in azure cloud provider config.
// This is only a subset of options that are available in azure cloud provider config.
// Some values for the cloud provider config are inferred from other parts of cluster api provider azure spec, and may not be available for overrides.
Expand Down
8 changes: 8 additions & 0 deletions api/v1alpha4/azurecluster_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ func (c *AzureCluster) ValidateUpdate(oldRaw runtime.Object) error {
)
}

// Allow enabling azure bastion but avoid disabling it.
if old.Spec.BastionSpec.AzureBastion != nil && !reflect.DeepEqual(old.Spec.BastionSpec.AzureBastion, c.Spec.BastionSpec.AzureBastion) {
allErrs = append(allErrs,
field.Invalid(field.NewPath("spec", "BastionSpec", "AzureBastion"),
c.Spec.BastionSpec.AzureBastion, "azure bastion cannot be removed from a cluster"),
)
}

if len(allErrs) == 0 {
return c.validateCluster(old)
}
Expand Down
16 changes: 16 additions & 0 deletions api/v1alpha4/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -571,3 +571,19 @@ const (
// AvailabilitySetRateLimit ...
AvailabilitySetRateLimit = "availabilitySetRateLimit"
)

// BastionSpec specifies how the Bastion feature should be set up for the cluster.
type BastionSpec struct {
// +optional
AzureBastion *AzureBastion `json:"azureBastion,omitempty"`
}

// AzureBastion specifies how the Azure Bastion cloud component should be configured.
type AzureBastion struct {
// +optional
Name string `json:"name,omitempty"`
// +optional
Subnet SubnetSpec `json:"subnet,omitempty"`
// +optional
PublicIP PublicIPSpec `json:"publicIP,omitempty"`
}
Loading