-
Notifications
You must be signed in to change notification settings - Fork 382
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit adds the controller logic for reconciling PodInfo Resources and also adds tests. Unit and Integration tests are added. Signed-off-by: Prateek Singh <[email protected]> Signed-off-by: Michi Mutsuzaki <[email protected]>
- Loading branch information
1 parent
d4aa97c
commit 07ea3d1
Showing
188 changed files
with
26,774 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
name: PodInfo Integration Test | ||
on: | ||
pull_request: | ||
types: | ||
- opened | ||
- synchronize | ||
- reopened | ||
paths-ignore: | ||
- 'docs/**' | ||
push: | ||
branches: | ||
- main | ||
paths-ignore: | ||
- 'docs/**' | ||
jobs: | ||
build: | ||
runs-on: ubuntu-20.04 | ||
timeout-minutes: 40 | ||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 | ||
|
||
- name: Set Up Job Variables | ||
id: vars | ||
run: | | ||
if [ ${{ github.event.issue.pull_request || github.event.pull_request }} ]; then | ||
PR_API_JSON=$(curl \ | ||
-H "Accept: application/vnd.github.v3+json" \ | ||
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ | ||
${{ github.event.issue.pull_request.url || github.event.pull_request.url }}) | ||
SHA=$(echo "$PR_API_JSON" | jq -r ".head.sha") | ||
else | ||
SHA=${{ github.sha }} | ||
fi | ||
echo "sha=${SHA}" >> $GITHUB_OUTPUT | ||
echo "operatorImage=quay.io/cilium/tetragon-operator-ci:${SHA}" >> $GITHUB_OUTPUT | ||
- name: Install Go | ||
uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1 | ||
with: | ||
# renovate: datasource=golang-version depName=go | ||
go-version: '1.20.8' | ||
|
||
- name: Install Kind and create cluster | ||
uses: helm/[email protected] | ||
|
||
- name: Pull Tetragon Images | ||
uses: nick-fields/retry@943e742917ac94714d2f408a0e8320f2d1fcafcd # v2.8.3 | ||
with: | ||
timeout_minutes: 2 | ||
max_attempts: 30 | ||
retry_wait_seconds: 30 | ||
warning_on_retry: false | ||
command: | | ||
set -e | ||
docker pull ${{ steps.vars.outputs.operatorImage }} | ||
- name: Run go tests | ||
run: | | ||
helm upgrade --install tetragon ./install/kubernetes -n kube-system \ | ||
--set podWatcher.enabled=true \ | ||
--set tetragonOperator.image.override=${{ steps.vars.outputs.operatorImage }} | ||
kubectl rollout status -n kube-system deployment/tetragon-operator | ||
go test --tags=integration -v ./operator/... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// Copyright Authors of Tetragon | ||
|
||
package serve | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/bombsimon/logrusr/v4" | ||
"github.com/cilium/cilium/pkg/logging" | ||
"github.com/cilium/cilium/pkg/logging/logfields" | ||
|
||
"github.com/cilium/tetragon/operator/podinfo" | ||
ciliumiov1alpha1 "github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1" | ||
"github.com/spf13/cobra" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||
clientgoscheme "k8s.io/client-go/kubernetes/scheme" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/healthz" | ||
) | ||
|
||
var ( | ||
metricsAddr string | ||
enableLeaderElection bool | ||
probeAddr string | ||
scheme = runtime.NewScheme() | ||
setupLog = ctrl.Log.WithName("setup") | ||
) | ||
|
||
func init() { | ||
utilruntime.Must(clientgoscheme.AddToScheme(scheme)) | ||
utilruntime.Must(ciliumiov1alpha1.AddToScheme(scheme)) | ||
} | ||
|
||
func New() *cobra.Command { | ||
cmd := cobra.Command{ | ||
Use: "serve", | ||
Short: "Run Tetragon operator", | ||
RunE: func(cmd *cobra.Command, _ []string) error { | ||
log := logrusr.New(logging.DefaultLogger.WithField(logfields.LogSubsys, "operator")) | ||
ctrl.SetLogger(log) | ||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ | ||
Scheme: scheme, | ||
MetricsBindAddress: metricsAddr, | ||
Port: 9443, | ||
HealthProbeBindAddress: probeAddr, | ||
LeaderElection: enableLeaderElection, | ||
LeaderElectionID: "f161f714.tetragon.cilium.io", | ||
// LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily | ||
// when the Manager ends. This requires the binary to immediately end when the | ||
// Manager is stopped, otherwise, this setting is unsafe. Setting this significantly | ||
// speeds up voluntary leader transitions as the new leader don't have to wait | ||
// LeaseDuration time first. | ||
// | ||
// In the default scaffold provided, the program ends immediately after | ||
// the manager stops, so would be fine to enable this option. However, | ||
// if you are doing or is intended to do any operation such as perform cleanups | ||
// after the manager stops then its usage might be unsafe. | ||
// LeaderElectionReleaseOnCancel: true, | ||
}) | ||
if err != nil { | ||
return fmt.Errorf("unable to start manager: %w", err) | ||
} | ||
|
||
if err = (&podinfo.Reconciler{ | ||
Client: mgr.GetClient(), | ||
}).SetupWithManager(mgr); err != nil { | ||
return fmt.Errorf("unable to create controller: %w %s %s", err, "controller", "podinfo") | ||
} | ||
|
||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { | ||
return fmt.Errorf("unable to set up health check %w", err) | ||
} | ||
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { | ||
return fmt.Errorf("unable to set up ready check %w", err) | ||
} | ||
|
||
setupLog.Info("starting manager") | ||
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { | ||
return fmt.Errorf("problem running manager %w", err) | ||
} | ||
return nil | ||
}, | ||
} | ||
cmd.Flags().StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") | ||
cmd.Flags().StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") | ||
cmd.Flags().BoolVar(&enableLeaderElection, "leader-elect", false, | ||
"Enable leader election for controller manager. "+ | ||
"Enabling this will ensure there is only one active controller manager.") | ||
return &cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// Copyright Authors of Tetragon | ||
|
||
package podinfo | ||
|
||
import ( | ||
"context" | ||
"reflect" | ||
|
||
ciliumiov1alpha1 "github.com/cilium/tetragon/pkg/k8s/apis/cilium.io/v1alpha1" | ||
"golang.org/x/exp/maps" | ||
corev1 "k8s.io/api/core/v1" | ||
"k8s.io/apimachinery/pkg/api/errors" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
"sigs.k8s.io/controller-runtime/pkg/log" | ||
) | ||
|
||
// Reconciler reconciles a PodInfo object | ||
type Reconciler struct { | ||
client.Client | ||
//Scheme *runtime.Scheme | ||
} | ||
|
||
//+kubebuilder:rbac:groups=cilium.io,resources=PodInfo,verbs=get;list;watch;create;update;patch;delete | ||
//+kubebuilder:rbac:groups=cilium.io,resources=PodInfo/status,verbs=get;update;patch | ||
//+kubebuilder:rbac:groups=cilium.io,resources=PodInfo/finalizers,verbs=update | ||
|
||
// Reconcile gets notified about a pod and reconciles the corresponding PodInfo object. | ||
func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { | ||
l := log.FromContext(ctx) | ||
|
||
// Get the Pod. | ||
pod := &corev1.Pod{} | ||
if err := r.Get(ctx, req.NamespacedName, pod); err != nil { | ||
if !errors.IsNotFound(err) { | ||
// Error fetching the pod. Try again later. | ||
l.Error(err, "unable to fetch Pod") | ||
return ctrl.Result{}, err | ||
} | ||
// Pod is deleted. Nothing to reconcile. | ||
return ctrl.Result{}, nil | ||
} | ||
|
||
// Wait until the necessary pod fields are available. | ||
if !hasAllRequiredFields(pod) { | ||
return ctrl.Result{Requeue: true}, nil | ||
} | ||
|
||
podInfo := &ciliumiov1alpha1.PodInfo{} | ||
if err := r.Get(ctx, req.NamespacedName, podInfo); err != nil { | ||
if !errors.IsNotFound(err) { | ||
// Error fetching the pod info. Try again later. | ||
return ctrl.Result{}, err | ||
} | ||
// Pod info does not exist. Create it. | ||
return ctrl.Result{}, r.Create(ctx, generatePodInfo(pod)) | ||
} | ||
if !equal(pod, podInfo) { | ||
updatedPodInfo := generatePodInfo(pod) | ||
updatedPodInfo.ResourceVersion = podInfo.ResourceVersion | ||
return ctrl.Result{}, r.Update(ctx, updatedPodInfo) | ||
} | ||
return ctrl.Result{}, nil | ||
} | ||
|
||
// equal returns true if the given pod and pod info are equal. | ||
func equal(pod *corev1.Pod, podInfo *ciliumiov1alpha1.PodInfo) bool { | ||
if len(pod.Status.PodIPs) != len(podInfo.Status.PodIPs) { | ||
return false | ||
} | ||
for i, podIP := range pod.Status.PodIPs { | ||
if podIP.IP != podInfo.Status.PodIPs[i].IP { | ||
return false | ||
} | ||
} | ||
|
||
// check if ownerReference is changed. | ||
controller := true | ||
blockOwnerDeletion := true | ||
expectedOwnerReference := metav1.OwnerReference{ | ||
APIVersion: "v1", | ||
Kind: "Pod", | ||
Name: pod.Name, | ||
UID: pod.UID, | ||
Controller: &controller, | ||
BlockOwnerDeletion: &blockOwnerDeletion, | ||
} | ||
return pod.Name == podInfo.Name && | ||
pod.Namespace == podInfo.Namespace && | ||
pod.Status.PodIP == podInfo.Status.PodIP && | ||
maps.Equal(pod.Annotations, podInfo.Annotations) && | ||
maps.Equal(pod.Labels, podInfo.Labels) && | ||
len(podInfo.OwnerReferences) == 1 && | ||
reflect.DeepEqual(podInfo.OwnerReferences[0], expectedOwnerReference) | ||
} | ||
|
||
// hasAllRequiredFields checks if the necessary pod fields are available. | ||
func hasAllRequiredFields(pod *corev1.Pod) bool { | ||
return pod.UID != "" && | ||
pod.Name != "" && | ||
pod.Namespace != "" && | ||
pod.Status.PodIP != "" && | ||
len(pod.Status.PodIPs) > 0 | ||
} | ||
|
||
// generatePodInfo creates a PodInfo from a Pod | ||
func generatePodInfo(pod *corev1.Pod) *ciliumiov1alpha1.PodInfo { | ||
var podIPs []ciliumiov1alpha1.PodIP | ||
// Copy the Pod IPs into the PodInfo IPs. | ||
for _, podIP := range pod.Status.PodIPs { | ||
podIPs = append(podIPs, ciliumiov1alpha1.PodIP{IP: podIP.IP}) | ||
} | ||
controller := true | ||
blockOwnerDeletion := true | ||
return &ciliumiov1alpha1.PodInfo{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: pod.Name, | ||
Namespace: pod.Namespace, | ||
Labels: pod.Labels, | ||
Annotations: pod.Annotations, | ||
// setting up owner reference to the pod will ensure that the PodInfo resource is deleted when the pod is deleted. | ||
OwnerReferences: []metav1.OwnerReference{ | ||
{ | ||
APIVersion: pod.APIVersion, | ||
Kind: pod.Kind, | ||
Name: pod.Name, | ||
UID: pod.UID, | ||
Controller: &controller, | ||
BlockOwnerDeletion: &blockOwnerDeletion, | ||
}, | ||
}, | ||
}, | ||
Status: ciliumiov1alpha1.PodInfoStatus{ | ||
PodIP: pod.Status.PodIP, | ||
PodIPs: podIPs, | ||
}, | ||
} | ||
} | ||
|
||
// SetupWithManager sets up the controller with the Manager. | ||
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { | ||
return ctrl.NewControllerManagedBy(mgr). | ||
For(&corev1.Pod{}). | ||
Owns(&ciliumiov1alpha1.PodInfo{}). | ||
Complete(r) | ||
} |
Oops, something went wrong.