Skip to content
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

[Multitenancy]: Add PNI CRD #2134

Merged
merged 15 commits into from
Aug 16, 2023
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
2 changes: 2 additions & 0 deletions .github/workflows/crdgen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ jobs:
run: make -C crd/nodenetworkconfig
- name: Regenerate MultitenantNetworkContainer CRD
run: make -C crd/multitenantnetworkcontainer
- name: Regenerate PodNetworkInstance CRD
run: make -C crd/external/podnetworkinstance
- name: Regenerate PodNetwork CRD
run: make -C crd/external/podnetwork
- name: Regenerate NodeInfo CRD
Expand Down
19 changes: 19 additions & 0 deletions crd/external/podnetworkinstance/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.DEFAULT_GOAL: all

REPO_ROOT = $(shell git rev-parse --show-toplevel)
TOOLS_DIR = $(REPO_ROOT)/build/tools
TOOLS_BIN_DIR = $(REPO_ROOT)/build/tools/bin
CONTROLLER_GEN = $(TOOLS_BIN_DIR)/controller-gen

all: generate manifests

generate: $(CONTROLLER_GEN)
$(CONTROLLER_GEN) object paths="./..."

.PHONY: manifests
manifests: $(CONTROLLER_GEN)
mkdir -p manifests
$(CONTROLLER_GEN) crd paths="./..." output:crd:artifacts:config=manifests/

$(CONTROLLER_GEN):
@make -C $(REPO_ROOT) $(CONTROLLER_GEN)
5 changes: 5 additions & 0 deletions crd/external/podnetworkinstance/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Pod Network Instance (PNI)

PNIs represent optional requirements, or behavior configurations for how we setup the pod networking. They should map 1:1 and follow the lifetime of a customer workload.

The object points to the PodNetwork for the delegated subnet to use and defines allocation requirements (e.g.: for IPs to reserve for pod endpoints). Orchestrator can map the deployments with these requirements to the PNI object through labels on the pod spec pointing to this object identifier. 
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//go:build !ignore_uncovered
// +build !ignore_uncovered

// Package v1alpha1 contains API Schema definitions for the networking v1alpha1 API group
// +kubebuilder:object:generate=true
// +groupName=acn.azure.com
package v1alpha1

import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)

var (
// GroupVersion is group version used to register these objects
GroupVersion = schema.GroupVersion{Group: "acn.azure.com", Version: "v1alpha1"}

// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}

// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//go:build !ignore_uncovered
// +build !ignore_uncovered

package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// Important: Run "make" to regenerate code after modifying this file

// +kubebuilder:object:root=true

// PodNetworkInstance is the Schema for the PodNetworkInstances API
// +kubebuilder:resource:scope=Namespaced
// +kubebuilder:resource:shortName=pni
// +kubebuilder:subresource:status
// +kubebuilder:metadata:labels=managed=
// +kubebuilder:metadata:labels=owner=
// +kubebuilder:printcolumn:name="Pod IPs",type=string,priority=1,JSONPath=`.status.podIPAddresses`
// +kubebuilder:printcolumn:name="PodNetwork",type=string,priority=1,JSONPath=`.spec.podNetwork`
// +kubebuilder:printcolumn:name="PodIPReservationSize",type=string,priority=1,JSONPath=`.spec.podIPReservationSize`
type PodNetworkInstance struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec PodNetworkInstanceSpec `json:"spec,omitempty"`
Status PodNetworkInstanceStatus `json:"status,omitempty"`
}

// +kubebuilder:object:root=true

// PodNetworkInstanceList contains a list of PodNetworkInstance
type PodNetworkInstanceList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []PodNetworkInstance `json:"items"`
}

// PodNetworkInstanceSpec defines the desired state of PodNetworkInstance
type PodNetworkInstanceSpec struct {
// pod network resource object name
PodNetwork string `json:"podnetwork"`
// number of backend IP address to reserve for running pods
// +kubebuilder:default=0
PodIPReservationSize int `json:"podIPReservationSize"`
}

// PodNetworkInstanceStatus defines the observed state of PodNetworkInstance
type PodNetworkInstanceStatus struct {
PodIPAddresses []string `json:"podIPAddresses,omitempty"`
}

func init() {
SchemeBuilder.Register(&PodNetworkInstance{}, &PodNetworkInstanceList{})
}

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

84 changes: 84 additions & 0 deletions crd/external/podnetworkinstance/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package podnetworkinstance

import (
"context"
"reflect"

"github.com/Azure/azure-container-networking/crd"
"github.com/Azure/azure-container-networking/crd/external/podnetworkinstance/api/v1alpha1"
"github.com/pkg/errors"
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
typedv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
)

// Scheme is a runtime scheme containing the client-go scheme and the PodNetworkInstance scheme.
var Scheme = runtime.NewScheme()

func init() {
_ = scheme.AddToScheme(Scheme)
_ = v1alpha1.AddToScheme(Scheme)
}

// Installer provides methods to manage the lifecycle of the PodNetworkInstance resource definition.
type Installer struct {
cli typedv1.CustomResourceDefinitionInterface
}

func NewInstaller(c *rest.Config) (*Installer, error) {
cli, err := crd.NewCRDClientFromConfig(c)
if err != nil {
return nil, errors.Wrap(err, "failed to init crd client")
}
return &Installer{
cli: cli,
}, nil
}

func (i *Installer) create(ctx context.Context, res *v1.CustomResourceDefinition) (*v1.CustomResourceDefinition, error) {
res, err := i.cli.Create(ctx, res, metav1.CreateOptions{})
if err != nil {
return nil, errors.Wrap(err, "failed to create podnetworkinstance crd")
}
return res, nil
}

// Install installs the embedded PodNetworkInstance CRD definition in the cluster.
func (i *Installer) Install(ctx context.Context) (*v1.CustomResourceDefinition, error) {
podnetworkinstance, err := GetPodNetworkInstances()
if err != nil {
return nil, errors.Wrap(err, "failed to get embedded podnetworkinstance crd")
}
return i.create(ctx, podnetworkinstance)
}

// InstallOrUpdate installs the embedded PodNetworkInstance CRD definition in the cluster or updates it if present.
func (i *Installer) InstallOrUpdate(ctx context.Context) (*v1.CustomResourceDefinition, error) {
podnetworkinstance, err := GetPodNetworkInstances()
if err != nil {
return nil, errors.Wrap(err, "failed to get embedded podnetworkinstance crd")
}
current, err := i.create(ctx, podnetworkinstance)
if !apierrors.IsAlreadyExists(err) {
return current, err
}
if current == nil {
current, err = i.cli.Get(ctx, podnetworkinstance.Name, metav1.GetOptions{})
if err != nil {
return nil, errors.Wrap(err, "failed to get existing podnetworkinstance crd")
}
}
if !reflect.DeepEqual(podnetworkinstance.Spec.Versions, current.Spec.Versions) {
podnetworkinstance.SetResourceVersion(current.GetResourceVersion())
previous := *current
current, err = i.cli.Update(ctx, podnetworkinstance, metav1.UpdateOptions{})
if err != nil {
return &previous, errors.Wrap(err, "failed to update existing podnetworkinstance crd")
}
}
return current, nil
}
24 changes: 24 additions & 0 deletions crd/external/podnetworkinstance/embed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package podnetworkinstance

import (
_ "embed"

"github.com/pkg/errors"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"sigs.k8s.io/yaml"
)

// PodNetworkInstanceYAML embeds the CRD YAML for downstream consumers.
//
//go:embed manifests/acn.azure.com_podnetworkinstances.yaml
var PodNetworkInstanceYAML []byte

// GetPodNetworkInstances parses the raw []byte PodNetworkInstance in
// to a CustomResourceDefinition and returns it or an unmarshalling error.
func GetPodNetworkInstances() (*apiextensionsv1.CustomResourceDefinition, error) {
podNetworkInstances := &apiextensionsv1.CustomResourceDefinition{}
if err := yaml.Unmarshal(PodNetworkInstanceYAML, &podNetworkInstances); err != nil {
return nil, errors.Wrap(err, "error unmarshalling embedded podNetworkInstance")
}
return podNetworkInstances, nil
}
21 changes: 21 additions & 0 deletions crd/external/podnetworkinstance/embed_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package podnetworkinstance

import (
"os"
"testing"

"github.com/stretchr/testify/assert"
)

const filename = "manifests/acn.azure.com_podnetworkinstances.yaml"

func TestEmbed(t *testing.T) {
b, err := os.ReadFile(filename)
assert.NoError(t, err)
assert.Equal(t, b, PodNetworkInstanceYAML)
}

func TestGetPodNetworkInstances(t *testing.T) {
_, err := GetPodNetworkInstances()
assert.NoError(t, err)
}
Loading