Add Status field to OnePasswordItem resource

This makes it easier to see whehter the controller
succeeded in creating the Kubernetes secret for a
OnePasswordItem. If something failed, the `ready` field
will be `false` and the `OnePasswordItemReady` condition
will have a `status` of `False` with the error messages
in the `message` field.
This commit is contained in:
Joris Coenen
2022-06-15 17:46:56 +02:00
parent 0796b9c5e2
commit 6c20db47d6
5 changed files with 121 additions and 11 deletions

View File

@@ -11,11 +11,34 @@ type OnePasswordItemSpec struct {
ItemPath string `json:"itemPath,omitempty"`
}
type OnePasswordItemConditionType string
const (
// OnePasswordItemReady means the Kubernetes secret is ready for use.
OnePasswordItemReady OnePasswordItemConditionType = "Ready"
)
type OnePasswordItemCondition struct {
// Type of job condition, Completed.
Type OnePasswordItemConditionType `json:"type"`
// Status of the condition, one of True, False, Unknown.
Status metav1.ConditionStatus `json:"status"`
// Last time the condition transit from one status to another.
// +optional
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
// Human-readable message indicating details about last transition.
// +optional
Message string `json:"message,omitempty"`
}
// OnePasswordItemStatus defines the observed state of OnePasswordItem
type OnePasswordItemStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
// Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html
Conditions []OnePasswordItemCondition `json:"conditions"`
// True when the Kubernetes secret is ready for use.
Ready *bool `json:"ready,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

View File

@@ -1,3 +1,4 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
// Code generated by operator-sdk. DO NOT EDIT.
@@ -14,7 +15,7 @@ func (in *OnePasswordItem) DeepCopyInto(out *OnePasswordItem) {
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
out.Status = in.Status
in.Status.DeepCopyInto(&out.Status)
return
}
@@ -36,6 +37,23 @@ func (in *OnePasswordItem) DeepCopyObject() runtime.Object {
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OnePasswordItemCondition) DeepCopyInto(out *OnePasswordItemCondition) {
*out = *in
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OnePasswordItemCondition.
func (in *OnePasswordItemCondition) DeepCopy() *OnePasswordItemCondition {
if in == nil {
return nil
}
out := new(OnePasswordItemCondition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OnePasswordItemList) DeepCopyInto(out *OnePasswordItemList) {
*out = *in
@@ -88,6 +106,18 @@ func (in *OnePasswordItemSpec) DeepCopy() *OnePasswordItemSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OnePasswordItemStatus) DeepCopyInto(out *OnePasswordItemStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]OnePasswordItemCondition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.Ready != nil {
in, out := &in.Ready, &out.Ready
*out = new(bool)
**out = **in
}
return
}

View File

@@ -96,10 +96,11 @@ func (r *ReconcileOnePasswordItem) Reconcile(request reconcile.Request) (reconci
}
// Handles creation or updating secrets for deployment if needed
if err := r.HandleOnePasswordItem(onepassworditem, request); err != nil {
return reconcile.Result{}, err
err := r.HandleOnePasswordItem(onepassworditem, request)
if updateStatusErr := r.updateStatus(onepassworditem, err); updateStatusErr != nil {
return reconcile.Result{}, fmt.Errorf("cannot update status: %s", updateStatusErr)
}
return reconcile.Result{}, nil
return reconcile.Result{}, err
}
// If one password finalizer exists then we must cleanup associated secrets
if utils.ContainsString(onepassworditem.ObjectMeta.Finalizers, finalizer) {
@@ -169,3 +170,24 @@ func (r *ReconcileOnePasswordItem) HandleOnePasswordItem(resource *onepasswordv1
return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, resource.Namespace, item, autoRestart, labels, secretType, ownerRef)
}
func (r *ReconcileOnePasswordItem) updateStatus(resource *onepasswordv1.OnePasswordItem, err error) error {
condition := onepasswordv1.OnePasswordItemCondition{
Type: onepasswordv1.OnePasswordItemReady,
Status: metav1.ConditionTrue,
}
if err != nil {
condition.Message = err.Error()
condition.Status = metav1.ConditionFalse
}
ready := err == nil
if resource.Status.Ready == nil || ready != *resource.Status.Ready {
condition.LastTransitionTime = metav1.Now()
}
resource.Status.Ready = &ready
resource.Status.Conditions = []onepasswordv1.OnePasswordItemCondition{condition}
return r.kubeClient.Status().Update(context.Background(), resource)
}

View File

@@ -45,8 +45,7 @@ func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretNa
if autoRestart != "" {
_, err := utils.StringToBool(autoRestart)
if err != nil {
log.Error(err, "Error parsing %v annotation on Secret %v. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, secretName)
return err
return fmt.Errorf("Error parsing %v annotation on Secret %v. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, secretName)
}
secretAnnotations[RestartDeploymentsAnnotation] = autoRestart
}
@@ -75,7 +74,10 @@ func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretNa
currentSecret.ObjectMeta.Annotations = secretAnnotations
currentSecret.ObjectMeta.Labels = labels
currentSecret.Data = secret.Data
return kubeClient.Update(context.Background(), currentSecret)
if err := kubeClient.Update(context.Background(), currentSecret); err != nil {
return fmt.Errorf("Kubernetes secret update failed: %w", err)
}
return nil
}
log.Info(fmt.Sprintf("Secret with name %v and version %v already exists", secret.Name, secret.Annotations[VersionAnnotation]))