mirror of
https://github.com/1Password/onepassword-operator.git
synced 2025-10-22 07:28:06 +00:00
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:
@@ -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,43 @@ 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
|
||||||
|
ready:
|
||||||
|
description: True when the Kubernetes secret is ready for use.
|
||||||
|
type: boolean
|
||||||
|
required:
|
||||||
|
- conditions
|
||||||
type: object
|
type: object
|
||||||
type:
|
type:
|
||||||
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: {}
|
||||||
|
@@ -11,11 +11,34 @@ 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"`
|
||||||
|
|
||||||
|
// True when the Kubernetes secret is ready for use.
|
||||||
|
Ready *bool `json:"ready,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
@@ -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,18 @@ 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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if in.Ready != nil {
|
||||||
|
in, out := &in.Ready, &out.Ready
|
||||||
|
*out = new(bool)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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,24 @@ 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 {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
@@ -45,8 +45,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
|
||||||
}
|
}
|
||||||
@@ -75,7 +74,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]))
|
||||||
|
Reference in New Issue
Block a user