// Package resources provides handlers for the etcd operator to assist with managing the kubernetes resources package resources import ( "context" "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "edge-infra.dev/pkg/k8s/runtime/conditions" "edge-infra.dev/pkg/k8s/runtime/patch" v1etcd "edge-infra.dev/pkg/sds/etcd/operator/apis/etcdmember/v1" etcdretryclient "edge-infra.dev/pkg/sds/lib/etcd/client/retry" kubeclienttypes "edge-infra.dev/pkg/sds/lib/k8s/retryclient/types" ) type EtcdMemberHandler struct { *v1etcd.EtcdMember Client kubeclienttypes.Retrier Key types.NamespacedName Found bool IsMember bool IsLearner bool } // Validate will validate the EtcdMember using a DryRun create func (h *EtcdMemberHandler) Validate(ctx context.Context) error { opts := &client.CreateOptions{} client.DryRunAll.ApplyToCreate(opts) return client.IgnoreAlreadyExists(h.Client.SafeCreate(ctx, h.EtcdMember, opts)) } func (h *EtcdMemberHandler) ReconcileMembershipStatus(ctx context.Context, client etcdretryclient.Retrier) error { resp, err := client.SafeMemberList(ctx) if err != nil { return err } for _, member := range resp.Members { if member.PeerURLs[0] == h.PeerURL() { h.IsMember = true h.IsLearner = member.IsLearner } } return nil } // ReconcileLocal updates the local copy of the EtcdMember with the latest // version from the API server func (h *EtcdMemberHandler) ReconcileLocal(ctx context.Context) error { return h.Client.SafeGet(ctx, h.Key, h.EtcdMember) } // CreateRemote creates the EtcdMember in the API server func (h *EtcdMemberHandler) CreateRemote(ctx context.Context) error { return h.Client.SafeCreate(ctx, h.EtcdMember) } // DeleteRemote deletes the EtcdMember from the API server func (h *EtcdMemberHandler) DeleteRemote(ctx context.Context) error { return h.Client.SafeDelete(ctx, h.EtcdMember) } // WithReconcileRemote will update the EtcdMember in the API server, after // applying the provided function to the EtcdMember func (h *EtcdMemberHandler) WithReconcileRemote(ctx context.Context, fn func(e *v1etcd.EtcdMember)) error { patcher := patch.NewSerialPatcher(h.EtcdMember, h.Client.Client()) fn(h.EtcdMember) return patcher.Patch(ctx, h.EtcdMember) } // DeepCopyFrom copies the contents of the provided EtcdMember into the local // copy of the EtcdMember func (h *EtcdMemberHandler) DeepCopyFrom(from *v1etcd.EtcdMember) { from.DeepCopyInto(h.EtcdMember) } // ReconfigurationRequired behaves differently depending on whether an // installation has been attempted. // // Case 1 - Installation has been attempted: // // returns true if the member has been unhealthy for longer than the // reconfiguration backoff period, otherwise false. // // Case 2 - Installation has not been attempted: // // returns true if the member has been unhealthy for longer than 60 minutes, // otherwise false. func (h *EtcdMemberHandler) ReconfigurationRequired() bool { healthCondition := conditions.Get(h.EtcdMember, v1etcd.Health) if healthCondition == nil { return false } installedCondition := conditions.Get(h.EtcdMember, v1etcd.Installed) switch installedCondition { case nil: return healthCondition.Status != metav1.ConditionTrue && healthCondition.LastTransitionTime.Add(60*time.Minute).Before(time.Now()) default: return healthCondition.Status != metav1.ConditionTrue && healthCondition.LastTransitionTime.Add(h.Spec.BackoffPeriod()).Before(time.Now()) } } // DeletionRequired returns true if the member meets the criteria // for deletion func (h *EtcdMemberHandler) DeletionRequired(currentIENVersion string) bool { if !h.InstalledForLatestIEN(currentIENVersion) { return true } if !h.DeletionTimestamp.IsZero() { return true } return h.ReconfigurationRequired() } // IsProvisioned returns true if the Secret was successfully prepared func (h *EtcdMemberHandler) IsProvisioned() bool { return conditions.IsTrue(h.EtcdMember, v1etcd.Provisioned) } // IsInstalled returns true if etcd was successfully installed func (h *EtcdMemberHandler) IsInstalled() bool { return conditions.IsTrue(h.EtcdMember, v1etcd.Installed) } // IsSuspended returns true if EtcdMember reconciliation is suspended func (h *EtcdMemberHandler) IsSuspended() bool { return h.Spec.Suspend } // InstallationAttempted returns true if all steps of the configuration // process have been attempted func (h *EtcdMemberHandler) InstallationAttempted() bool { condition := conditions.Get(h, v1etcd.Installed) return condition != nil } // InstalledForLatestIEN returns true if the EtcdMember was installed for the // latest IEN version func (h *EtcdMemberHandler) InstalledForLatestIEN(currentVersion string) bool { installedVersion := h.EtcdMember.GetLabels()[v1etcd.IENVersionLabel] return installedVersion == currentVersion }