Skip to content

Feat: Support VCN Native CNI/Native Pod Networking changes #399

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

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
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
15 changes: 15 additions & 0 deletions api/v1beta1/ocimachine_types.go
Original file line number Diff line number Diff line change
@@ -145,6 +145,21 @@ type OCIMachineSpec struct {
// Specifies whether to delete or preserve the data volumes created during launch when
//terminating an instance. When set to true, the data volumes are preserved. The default value is true.
PreserveDataVolumesCreatedAtLaunch bool `json:"preserveDataVolumesCreatedAtLaunch,omitempty"`

// Specifies the list of pod subnets being used for the VCN IP NATIVE CNI type for pod networking.
// Set on each NPN CR associated with the OCI Machine.
PodSubnetIds []string `json:"podSubnetIds,omitempty"`

// Specifies the maximum number of pods allowed for each node, decided by the shape
MaxPodPerNode int `json:"maxPodCount,omitempty"`

// VCN_IP_NATIVE or FLANNEL_OVERLAY.
// Used for the NPN CR Reconciliation feature gate.
CNIType string `json:"cniType,omitempty"`

// Specifies the list of Network Security Groups used for the VCN IP NATIVE CNI type for pod networking.
// Set on each NPN CR associated with the OCI Machine.
PodNSGIds []string `json:"podNsgIds,omitempty"`
}

// OCIMachineStatus defines the observed state of OCIMachine.
8 changes: 8 additions & 0 deletions api/v1beta1/zz_generated.conversion.go

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

10 changes: 10 additions & 0 deletions api/v1beta1/zz_generated.deepcopy.go

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

15 changes: 15 additions & 0 deletions api/v1beta2/ocimachine_types.go
Original file line number Diff line number Diff line change
@@ -139,6 +139,21 @@ type OCIMachineSpec struct {
// Specifies whether to delete or preserve the data volumes created during launch when
//terminating an instance. When set to true, the data volumes are preserved. The default value is true.
PreserveDataVolumesCreatedAtLaunch bool `json:"preserveDataVolumesCreatedAtLaunch,omitempty"`

// Specifies the list of pod subnets being used for the VCN IP NATIVE CNI type for pod networking.
// Set on each NPN CR associated with the OCI Machine.
PodSubnetIds []string `json:"podSubnetIds,omitempty"`

// Specifies the maximum number of pods allowed for each node, decided by the shape
MaxPodPerNode int `json:"maxPodCount,omitempty"`

// VCN_IP_NATIVE or FLANNEL_OVERLAY.
// Used for the NPN CR Reconciliation feature gate.
CNIType string `json:"cniType,omitempty"`

// Specifies the list of Network Security Groups used for the VCN IP NATIVE CNI type for pod networking.
// Set on each NPN CR associated with the OCI Machine.
PodNSGIds []string `json:"podNsgIds,omitempty"`
}

// OCIMachineStatus defines the observed state of OCIMachine.
10 changes: 10 additions & 0 deletions api/v1beta2/zz_generated.deepcopy.go

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

167 changes: 167 additions & 0 deletions cloud/scope/native_pod_network_reconciler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
Copyright (c) 2022 Oracle and/or its affiliates.

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

https://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 scope

import (
"context"
"fmt"
"strings"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/tools/clientcmd"
"sigs.k8s.io/controller-runtime/pkg/client"
)

const (
npnVersion = "oci.oraclecloud.com/v1beta1"
npnKind = "NativePodNetwork"
apiExtensionVersion = "apiextensions.k8s.io/v1"
npnCrdName = "nativepodnetworks.oci.oraclecloud.com"
)

func (m *MachineScope) NewWorkloadClient(ctx context.Context) (wlClient client.Client, err error) {
u := &unstructured.Unstructured{}
u.SetGroupVersionKind(schema.GroupVersionKind{
Kind: "Secret",
Version: "v1",
})
cluster := m.Cluster

secret_obj := client.ObjectKey{
Namespace: cluster.Namespace,
Name: cluster.Spec.InfrastructureRef.Name + "-kubeconfig",
}
secret := &corev1.Secret{}
if err := m.Client.Get(ctx, secret_obj, secret); err != nil {
return nil, err
}
secretData := secret.Data["value"]
config, err := clientcmd.RESTConfigFromKubeConfig(secretData)
if err != nil {
m.Info(fmt.Sprintf("error build config: %s", err))
return nil, err
}

wlClient, err = client.New(config, client.Options{})

return wlClient, err
}

func (m *MachineScope) DeleteNpn(ctx context.Context) error {
m.Info("DELETE NPN CR NOW.")

wlClient, err := m.NewWorkloadClient(ctx)
if err != nil {
m.Info(fmt.Sprintf("Failed to initialize kube client set: %s", err))
return err
}
instance, err := m.GetOrCreateMachine(ctx)
if err != nil {
m.Info(fmt.Sprintf("Failed to get machine: %s", err))
return err
}
npnCr := &unstructured.Unstructured{}
slicedId := strings.Split(*instance.Id, ".")
instanceSuffix := slicedId[len(slicedId)-1]
npnCr.SetName(instanceSuffix)
npnCr.SetGroupVersionKind(schema.GroupVersionKind{
Version: npnVersion,
Kind: npnKind,
})
if err := wlClient.Delete(ctx, npnCr); err != nil {
m.Info(fmt.Sprintf("Failed to delete NPN CR: %s", err))
return err
}
return nil
}

func (m *MachineScope) HasNpnCrd(ctx context.Context) (bool, error) {
m.Info("Get NPN CRD Now.")

wlClient, err := m.NewWorkloadClient(ctx)
if err != nil {
return false, err
}
npnCrd := &unstructured.Unstructured{}
npnCrd.SetGroupVersionKind(schema.GroupVersionKind{
Version: apiExtensionVersion,
Kind: "CustomResourceDefinition",
})

err = wlClient.Get(context.Background(), client.ObjectKey{
Name: npnCrdName,
}, npnCrd)
if err != nil {
m.Info(fmt.Sprintf("Failed to Get NPN CRD, reason: %v", err))
return false, err
}

return true, nil

}

func (m *MachineScope) GetOrCreateNpn(ctx context.Context) (*unstructured.Unstructured, error) {

m.Info("Get Or Create NPN CR NOW.")
instance, err := m.GetOrCreateMachine(ctx)
if err != nil {
m.Info(fmt.Sprintf("Failed to get machine: %s", err))
return nil, err
}
wlClient, err := m.NewWorkloadClient(ctx)
if err != nil {
return nil, err
}
npnCr := &unstructured.Unstructured{}
slicedId := strings.Split(*instance.Id, ".")
instanceSuffix := slicedId[len(slicedId)-1]
npnCr.SetGroupVersionKind(schema.GroupVersionKind{
Version: npnVersion,
Kind: npnKind,
})
err = wlClient.Get(ctx, client.ObjectKey{Name: instanceSuffix}, npnCr)
// Return NPN CR Object if it existed
if err == nil {
m.Info(fmt.Sprintf("Sucessfully Get an Existed NPN CR Object: %s", npnCr))
return npnCr, nil
}
maxPodCount := m.OCIMachine.Spec.MaxPodPerNode
podSubnetIds := m.OCIMachine.Spec.PodSubnetIds
podNsgIds := m.OCIMachine.Spec.PodNSGIds
npnCrCreate := &unstructured.Unstructured{}
npnCrCreate.Object = map[string]interface{}{
"metadata": map[string]interface{}{
"name": instanceSuffix,
},
"spec": map[string]interface{}{
"id": *instance.Id,
"maxPodCount": maxPodCount,
"podSubnetIds": podSubnetIds,
"networkSecurityGroupIds": podNsgIds,
},
}

npnCrCreate.SetGroupVersionKind(schema.GroupVersionKind{
Version: npnVersion,
Kind: npnKind,
})
m.Info(fmt.Sprintf("NPN CR to Create is: %v", npnCrCreate))
err = wlClient.Create(ctx, npnCrCreate)
return npnCrCreate, err
}
44 changes: 44 additions & 0 deletions config/crd/bases/infrastructure.cluster.x-k8s.io_ocimachines.yaml
Original file line number Diff line number Diff line change
@@ -134,6 +134,10 @@ spec:
input for this field. For more information, see Capacity Reservations
(https://docs.cloud.oracle.com/iaas/Content/Compute/Tasks/reserve-capacity.htm#default).
type: string
cniType:
description: VCN_IP_NATIVE or FLANNEL_OVERLAY. Used for the NPN CR
Reconciliation feature gate.
type: string
compartmentId:
description: Compartment to launch the instance in.
type: string
@@ -399,6 +403,10 @@ spec:
type: string
type: object
type: array
maxPodCount:
description: Specifies the maximum number of pods allowed for each
node, decided by the shape
type: integer
metadata:
additionalProperties:
type: string
@@ -764,6 +772,20 @@ spec:
types must be set
type: string
type: object
podNsgIds:
description: Specifies the list of Network Security Groups used for
the VCN IP NATIVE CNI type for pod networking. Set on each NPN CR
associated with the OCI Machine.
items:
type: string
type: array
podSubnetIds:
description: Specifies the list of pod subnets being used for the
VCN IP NATIVE CNI type for pod networking. Set on each NPN CR associated
with the OCI Machine.
items:
type: string
type: array
preemptibleInstanceConfig:
description: PreemptibleInstanceConfig Configuration options for preemptible
instances.
@@ -1069,6 +1091,10 @@ spec:
input for this field. For more information, see Capacity Reservations
(https://docs.cloud.oracle.com/iaas/Content/Compute/Tasks/reserve-capacity.htm#default).
type: string
cniType:
description: VCN_IP_NATIVE or FLANNEL_OVERLAY. Used for the NPN CR
Reconciliation feature gate.
type: string
compartmentId:
description: Compartment to launch the instance in.
type: string
@@ -1336,6 +1362,10 @@ spec:
type: string
type: object
type: array
maxPodCount:
description: Specifies the maximum number of pods allowed for each
node, decided by the shape
type: integer
metadata:
additionalProperties:
type: string
@@ -1695,6 +1725,20 @@ spec:
types must be set
type: string
type: object
podNsgIds:
description: Specifies the list of Network Security Groups used for
the VCN IP NATIVE CNI type for pod networking. Set on each NPN CR
associated with the OCI Machine.
items:
type: string
type: array
podSubnetIds:
description: Specifies the list of pod subnets being used for the
VCN IP NATIVE CNI type for pod networking. Set on each NPN CR associated
with the OCI Machine.
items:
type: string
type: array
preemptibleInstanceConfig:
description: PreemptibleInstanceConfig Configuration options for preemptible
instances.
Original file line number Diff line number Diff line change
@@ -149,6 +149,10 @@ spec:
an empty string as input for this field. For more information,
see Capacity Reservations (https://docs.cloud.oracle.com/iaas/Content/Compute/Tasks/reserve-capacity.htm#default).
type: string
cniType:
description: VCN_IP_NATIVE or FLANNEL_OVERLAY. Used for the
NPN CR Reconciliation feature gate.
type: string
compartmentId:
description: Compartment to launch the instance in.
type: string
@@ -433,6 +437,10 @@ spec:
type: string
type: object
type: array
maxPodCount:
description: Specifies the maximum number of pods allowed
for each node, decided by the shape
type: integer
metadata:
additionalProperties:
type: string
@@ -829,6 +837,20 @@ spec:
types must be set
type: string
type: object
podNsgIds:
description: Specifies the list of Network Security Groups
used for the VCN IP NATIVE CNI type for pod networking.
Set on each NPN CR associated with the OCI Machine.
items:
type: string
type: array
podSubnetIds:
description: Specifies the list of pod subnets being used
for the VCN IP NATIVE CNI type for pod networking. Set on
each NPN CR associated with the OCI Machine.
items:
type: string
type: array
preemptibleInstanceConfig:
description: PreemptibleInstanceConfig Configuration options
for preemptible instances.
@@ -1074,6 +1096,10 @@ spec:
an empty string as input for this field. For more information,
see Capacity Reservations (https://docs.cloud.oracle.com/iaas/Content/Compute/Tasks/reserve-capacity.htm#default).
type: string
cniType:
description: VCN_IP_NATIVE or FLANNEL_OVERLAY. Used for the
NPN CR Reconciliation feature gate.
type: string
compartmentId:
description: Compartment to launch the instance in.
type: string
@@ -1360,6 +1386,10 @@ spec:
type: string
type: object
type: array
maxPodCount:
description: Specifies the maximum number of pods allowed
for each node, decided by the shape
type: integer
metadata:
additionalProperties:
type: string
@@ -1750,6 +1780,20 @@ spec:
types must be set
type: string
type: object
podNsgIds:
description: Specifies the list of Network Security Groups
used for the VCN IP NATIVE CNI type for pod networking.
Set on each NPN CR associated with the OCI Machine.
items:
type: string
type: array
podSubnetIds:
description: Specifies the list of pod subnets being used
for the VCN IP NATIVE CNI type for pod networking. Set on
each NPN CR associated with the OCI Machine.
items:
type: string
type: array
preemptibleInstanceConfig:
description: PreemptibleInstanceConfig Configuration options
for preemptible instances.
29 changes: 29 additions & 0 deletions controllers/ocimachine_controller.go
Original file line number Diff line number Diff line change
@@ -413,6 +413,22 @@ func (r *OCIMachineReconciler) reconcileNormal(ctx context.Context, logger logr.
"Instance is in ready state")
conditions.MarkTrue(machineScope.OCIMachine, infrastructurev1beta2.InstanceReadyCondition)
machineScope.SetReady()

CNIType := machineScope.OCIMachine.Spec.CNIType
if CNIType == "OCI_VCN_IP_NATIVE" {
machineScope.Info(fmt.Sprintf("CNI Type is: %s", CNIType))
if crdExsited, _ := machineScope.HasNpnCrd(ctx); crdExsited != true {
return reconcile.Result{RequeueAfter: 60 * time.Second}, nil
} else {
_, err := machineScope.GetOrCreateNpn(ctx)
if err != nil {
machineScope.Info(fmt.Sprintf("GetOrCreate NPN CR failed, Requeue Now, Reason: %v", err))
return reconcile.Result{RequeueAfter: 120 * time.Second}, nil
}
machineScope.Info(fmt.Sprintf("NPN CR Reconcile Normal Completes"))
}
}

if deleteMachineOnTermination {
// typically, if the VM is terminated, we should get machine events, so ideally, the 300 seconds
// requeue time is not required, but in case, the event is missed, adding the requeue time
@@ -478,6 +494,19 @@ func (r *OCIMachineReconciler) reconcileDelete(ctx context.Context, machineScope
machineScope.Info("Instance is deleted")
r.Recorder.Eventf(machineScope.OCIMachine, corev1.EventTypeNormal,
"InstanceTerminated", "Deleted the instance")

CNIType := machineScope.OCIMachine.Spec.CNIType
if CNIType == "OCI_VCN_IP_NATIVE" {
machineScope.Info(fmt.Sprintf("CNI Type is: %s", CNIType))
err := machineScope.DeleteNpn(ctx)
if err != nil {
machineScope.Info(fmt.Sprintf("Delete NPN CR failed, reason: %v", apierrors.ReasonForError(err)))
return reconcile.Result{RequeueAfter: 60 * time.Second}, nil
} else {
machineScope.Info(fmt.Sprintf("Successfully Deleted NPN CR for the current node."))
}
}

return reconcile.Result{}, nil
default:
if !machineScope.IsResourceCreatedByClusterAPI(instance.FreeformTags) {