diff --git a/cyclops-ctrl/api/v1alpha1/module_types.go b/cyclops-ctrl/api/v1alpha1/module_types.go index c7a20891..f8625932 100644 --- a/cyclops-ctrl/api/v1alpha1/module_types.go +++ b/cyclops-ctrl/api/v1alpha1/module_types.go @@ -50,20 +50,24 @@ type TemplateGitRef struct { Commit string `json:"commit"` } -// ModuleStatus defines the observed state of Module -type ModuleStatus struct { - Resources []Resource `json:"resources,omitempty"` - Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"` +type ReconciliationStatusState string + +const ( + Unknown ReconciliationStatusState = "unknown" + Succeeded ReconciliationStatusState = "succeeded" + Failed ReconciliationStatusState = "failed" +) + +type ReconciliationStatus struct { + // +kubebuilder:validation:Enum=unknown;succeeded;failed + // +kubebuilder:default:=unknown + Status ReconciliationStatusState `json:"status"` + Reason string `json:"reason"` } -type Resource struct { - Group string `json:"group,omitempty" protobuf:"bytes,1,opt,name=group"` - Version string `json:"version,omitempty" protobuf:"bytes,2,opt,name=version"` - Kind string `json:"kind,omitempty" protobuf:"bytes,3,opt,name=kind"` - Namespace string `json:"namespace,omitempty" protobuf:"bytes,4,opt,name=namespace"` - Name string `json:"name,omitempty" protobuf:"bytes,5,opt,name=name"` - Status string `json:"status,omitempty" protobuf:"bytes,6,opt,name=status"` - Health string `json:"health,omitempty" protobuf:"bytes,7,opt,name=health"` +// ModuleStatus defines the observed state of Module +type ModuleStatus struct { + ReconciliationStatus ReconciliationStatus `json:"reconciliationStatus"` } type HistoryEntry struct { diff --git a/cyclops-ctrl/api/v1alpha1/zz_generated.deepcopy.go b/cyclops-ctrl/api/v1alpha1/zz_generated.deepcopy.go index b3dc6427..5e901723 100644 --- a/cyclops-ctrl/api/v1alpha1/zz_generated.deepcopy.go +++ b/cyclops-ctrl/api/v1alpha1/zz_generated.deepcopy.go @@ -22,7 +22,6 @@ limitations under the License. package v1alpha1 import ( - "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -49,7 +48,7 @@ func (in *Module) DeepCopyInto(out *Module) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) + out.Status = in.Status if in.History != nil { in, out := &in.History, &out.History *out = make([]HistoryEntry, len(*in)) @@ -129,18 +128,7 @@ func (in *ModuleSpec) DeepCopy() *ModuleSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ModuleStatus) DeepCopyInto(out *ModuleStatus) { *out = *in - if in.Resources != nil { - in, out := &in.Resources, &out.Resources - *out = make([]Resource, len(*in)) - copy(*out, *in) - } - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]v1.Condition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } + out.ReconciliationStatus = in.ReconciliationStatus } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ModuleStatus. @@ -169,16 +157,16 @@ func (in *ModuleValue) DeepCopy() *ModuleValue { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Resource) DeepCopyInto(out *Resource) { +func (in *ReconciliationStatus) DeepCopyInto(out *ReconciliationStatus) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resource. -func (in *Resource) DeepCopy() *Resource { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReconciliationStatus. +func (in *ReconciliationStatus) DeepCopy() *ReconciliationStatus { if in == nil { return nil } - out := new(Resource) + out := new(ReconciliationStatus) in.DeepCopyInto(out) return out } diff --git a/cyclops-ctrl/config/crd/bases/cyclops-ui.com_modules.yaml b/cyclops-ctrl/config/crd/bases/cyclops-ui.com_modules.yaml index de4a89a6..61eee611 100644 --- a/cyclops-ctrl/config/crd/bases/cyclops-ui.com_modules.yaml +++ b/cyclops-ctrl/config/crd/bases/cyclops-ui.com_modules.yaml @@ -84,93 +84,23 @@ spec: status: description: ModuleStatus defines the observed state of Module properties: - conditions: - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: \"Available\", - \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge - // +listType=map // +listMapKey=type Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - resources: - items: - properties: - group: - type: string - health: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - status: - type: string - version: - type: string - type: object - type: array + reconciliationStatus: + properties: + reason: + type: string + status: + default: unknown + enum: + - unknown + - succeeded + - failed + type: string + required: + - reason + - status + type: object + required: + - reconciliationStatus type: object type: object served: true diff --git a/cyclops-ctrl/internal/cluster/v1alpha1/modules.go b/cyclops-ctrl/internal/cluster/v1alpha1/modules.go index c101a931..6ffd01dd 100644 --- a/cyclops-ctrl/internal/cluster/v1alpha1/modules.go +++ b/cyclops-ctrl/internal/cluster/v1alpha1/modules.go @@ -52,6 +52,7 @@ func (c *moduleClient) Get(name string) (*cyclopsv1alpha1.Module, error) { func (c *moduleClient) Create(project *cyclopsv1alpha1.Module) (*cyclopsv1alpha1.Module, error) { result := cyclopsv1alpha1.Module{} + err := c.restClient. Post(). Namespace(c.ns). diff --git a/cyclops-ctrl/internal/modulecontroller/module_controller.go b/cyclops-ctrl/internal/modulecontroller/module_controller.go index 4fe591ae..fdc1a8d7 100644 --- a/cyclops-ctrl/internal/modulecontroller/module_controller.go +++ b/cyclops-ctrl/internal/modulecontroller/module_controller.go @@ -25,6 +25,7 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/yaml" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -94,6 +95,11 @@ func (r *ModuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr resources, err := r.kubernetesClient.GetResourcesForModule(req.Name) if err != nil { r.logger.Error(err, "error on get module resources", "namespaced name", req.NamespacedName) + + if err = r.setStatus(ctx, module, req.NamespacedName, cyclopsv1alpha1.Failed, err.Error()); err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, err } @@ -107,6 +113,10 @@ func (r *ModuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr "resource namespaced name", fmt.Sprintf("%s/%s", resource.GetNamespace(), resource.GetName()), ) + + if err = r.setStatus(ctx, module, req.NamespacedName, cyclopsv1alpha1.Failed, err.Error()); err != nil { + return ctrl.Result{}, err + } } } @@ -119,6 +129,15 @@ func (r *ModuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr r.logger.Info("upsert module", "namespaced name", req.NamespacedName) if err := r.moduleToResources(req.Name); err != nil { r.logger.Error(err, "error on upsert module", "namespaced name", req.NamespacedName) + + if err = r.setStatus(ctx, module, req.NamespacedName, cyclopsv1alpha1.Failed, err.Error()); err != nil { + return ctrl.Result{}, err + } + + return ctrl.Result{}, err + } + + if err = r.setStatus(ctx, module, req.NamespacedName, cyclopsv1alpha1.Succeeded, ""); err != nil { return ctrl.Result{}, err } @@ -209,3 +228,23 @@ func (r *ModuleReconciler) generateResources(kClient *k8sclient.KubernetesClient return nil } + +func (r *ModuleReconciler) setStatus( + ctx context.Context, + module cyclopsv1alpha1.Module, + namespacedName types.NamespacedName, + status cyclopsv1alpha1.ReconciliationStatusState, + reason string, +) error { + module.Status = cyclopsv1alpha1.ModuleStatus{ReconciliationStatus: cyclopsv1alpha1.ReconciliationStatus{ + Status: status, + Reason: reason, + }} + + if err := r.Status().Update(ctx, &module); err != nil { + r.logger.Error(err, "error updating module status", "namespaced name", namespacedName) + return err + } + + return nil +} diff --git a/install/chart/crds/cyclops-module.yaml b/install/chart/crds/cyclops-module.yaml index 9711da8d..130a256b 100644 --- a/install/chart/crds/cyclops-module.yaml +++ b/install/chart/crds/cyclops-module.yaml @@ -82,93 +82,23 @@ spec: status: description: ModuleStatus defines the observed state of Module properties: - conditions: - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: \"Available\", - \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge - // +listType=map // +listMapKey=type Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - resources: - items: - properties: - group: - type: string - health: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - status: - type: string - version: - type: string - type: object - type: array + reconciliationStatus: + properties: + reason: + type: string + status: + default: unknown + enum: + - unknown + - succeeded + - failed + type: string + required: + - reason + - status + type: object + required: + - reconciliationStatus type: object type: object served: true diff --git a/install/cyclops-install.yaml b/install/cyclops-install.yaml index c5e34161..3d2b464d 100644 --- a/install/cyclops-install.yaml +++ b/install/cyclops-install.yaml @@ -82,93 +82,23 @@ spec: status: description: ModuleStatus defines the observed state of Module properties: - conditions: - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: \"Available\", - \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge - // +listType=map // +listMapKey=type Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - resources: - items: - properties: - group: - type: string - health: - type: string - kind: - type: string - name: - type: string - namespace: - type: string - status: - type: string - version: - type: string - type: object - type: array + reconciliationStatus: + properties: + reason: + type: string + status: + default: unknown + enum: + - unknown + - succeeded + - failed + type: string + required: + - reason + - status + type: object + required: + - reconciliationStatus type: object type: object served: true