Skip to content

Ipv6 implementation #410

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
May 8, 2025
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
19 changes: 2 additions & 17 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,8 @@ _artifacts/
config/default/manager_image_patch.yaml-e
config/default/manager_pull_policy.yaml-e
test/e2e/config/e2e_conf-envsubst.yaml
test/e2e/data/infrastructure-oci/v1beta1/cluster-template-antrea.yaml
test/e2e/data/infrastructure-oci/v1beta1/cluster-template-ccm-testing.yaml
test/e2e/data/infrastructure-oci/v1beta1/cluster-template-kcp-remediation.yaml
test/e2e/data/infrastructure-oci/v1beta1/cluster-template-md-remediation.yaml
test/e2e/data/infrastructure-oci/v1beta1/cluster-template-node-drain.yaml
test/e2e/data/infrastructure-oci/v1beta1/cluster-template-oracle-linux.yaml
test/e2e/data/infrastructure-oci/v1beta1/cluster-template.yaml
test/e2e/data/infrastructure-oci/v1beta1/cluster-template-bare-metal.yaml
test/e2e/data/infrastructure-oci/v1beta1/cluster-template-custom-networking-seclist.yaml
test/e2e/data/infrastructure-oci/v1beta1/cluster-template-custom-networking-nsg.yaml
test/e2e/data/infrastructure-oci/v1beta1/cluster-template-multiple-node-nsg.yaml
test/e2e/data/infrastructure-oci/v1beta1/cluster-template-local-vcn-peering.yaml
test/e2e/data/infrastructure-oci/v1beta1/cluster-template-cluster-class.yaml
test/e2e/data/infrastructure-oci/v1beta1/cluster-template-remote-vcn-peering.yaml
test/e2e/data/infrastructure-oci/v1beta1/cluster-template-externally-managed-vcn.yaml
test/e2e/data/infrastructure-oci/v1beta1/cluster-template-alternative-region.yaml
test/e2e/data/infrastructure-oci/v1beta1/cluster-template-machine-pool.yaml
test/e2e/data/infrastructure-oci/v1beta*/cluster-template*.yaml


# tilt
tilt-settings.json
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@ generate-e2e-templates: $(KUSTOMIZE)
$(KUSTOMIZE) build $(OCI_TEMPLATES)/v1beta2/cluster-template-windows-calico --load-restrictor LoadRestrictionsNone > $(OCI_TEMPLATES)/v1beta2/cluster-template-windows-calico.yaml
$(KUSTOMIZE) build $(OCI_TEMPLATES)/v1beta2/cluster-template-managed-virtual --load-restrictor LoadRestrictionsNone > $(OCI_TEMPLATES)/v1beta2/cluster-template-managed-virtual.yaml
$(KUSTOMIZE) build $(OCI_TEMPLATES)/v1beta2/cluster-template-managed-self-managed-nodes --load-restrictor LoadRestrictionsNone > $(OCI_TEMPLATES)/v1beta2/cluster-template-managed-self-managed-nodes.yaml
$(KUSTOMIZE) build $(OCI_TEMPLATES)/v1beta2/cluster-template-machine-with-ipv6 --load-restrictor LoadRestrictionsNone > $(OCI_TEMPLATES)/v1beta2/cluster-template-machine-with-ipv6.yaml
$(KUSTOMIZE) build $(OCI_TEMPLATES)/v1beta2/cluster-template-with-paravirt-bv --load-restrictor LoadRestrictionsNone > $(OCI_TEMPLATES)/v1beta2/cluster-template-with-paravirt-bv.yaml

.PHONY: test-e2e-run
test-e2e-run: generate-e2e-templates $(GINKGO) $(ENVSUBST) ## Run e2e tests
Expand Down
24 changes: 21 additions & 3 deletions api/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ type NetworkDetails struct {
// SubnetId defines the ID of the subnet to use. This parameter takes priority over SubnetName.
SubnetId *string `json:"subnetId,omitempty"`

// IPv6 defines if the instance should have an IPv6
AssignIpv6Ip bool `json:"assignIpv6Ip,omitempty"`

// AssignPublicIp defines whether the instance should have a public IP address
AssignPublicIp bool `json:"assignPublicIp,omitempty"`

Expand Down Expand Up @@ -866,6 +869,13 @@ type Subnet struct {
// within this subnet (for example, `bminstance1.subnet123.vcn1.oraclevcn.com`).
// +optional
DnsLabel *string `json:"dnsLabel,omitempty"`

// Use this to enable IPv6 hextet for this subnet. The VCN must be enabled for IPv6.
// You can't change this subnet characteristic later. All subnets are /64 in size. The subnet
// portion of the IPv6 address is the fourth hextet from the left (1111 in the following example).
// Example: `2001:0db8:0123:1111::/64`
// +optional
Ipv6CidrBlockHextet *string `json:"ipv6CidrBlockHextet,omitempty"`
}

// NSG defines configuration for a Network Security Group.
Expand Down Expand Up @@ -942,6 +952,14 @@ type VCN struct {
// within this subnet (for example, `bminstance1.subnet123.vcn1.oraclevcn.com`).
// +optional
DnsLabel *string `json:"dnsLabel,omitempty"`

// Configuration to allow OCI to assign IPv6 Prefix. When true will use a /56 IPv6 global unicast address (GUA) prefix allocated by Oracle
// +optional
IsOracleGuaAllocationEnabled *bool `json:"isOracleGuaAllocationEnabled,omitempty"`

// Configuration to enable IPv6.
// +optional
IsIpv6Enabled *bool `json:"isIpv6Enabled,omitempty"`
}

// LoadBalancer Configuration
Expand Down Expand Up @@ -1097,7 +1115,7 @@ type RemotePeeringConnection struct {
type VolumeType string

const (
IscsiType VolumeType = "iscsi"
IscsiType VolumeType = "iscsi"
ParavirtualizedType VolumeType = "paravirtualized"
)

Expand All @@ -1115,7 +1133,7 @@ type LaunchVolumeAttachment struct {
Type VolumeType `json:"volumeType,omitempty"`

// The details of iscsi volume attachment.
IscsiAttachment LaunchIscsiVolumeAttachment `json:"launchIscsiVolumeAttachment,omitempty"`
IscsiAttachment LaunchIscsiVolumeAttachment `json:"launchIscsiVolumeAttachment,omitempty"`
ParavirtualizedAttachment LaunchParavirtualizedVolumeAttachment `json:"launchParavirtualizedVolumeAttachment,omitempty"`
}

Expand Down Expand Up @@ -1177,7 +1195,7 @@ type LaunchParavirtualizedVolumeAttachment struct {

// LaunchCreateVolumeFromAttributes The details of the volume to create for CreateVolume operation.
LaunchCreateVolumeFromAttributes LaunchCreateVolumeFromAttributes `json:"launchCreateVolumeFromAttributes,omitempty"`

// Refer the top-level definition of isPvEncryptionInTransitEnabled.
// The default value is False.
IsPvEncryptionInTransitEnabled *bool `json:"isPvEncryptionInTransitEnabled,omitempty"`
Expand Down
20 changes: 20 additions & 0 deletions api/v1beta2/ocicluster_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,26 @@ func (c *OCICluster) ValidateCreate() (admission.Warnings, error) {
clusterlogger.Info("validate update cluster", "name", c.Name)

var allErrs field.ErrorList
var ipv6hextets []*string
var hextatassigned bool

ipv6hextets = append(ipv6hextets, c.GetControlPlaneEndpointSubnet().Ipv6CidrBlockHextet, c.GetControlPlaneMachineSubnet().Ipv6CidrBlockHextet, c.GetServiceLoadBalancerSubnet().Ipv6CidrBlockHextet, c.GetNodeSubnet()[0].Ipv6CidrBlockHextet)

for _, hextet := range ipv6hextets {
if hextet != nil {
hextatassigned = true
break
}
}

if hextatassigned {
if c.Spec.NetworkSpec.Vcn.IsIpv6Enabled == nil {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "NetworkSpec.Vcn.IsIpv6Enabled"), c.Spec.NetworkSpec.Vcn.IsIpv6Enabled, "field needs to be true and not nil"))
}
if c.Spec.NetworkSpec.Vcn.IsIpv6Enabled == common.Bool(false) {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "NetworkSpec.Vcn.IsIpv6Enabled"), c.Spec.NetworkSpec.Vcn.IsIpv6Enabled, "field needs to be true"))
}
}

allErrs = append(allErrs, c.validate(nil)...)

Expand Down
24 changes: 21 additions & 3 deletions api/v1beta2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ type NetworkDetails struct {
// SubnetId defines the ID of the subnet to use. This parameter takes priority over SubnetName.
SubnetId *string `json:"subnetId,omitempty"`

// AssignIPv6 determines whether to assign a IPv6 address to the instance.
// +optional
AssignIpv6Ip bool `json:"assignIpv6Ip,omitempty"`

// AssignPublicIp defines whether the instance should have a public IP address
AssignPublicIp bool `json:"assignPublicIp,omitempty"`

Expand Down Expand Up @@ -865,6 +869,13 @@ type Subnet struct {
// within this subnet (for example, `bminstance1.subnet123.vcn1.oraclevcn.com`).
// +optional
DnsLabel *string `json:"dnsLabel,omitempty"`

// Use this to enable IPv6 hextet for this subnet. The VCN must be enabled for IPv6.
// You can't change this subnet characteristic later. All subnets are /64 in size. The subnet
// portion of the IPv6 address is the fourth hextet from the left (1111 in the following example).
// Example: `2001:0db8:0123:1111::/64`
// +optional
Ipv6CidrBlockHextet *string `json:"ipv6CidrBlockHextet,omitempty"`
}

// NSG defines configuration for a Network Security Group.
Expand Down Expand Up @@ -935,6 +946,14 @@ type VCN struct {
// within this subnet (for example, `bminstance1.subnet123.vcn1.oraclevcn.com`).
// +optional
DnsLabel *string `json:"dnsLabel,omitempty"`

// Configuration to allow OCI to assign IPv6 Prefix. When true will use a /56 IPv6 global unicast address (GUA) prefix allocated by Oracle
// +optional
IsOracleGuaAllocationEnabled *bool `json:"isOracleGuaAllocationEnabled,omitempty"`

// Configuration to enable IPv6.
// +optional
IsIpv6Enabled *bool `json:"isIpv6Enabled,omitempty"`
}

// LoadBalancerType is an enumeration of the supported load balancer types.
Expand Down Expand Up @@ -1167,7 +1186,7 @@ type NetworkSecurityGroup struct {
type VolumeType string

const (
IscsiType VolumeType = "iscsi"
IscsiType VolumeType = "iscsi"
ParavirtualizedType VolumeType = "paravirtualized"
)

Expand All @@ -1186,7 +1205,7 @@ type LaunchVolumeAttachment struct {
Type VolumeType `json:"volumeType,omitempty"`

// The details of iscsi volume attachment.
IscsiAttachment LaunchIscsiVolumeAttachment `json:"launchIscsiVolumeAttachment,omitempty"`
IscsiAttachment LaunchIscsiVolumeAttachment `json:"launchIscsiVolumeAttachment,omitempty"`
ParavirtualizedAttachment LaunchParavirtualizedVolumeAttachment `json:"launchParavirtualizedVolumeAttachment,omitempty"`
}

Expand Down Expand Up @@ -1254,7 +1273,6 @@ type LaunchParavirtualizedVolumeAttachment struct {
IsPvEncryptionInTransitEnabled *bool `json:"isPvEncryptionInTransitEnabled,omitempty"`
}


// LaunchCreateVolumeFromAttributes The details of the volume to create for CreateVolume operation.
type LaunchCreateVolumeFromAttributes struct {

Expand Down
15 changes: 8 additions & 7 deletions cloud/scope/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ func (m *MachineScope) GetOrCreateMachine(ctx context.Context) (*core.Instance,
SourceDetails: sourceDetails,
CreateVnicDetails: &core.CreateVnicDetails{
SubnetId: subnetId,
AssignIpv6Ip: common.Bool(m.OCIMachine.Spec.NetworkDetails.AssignIpv6Ip),
AssignPublicIp: common.Bool(m.OCIMachine.Spec.NetworkDetails.AssignPublicIp),
FreeformTags: tags,
DefinedTags: definedTags,
Expand Down Expand Up @@ -1023,13 +1024,13 @@ func getIscsiVolumeAttachment(attachment infrastructurev1beta2.LaunchIscsiVolume

func getParavirtualizedVolumeAttachment(attachment infrastructurev1beta2.LaunchParavirtualizedVolumeAttachment) core.LaunchAttachVolumeDetails {
volumeDetails := core.LaunchAttachParavirtualizedVolumeDetails{
Device: attachment.Device,
DisplayName: attachment.DisplayName,
IsShareable: attachment.IsShareable,
IsReadOnly: attachment.IsReadOnly,
VolumeId: attachment.VolumeId,
IsPvEncryptionInTransitEnabled: attachment.IsPvEncryptionInTransitEnabled,
LaunchCreateVolumeDetails: getLaunchCreateVolumeDetails(attachment.LaunchCreateVolumeFromAttributes),
Device: attachment.Device,
DisplayName: attachment.DisplayName,
IsShareable: attachment.IsShareable,
IsReadOnly: attachment.IsReadOnly,
VolumeId: attachment.VolumeId,
IsPvEncryptionInTransitEnabled: attachment.IsPvEncryptionInTransitEnabled,
LaunchCreateVolumeDetails: getLaunchCreateVolumeDetails(attachment.LaunchCreateVolumeFromAttributes),
}
return volumeDetails
}
Expand Down
12 changes: 12 additions & 0 deletions cloud/scope/route_table_reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,18 @@ func (s *ClusterScope) CreateRouteTable(ctx context.Context, routeTableType stri
Description: common.String("traffic to/from internet"),
},
}
resp, err := s.VCNClient.GetVcn(ctx, core.GetVcnRequest{VcnId: s.getVcnId()})
if err != nil {
panic(err)
}
if resp.Vcn.Ipv6CidrBlocks != nil {
routeRules = append(routeRules, core.RouteRule{
DestinationType: core.RouteRuleDestinationTypeCidrBlock,
Destination: common.String("::/0"),
NetworkEntityId: s.OCIClusterAccessor.GetNetworkSpec().Vcn.InternetGateway.Id,
Description: common.String("ipv6 traffic to/from internet"),
})
}
routeTableName = PublicRouteTableName
}
vcnId := s.getVcnId()
Expand Down
38 changes: 38 additions & 0 deletions cloud/scope/subnet_reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ package scope

import (
"context"
"fmt"
"net"
"strings"

infrastructurev1beta2 "github.com/oracle/cluster-api-provider-oci/api/v1beta2"
"github.com/oracle/cluster-api-provider-oci/cloud/ociutil"
Expand Down Expand Up @@ -101,6 +104,39 @@ func (s *ClusterScope) CreateSubnet(ctx context.Context, spec infrastructurev1be
} else {
routeTable = s.getRouteTableId(infrastructurev1beta2.Public)
}

resp, err := s.VCNClient.GetVcn(ctx, core.GetVcnRequest{VcnId: s.getVcnId()})

var ipv6subnetCIDR_Ptr *string

// Constructing IPv6 Subnet CIDR
if resp.Vcn.Ipv6CidrBlocks != nil {

// VCNs can have multiple IPv6 CIDR Blocks, and the CIDR block with IPv6 GUA Allocated by Oracle is the first (index 0) in the list
vcnCIDR := resp.Vcn.Ipv6CidrBlocks[0]

// Split CIDR block into hextets
ip, _, err := net.ParseCIDR(vcnCIDR)
if err != nil {
panic(err)
}
hextets := strings.Split(ip.String(), ":")

// Modify the 4th hextet (index 3) of vcn CIDR to reflect the subnet CIDR with Ipv6CidrBlockHextet value in it
if len(hextets) >= 4 {
originalHextet := hextets[3]
if len(originalHextet) < 4 {
originalHextet = fmt.Sprintf("%04s", originalHextet)
}
newHextet := originalHextet[:2] + *spec.Ipv6CidrBlockHextet
hextets[3] = newHextet

// Reconstruct the IPv6 address with a /64 CIDR for the subnet
ipv6subnetCIDR := strings.Join(hextets, ":") + "/64"
ipv6subnetCIDR_Ptr = &ipv6subnetCIDR
}
}

createSubnetDetails := core.CreateSubnetDetails{
CompartmentId: common.String(s.GetCompartmentId()),
CidrBlock: common.String(spec.CIDR),
Expand All @@ -112,7 +148,9 @@ func (s *ClusterScope) CreateSubnet(ctx context.Context, spec infrastructurev1be
FreeformTags: s.GetFreeFormTags(),
DefinedTags: s.GetDefinedTags(),
DnsLabel: spec.DnsLabel,
Ipv6CidrBlock: ipv6subnetCIDR_Ptr,
}

if spec.SecurityList != nil {
createSubnetDetails.SecurityListIds = []string{*spec.SecurityList.ID}
}
Expand Down
8 changes: 8 additions & 0 deletions cloud/scope/vcn_reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,15 @@ func (s *ClusterScope) CreateVCN(ctx context.Context, spec infrastructurev1beta2
FreeformTags: s.GetFreeFormTags(),
DefinedTags: s.GetDefinedTags(),
DnsLabel: spec.DnsLabel,
IsIpv6Enabled: spec.IsIpv6Enabled,
}

if spec.IsIpv6Enabled != nil {
if spec.IsIpv6Enabled == common.Bool(true) {
vcnDetails.IsOracleGuaAllocationEnabled = common.Bool(true)
}
}

vcnResponse, err := s.VCNClient.CreateVcn(ctx, core.CreateVcnRequest{
CreateVcnDetails: vcnDetails,
OpcRetryToken: ociutil.GetOPCRetryToken("%s-%s", "create-vcn", string(s.OCIClusterAccessor.GetOCIResourceIdentifier())),
Expand Down
14 changes: 8 additions & 6 deletions cloud/scope/vcn_reconciler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,10 @@ func TestClusterScope_CreateVCN(t *testing.T) {
spec: infrastructurev1beta2.OCIClusterSpec{
NetworkSpec: infrastructurev1beta2.NetworkSpec{
Vcn: infrastructurev1beta2.VCN{
Name: "normal",
DnsLabel: common.String("label"),
CIDR: "test-cidr",
Name: "normal",
DnsLabel: common.String("label"),
CIDR: "test-cidr",
IsIpv6Enabled: common.Bool(true),
},
},
},
Expand All @@ -85,9 +86,10 @@ func TestClusterScope_CreateVCN(t *testing.T) {
spec: infrastructurev1beta2.OCIClusterSpec{
NetworkSpec: infrastructurev1beta2.NetworkSpec{
Vcn: infrastructurev1beta2.VCN{
Name: "normal",
DnsLabel: common.String("label"),
CIDRS: []string{"test-cidr1", "test-cidr2"},
Name: "normal",
DnsLabel: common.String("label"),
CIDRS: []string{"test-cidr1", "test-cidr2"},
IsIpv6Enabled: common.Bool(true),
},
},
},
Expand Down
12 changes: 12 additions & 0 deletions config/crd/bases/infrastructure.cluster.x-k8s.io_ociclusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ spec:
vcn:
description: VCN configuration.
properties:
isIpv6Enabled:
description: isIpv6Enabled enables the possibility to assign IPv6 to cluster nodes.
type: boolean
cidr:
description: VCN CIDR. Deprecated, please use NetworkDetails.cidrs
type: string
Expand Down Expand Up @@ -591,6 +594,9 @@ spec:
DNS label to form a fully qualified domain name (FQDN)
for each VNIC within this subnet (for example, `bminstance1.subnet123.vcn1.oraclevcn.com`).
type: string
ipv6CidrBlockHextet:
description: Subnet IPv6 CIDR.
type: string
id:
description: Subnet OCID.
type: string
Expand Down Expand Up @@ -1382,6 +1388,9 @@ spec:
vcn:
description: VCN configuration.
properties:
isIpv6Enabled:
description: isIpv6Enabled enables the possibility to assign IPv6 to cluster nodes.
type: boolean
cidr:
description: VCN CIDR. Deprecated, please use NetworkDetails.cidrs
type: string
Expand Down Expand Up @@ -1866,6 +1875,9 @@ spec:
DNS label to form a fully qualified domain name (FQDN)
for each VNIC within this subnet (for example, `bminstance1.subnet123.vcn1.oraclevcn.com`).
type: string
ipv6CidrBlockHextet:
description: Subnet IPv6 CIDR.
type: string
id:
description: Subnet OCID.
type: string
Expand Down
Loading