Skip to content

Commit

Permalink
feat: adding EIPAssociation CRD and controller to allow static EIP un…
Browse files Browse the repository at this point in the history
…assignment (#16)
  • Loading branch information
alabonte7 committed Jul 4, 2023
1 parent 3598909 commit 4ffd565
Show file tree
Hide file tree
Showing 8 changed files with 326 additions and 2 deletions.
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ Unassigning and releasing can also be done in one step.

##### EIP creation

You can use an `initContainer` as part of your pod definition to create the `EIP` custom resource. This requires that your pod has RBAC permissions to create `EIP` resources.
You can use an `initContainer` as part of your pod definition to create the `EIP` or `EIPAssociation` custom resource. This requires that your pod has RBAC permissions to create `EIP`/ `EIPAssociation` resources.

```yaml
apiVersion: v1
Expand All @@ -144,6 +144,7 @@ rules:
- aws.k8s.logmein.com
resources:
- eips
- eipassociations
verbs:
- '*'
---
Expand Down Expand Up @@ -229,6 +230,26 @@ spec:
podName: some-pod
```

##### EIPAssociation
Here is an example of how to create an EIPAssociation to have a static EIP assigned/unassigned to a pod.

```yaml
apiVersion: aws.k8s.logmein.com/v1alpha1
kind: EIPAssociation
metadata:
name: my-eip-association
ownerReferences:
- apiVersion: v1
kind: Pod
name: some-pod
uid: ... # put the UID of the pod here
blockOwnerDeletion: true
spec:
eipName: eip-name
assignment:
podName: some-pod
```

### ENIs

To be documented
46 changes: 46 additions & 0 deletions api/v1alpha1/eip_association_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
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 v1alpha1

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

type EIPAssociationSpec struct {
Assignment *EIPAssignment `json:"assignment,omitempty"`
EIPName string `json:"eipName,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:printcolumn:name="Pod Name",type=string,JSONPath=`.spec.assignment.podName`
// +kubebuilder:printcolumn:name="EIP Name",type=string,JSONPath=`.spec.eipName`
type EIPAssociation struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec EIPAssociationSpec `json:"spec,omitempty"`
}

// +kubebuilder:object:root=true
type EIPAssociationList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []EIPAssociation `json:"items"`
}

func init() {
SchemeBuilder.Register(&EIPAssociation{}, &EIPAssociationList{})
}
78 changes: 78 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

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

Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.9.0
creationTimestamp: null
name: eipassociations.aws.k8s.logmein.com
spec:
group: aws.k8s.logmein.com
names:
kind: EIPAssociation
listKind: EIPAssociationList
plural: eipassociations
singular: eipassociation
scope: Namespaced
versions:
- additionalPrinterColumns:
- jsonPath: .spec.assignment.podName
name: Pod Name
type: string
- jsonPath: .spec.eipName
name: EIP Name
type: string
name: v1alpha1
schema:
openAPIV3Schema:
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
properties:
assignment:
properties:
eni:
type: string
eniPrivateIPAddressIndex:
type: integer
podName:
minLength: 0
type: string
privateIPAddress:
type: string
type: object
eipName:
type: string
type: object
type: object
served: true
storage: true
subresources: {}
2 changes: 1 addition & 1 deletion charts/k8s-aws-operator/templates/rbac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ rules:
resources: ["pods"]
verbs: ["get"]
- apiGroups: ["aws.k8s.logmein.com"]
resources: ["eips", "enis"]
resources: ["eips", "enis", "eipassociations"]
verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1
Expand Down
12 changes: 12 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ metadata:
creationTimestamp: null
name: manager-role
rules:
- apiGroups:
- aws.k8s.logmein.com
resources:
- eipassociations
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- aws.k8s.logmein.com
resources:
Expand Down
98 changes: 98 additions & 0 deletions controllers/eip_association_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
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 controllers

import (
"context"
"fmt"

"github.com/go-logr/logr"
awsv1alpha1 "github.com/logmein/k8s-aws-operator/api/v1alpha1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
)

// EIPReconciler reconciles a EIP object
type EIPAssociationReconciler struct {
client.Client
Log logr.Logger
}

// +kubebuilder:rbac:groups=aws.k8s.logmein.com,resources=eipassociations,verbs=get;list;watch;create;update;patch;delete

func (r *EIPAssociationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Log.WithValues("eipAssociation", req.NamespacedName)

var eipAssociation awsv1alpha1.EIPAssociation
if err := r.Get(ctx, req.NamespacedName, &eipAssociation); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}

if eipAssociation.ObjectMeta.DeletionTimestamp.IsZero() {
if !containsString(eipAssociation.ObjectMeta.Finalizers, finalizerName) {
eipAssociation.ObjectMeta.Finalizers = append(eipAssociation.ObjectMeta.Finalizers, finalizerName)
log.Info("New EIP Association")
var eip awsv1alpha1.EIP
if err := r.Client.Get(ctx, client.ObjectKey{
Namespace: req.Namespace,
Name: eipAssociation.Spec.EIPName,
}, &eip); err != nil {
return ctrl.Result{}, err
}

if eip.Spec.Assignment == nil && eip.Status.State == "allocated" {
eip.Spec.Assignment = eipAssociation.Spec.Assignment
} else {
return ctrl.Result{}, fmt.Errorf("Cannot assign EIP because it isn't in allocated state.")
}

if err := r.Update(ctx, &eip); err != nil {
return ctrl.Result{}, err
}

return ctrl.Result{}, r.Update(ctx, &eipAssociation)
}
} else {
// Association is being deleted we want to unassign EIP
if containsString(eipAssociation.ObjectMeta.Finalizers, finalizerName) {
var eip awsv1alpha1.EIP
if err := r.Client.Get(ctx, client.ObjectKey{
Namespace: req.Namespace,
Name: eipAssociation.Spec.EIPName,
}, &eip); err != nil {
return ctrl.Result{}, err
}

if eip.Status.Assignment != nil && (eip.Status.Assignment.PodName == eipAssociation.Spec.Assignment.PodName || eip.Status.Assignment.ENI == eipAssociation.Spec.Assignment.ENI) {
log.Info("Unassigning corresponding EIP")
eip.Spec.Assignment = nil
if err := r.Update(ctx, &eip); err != nil {
return ctrl.Result{}, err
}
}
eipAssociation.ObjectMeta.Finalizers = removeString(eipAssociation.ObjectMeta.Finalizers, finalizerName)
return ctrl.Result{}, r.Update(ctx, &eipAssociation)
}
}

return ctrl.Result{}, nil
}

func (r *EIPAssociationReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&awsv1alpha1.EIPAssociation{}).
Complete(r)
}
8 changes: 8 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ func main() {
setupLog.Error(err, "unable to create controller", "controller", "ENI")
os.Exit(1)
}
err = (&controllers.EIPAssociationReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("EIPAssociation"),
}).SetupWithManager(mgr)
if err != nil {
setupLog.Error(err, "unable to create controller", "controller", "EIPAssociation")
os.Exit(1)
}
// +kubebuilder:scaffold:builder

setupLog.Info("starting manager")
Expand Down

0 comments on commit 4ffd565

Please sign in to comment.