Merge pull request #118 from 1Password/item-status

Add Status field to OnePasswordItem resource
This commit is contained in:
Joris Coenen
2022-06-28 11:28:18 +02:00
committed by GitHub
5 changed files with 123 additions and 11 deletions

View File

@@ -12,8 +12,6 @@ spec:
scope: Namespaced scope: Namespaced
versions: versions:
- name: v1 - name: v1
served: true
storage: true
schema: schema:
openAPIV3Schema: openAPIV3Schema:
description: OnePasswordItem is the Schema for the onepassworditems API description: OnePasswordItem is the Schema for the onepassworditems API
@@ -38,8 +36,41 @@ spec:
type: object type: object
status: status:
description: OnePasswordItemStatus defines the observed state of OnePasswordItem description: OnePasswordItemStatus defines the observed state of OnePasswordItem
properties:
conditions:
description: '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'
items:
properties:
lastTransitionTime:
description: Last time the condition transit from one status
to another.
format: date-time
type: string
message:
description: Human-readable message indicating details about
last transition.
type: string
status:
description: Status of the condition, one of True, False, Unknown.
type: string
type:
description: Type of job condition, Completed.
type: string
required:
- status
- type
type: object
type: array
required:
- conditions
type: object type: object
type: type:
description: 'Kubernetes secret type. More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types' description: 'Kubernetes secret type. More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types'
type: string type: string
type: object type: object
served: true
storage: true
subresources:
status: {}

View File

@@ -11,11 +11,31 @@ type OnePasswordItemSpec struct {
ItemPath string `json:"itemPath,omitempty"` 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 // OnePasswordItemStatus defines the observed state of OnePasswordItem
type OnePasswordItemStatus struct { 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 // 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 // Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html
Conditions []OnePasswordItemCondition `json:"conditions"`
} }
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
@@ -26,7 +46,9 @@ type OnePasswordItemStatus struct {
type OnePasswordItem struct { type OnePasswordItem struct {
metav1.TypeMeta `json:",inline"` metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"` metav1.ObjectMeta `json:"metadata,omitempty"`
Type string `json:"type,omitempty"`
// Kubernetes secret type. More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types
Type string `json:"type,omitempty"`
Spec OnePasswordItemSpec `json:"spec,omitempty"` Spec OnePasswordItemSpec `json:"spec,omitempty"`
Status OnePasswordItemStatus `json:"status,omitempty"` Status OnePasswordItemStatus `json:"status,omitempty"`

View File

@@ -1,3 +1,4 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated // +build !ignore_autogenerated
// Code generated by operator-sdk. DO NOT EDIT. // Code generated by operator-sdk. DO NOT EDIT.
@@ -14,7 +15,7 @@ func (in *OnePasswordItem) DeepCopyInto(out *OnePasswordItem) {
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec out.Spec = in.Spec
out.Status = in.Status in.Status.DeepCopyInto(&out.Status)
return return
} }
@@ -36,6 +37,23 @@ func (in *OnePasswordItem) DeepCopyObject() runtime.Object {
return nil 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. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OnePasswordItemList) DeepCopyInto(out *OnePasswordItemList) { func (in *OnePasswordItemList) DeepCopyInto(out *OnePasswordItemList) {
*out = *in *out = *in
@@ -88,6 +106,13 @@ func (in *OnePasswordItemSpec) DeepCopy() *OnePasswordItemSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OnePasswordItemStatus) DeepCopyInto(out *OnePasswordItemStatus) { func (in *OnePasswordItemStatus) DeepCopyInto(out *OnePasswordItemStatus) {
*out = *in *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])
}
}
return return
} }

View File

@@ -96,10 +96,11 @@ func (r *ReconcileOnePasswordItem) Reconcile(request reconcile.Request) (reconci
} }
// Handles creation or updating secrets for deployment if needed // Handles creation or updating secrets for deployment if needed
if err := r.HandleOnePasswordItem(onepassworditem, request); err != nil { err := r.HandleOnePasswordItem(onepassworditem, request)
return reconcile.Result{}, err 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 one password finalizer exists then we must cleanup associated secrets
if utils.ContainsString(onepassworditem.ObjectMeta.Finalizers, finalizer) { if utils.ContainsString(onepassworditem.ObjectMeta.Finalizers, finalizer) {
@@ -169,3 +170,34 @@ func (r *ReconcileOnePasswordItem) HandleOnePasswordItem(resource *onepasswordv1
return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, resource.Namespace, item, autoRestart, labels, secretType, ownerRef) return kubeSecrets.CreateKubernetesSecretFromItem(r.kubeClient, secretName, resource.Namespace, item, autoRestart, labels, secretType, ownerRef)
} }
func (r *ReconcileOnePasswordItem) updateStatus(resource *onepasswordv1.OnePasswordItem, err error) error {
existingCondition := findCondition(resource.Status.Conditions, onepasswordv1.OnePasswordItemReady)
updatedCondition := existingCondition
if err != nil {
updatedCondition.Message = err.Error()
updatedCondition.Status = metav1.ConditionFalse
} else {
updatedCondition.Message = ""
updatedCondition.Status = metav1.ConditionTrue
}
if existingCondition.Status != updatedCondition.Status {
updatedCondition.LastTransitionTime = metav1.Now()
}
resource.Status.Conditions = []onepasswordv1.OnePasswordItemCondition{updatedCondition}
return r.kubeClient.Status().Update(context.Background(), resource)
}
func findCondition(conditions []onepasswordv1.OnePasswordItemCondition, t onepasswordv1.OnePasswordItemConditionType) onepasswordv1.OnePasswordItemCondition {
for _, c := range conditions {
if c.Type == t {
return c
}
}
return onepasswordv1.OnePasswordItemCondition{
Type: t,
Status: metav1.ConditionUnknown,
}
}

View File

@@ -44,8 +44,7 @@ func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretNa
if autoRestart != "" { if autoRestart != "" {
_, err := utils.StringToBool(autoRestart) _, err := utils.StringToBool(autoRestart)
if err != nil { if err != nil {
log.Error(err, "Error parsing %v annotation on Secret %v. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, secretName) return fmt.Errorf("Error parsing %v annotation on Secret %v. Must be true or false. Defaulting to false.", RestartDeploymentsAnnotation, secretName)
return err
} }
secretAnnotations[RestartDeploymentsAnnotation] = autoRestart secretAnnotations[RestartDeploymentsAnnotation] = autoRestart
} }
@@ -83,7 +82,10 @@ func CreateKubernetesSecretFromItem(kubeClient kubernetesClient.Client, secretNa
currentSecret.ObjectMeta.Annotations = secretAnnotations currentSecret.ObjectMeta.Annotations = secretAnnotations
currentSecret.ObjectMeta.Labels = labels currentSecret.ObjectMeta.Labels = labels
currentSecret.Data = secret.Data 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])) log.Info(fmt.Sprintf("Secret with name %v and version %v already exists", secret.Name, secret.Annotations[VersionAnnotation]))