Skip to content
This repository has been archived by the owner on Aug 2, 2023. It is now read-only.

Commit

Permalink
Merge pull request #717 from kaczyns/targetNamespace
Browse files Browse the repository at this point in the history
Retry targetnamespace processing if failure
  • Loading branch information
kaczyns authored Jun 15, 2020
2 parents 74078d5 + de5e240 commit 1d98ae6
Show file tree
Hide file tree
Showing 7 changed files with 641 additions and 111 deletions.
16 changes: 16 additions & 0 deletions deploy/crds/kabanero.io_kabaneros_crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,22 @@ spec:
version:
type: string
type: object
targetNamespaces:
description: Target namespace status
properties:
message:
type: string
namespaces:
description: These are the target namespaces that are currently
being used. The spec.targetNamespaces will replace these when
the operator has finished applying the role bindings to those
namespaces.
items:
type: string
type: array
ready:
type: string
type: object
tekton:
description: Tekton instance readiness status.
properties:
Expand Down
13 changes: 13 additions & 0 deletions pkg/apis/kabanero/v1alpha2/kabanero_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,19 @@ type KabaneroStatus struct {
Sso SsoStatus `json:"sso,omitempty"`

Gitops GitopsStatus `json:"gitops,omitempty"`

// Target namespace status
TargetNamespaces TargetNamespaceStatus `json:"targetNamespaces,omitempty"`
}

type TargetNamespaceStatus struct {
// These are the target namespaces that are currently being used. The
// spec.targetNamespaces will replace these when the operator has finished
// applying the role bindings to those namespaces.
// +listType=set
Namespaces []string `json:"namespaces,omitempty"`
Ready string `json:"ready,omitempty"`
Message string `json:"message,omitempty"`
}

// PipelineStatus defines the observed state of the assets located within a single pipeline .tar.gz.
Expand Down
22 changes: 22 additions & 0 deletions pkg/apis/kabanero/v1alpha2/zz_generated.deepcopy.go

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

6 changes: 5 additions & 1 deletion pkg/controller/kabaneroplatform/gitops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ type testLogger struct{}
func (t testLogger) Info(msg string, keysAndValues ...interface{}) { fmt.Printf("Info: %v \n", msg) }
func (t testLogger) Enabled() bool { return true }
func (t testLogger) Error(err error, msg string, keysAndValues ...interface{}) {
fmt.Printf("Error: %v: %v\n", msg, err.Error())
if err != nil {
fmt.Printf("Error: %v: %v\n", msg, err.Error())
} else {
fmt.Printf("Error: %v\n", msg)
}
}
func (t testLogger) V(level int) logr.InfoLogger { return t }
func (t testLogger) WithValues(keysAndValues ...interface{}) logr.Logger { return t }
Expand Down
95 changes: 68 additions & 27 deletions pkg/controller/kabaneroplatform/kabaneroplatform_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
kabanerov1alpha1 "github.com/kabanero-io/kabanero-operator/pkg/apis/kabanero/v1alpha1"
kabanerov1alpha2 "github.com/kabanero-io/kabanero-operator/pkg/apis/kabanero/v1alpha2"
"github.com/kabanero-io/kabanero-operator/pkg/controller/utils/timer"
"github.com/operator-framework/operator-sdk/pkg/k8sutil"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -54,24 +55,30 @@ var reconcileFuncs = []reconcileFuncType{
{name: "events", function: reconcileEvents},
{name: "sso", function: reconcileSso},
{name: "gitops", function: reconcileGitopsPipelines},
{name: "target namespaces", function: reconcileTargetNamespaces},
}

// Add creates a new Kabanero Controller and adds it to the Manager. The Manager will set fields on the Controller
// and Start it when the Manager is Started.
func Add(mgr manager.Manager) error {
return add(mgr, newReconciler(mgr))
}
// It is very unlikely that this would fail, since the main also checks for it.
watchNamespace, err := k8sutil.GetWatchNamespace()
if err != nil {
return err
}

// newReconciler returns a new reconcile.Reconciler
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
return &ReconcileKabanero{
// Lets be sure a single namespace is specified.
numberOfWatchNamespaces := len(strings.Split(watchNamespace, ","))
if numberOfWatchNamespaces != 1 {
return fmt.Errorf("%v watch namespaces were specified, but only a single watch namespace is supported: %v", numberOfWatchNamespaces, watchNamespace)
}

r := &ReconcileKabanero{
client: mgr.GetClient(),
scheme: mgr.GetScheme(),
requeueDelayMap: make(map[string]RequeueData)}
}

// add adds a new Controller to mgr with r as the reconcile.Reconciler
func add(mgr manager.Manager, r reconcile.Reconciler) error {
requeueDelayMap: make(map[string]RequeueData),
watchNamespace: watchNamespace}

// Create a new controller
c, err := controller.New("kabaneroplatform-controller", mgr, controller.Options{Reconciler: r})
if err != nil {
Expand Down Expand Up @@ -104,6 +111,16 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error {
return err
}

// Watch Namespace instances. We only care about create and delete events, not update events.
// When we see that a namespace has been created/deleted, we need to process any Kabanero objects that
// reference that namespace.
err = c.Watch(&source.Kind{Type: &corev1.Namespace{}}, &handler.EnqueueRequestsFromMapFunc{
ToRequests: handler.ToRequestsFunc(r.targetNamespaceMapFunc)}, predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool { return false }})
if err != nil {
return err
}

/* Useful if RoleBindingList is changed to use Structured instead of Unstructured
// Index Rolebindings by name
if err := mgr.GetFieldIndexer().IndexField(&rbacv1.RoleBinding{}, "metadata.name", func(rawObj runtime.Object) []string {
Expand All @@ -117,22 +134,17 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error {
return nil
}

func getOperatorImage(c client.Client) (string, error) {
func (r *ReconcileKabanero) getOperatorImage() (string, error) {
// First, read the POD_NAME env variable. This is set in the deployment spec in the CSV.
podName := os.Getenv("POD_NAME")
if len(podName) == 0 {
return "", fmt.Errorf("The POD_NAME environment variable is not set, or is empty")
}

namespace := os.Getenv("WATCH_NAMESPACE")
if len(namespace) == 0 {
return "", fmt.Errorf("The WATCH_NAMESPACE environment variable is not set, or is empty")
}

// Second, get the Pod instance with that name
pod := &corev1.Pod{}
kubePodName := types.NamespacedName{Name: podName, Namespace: namespace}
err := c.Get(context.TODO(), kubePodName, pod)
kubePodName := types.NamespacedName{Name: podName, Namespace: r.watchNamespace}
err := r.client.Get(context.TODO(), kubePodName, pod)
if err != nil {
return "", fmt.Errorf("Pod %v could not be retrieved: %v", podName, err.Error())
}
Expand Down Expand Up @@ -177,6 +189,7 @@ type ReconcileKabanero struct {
client client.Client
scheme *runtime.Scheme
requeueDelayMap map[string]RequeueData
watchNamespace string
}

// RequeueData stores information that enables reconcile operations to be retried.
Expand All @@ -185,6 +198,33 @@ type RequeueData struct {
futureTime time.Time
}

// When we see that a namespace has changed, we want to reconcile any Kabanero instances that
// reference that namespace in its targetNamespaces list.
func (r *ReconcileKabanero) targetNamespaceMapFunc(a handler.MapObject) []reconcile.Request {
log.Info(fmt.Sprintf("Processing for change in namespace %v", a.Meta.GetName()))

// List Kabanero instances
kabaneros := &kabanerov1alpha2.KabaneroList{}
err := r.client.List(context.TODO(), kabaneros, client.InNamespace(r.watchNamespace))
if err != nil {
log.Error(err, fmt.Sprintf("Could not process namespace event for \"%v\"", a.Meta.GetName()))
return nil
}

// For each Kabanero instance, if spec.targetNamespaces includes a.meta.name then add a reconcile request.
requests := []reconcile.Request{}
for _, kabanero := range kabaneros.Items {
for _, namespace := range kabanero.Spec.TargetNamespaces {
if namespace == a.Meta.GetName() {
requests = append(requests, reconcile.Request{types.NamespacedName{Name: kabanero.Name, Namespace: kabanero.Namespace}})
break
}
}
}

return requests
}

// Determine if requeue is needed or not.
// If requeue is required set RequeueAfter to 60 seconds the first time.
// After the first time increase RequeueAfter by 60 seconds up to a max of 15 minutes.
Expand Down Expand Up @@ -288,7 +328,7 @@ func (r *ReconcileKabanero) Reconcile(request reconcile.Request) (reconcile.Resu
// in the add() method because the client is not started yet (that would have been ideal).
operatorContainerImageOp.Do(func() {
var err error
operatorContainerImage, err = getOperatorImage(r.client)
operatorContainerImage, err = r.getOperatorImage()
if err != nil {
log.Error(err, "Could not read the kabanero-operator container image from the pod")
}
Expand Down Expand Up @@ -380,13 +420,6 @@ func (r *ReconcileKabanero) Reconcile(request reconcile.Request) (reconcile.Resu
}
}

// Reconcile the targetNamespaces
err = reconcileTargetNamespaces(ctx, instance, r.client, reqLogger)
if err != nil {
reqLogger.Error(err, "Error reconciling targetNamespaces")
return reconcile.Result{}, err
}

// Deploy feature collection resources.
err = reconcileFeaturedStacks(ctx, instance, r.client, reqLogger)
if err != nil {
Expand Down Expand Up @@ -515,6 +548,12 @@ func cleanup(ctx context.Context, k *kabanerov1alpha2.Kabanero, client client.Cl
if err != nil {
return err
}

// Remove the cross-namespace objects that target namespaces use.
err = cleanupTargetNamespaces(ctx, k, client)
if err != nil {
return err
}

return nil
}
Expand Down Expand Up @@ -553,6 +592,7 @@ func processStatus(ctx context.Context, request reconcile.Request, k *kabanerov1
isAdmissionControllerWebhookReady, _ := getAdmissionControllerWebhookStatus(k, c, reqLogger)
isSsoReady, _ := getSsoStatus(k, c, reqLogger)
isGitopsReady, _ := getGitopsStatus(k)
isTargetNamespacesReady, _ := getTargetNamespacesStatus(k)

// Set the overall status.
isKabaneroReady := isCollectionControllerReady &&
Expand All @@ -567,7 +607,8 @@ func processStatus(ctx context.Context, request reconcile.Request, k *kabanerov1
isEventsReady &&
isAdmissionControllerWebhookReady &&
isSsoReady &&
isGitopsReady
isGitopsReady &&
isTargetNamespacesReady

if isKabaneroReady {
k.Status.KabaneroInstance.Message = ""
Expand Down
Loading

0 comments on commit 1d98ae6

Please sign in to comment.