Skip to content

Commit

Permalink
feat: added success/failure stats
Browse files Browse the repository at this point in the history
Signed-off-by: AlexsJones <[email protected]>
  • Loading branch information
AlexsJones committed Jan 21, 2025
1 parent e1fbdc8 commit 4ffef10
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 126 deletions.
4 changes: 2 additions & 2 deletions api/v1alpha1/mutation_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ type MutationSpec struct {
// Important: Run "make" to regenerate code after modifying this file
SimilarityScore string `json:"similarityScore,omitempty"`
ResourceGVK string `json:"resourceGVK,omitempty"`
Resource corev1.ObjectReference `json:"resource,omitempty"`
Result Result `json:"result,omitempty"`
ResourceRef corev1.ObjectReference `json:"resource,omitempty"`
ResultRef corev1.ObjectReference `json:"result,omitempty"`
OriginConfiguration string `json:"originConfiguration,omitempty"`
TargetConfiguration string `json:"targetConfiguration,omitempty"`
}
Expand Down
12 changes: 6 additions & 6 deletions api/v1alpha1/result_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const (
AutoRemediationPhaseNotStarted AutoRemediationPhase = iota
AutoRemediationPhaseInProgress
AutoRemediationPhaseCompleted
AutoRemediationPhaseSuccessful
AutoRemediationPhaseFailed
)

Expand All @@ -65,12 +66,11 @@ type ResultStatus struct {
Webhook string `json:"webhook,omitempty"`
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="Kind",type="string",JSONPath=".spec.kind",description="Kind"
//+kubebuilder:printcolumn:name="Backend",type="string",JSONPath=".spec.backend",description="Backend"
//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Age"

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Kind",type="string",JSONPath=".spec.kind",description="Kind"
// +kubebuilder:printcolumn:name="Backend",type="string",JSONPath=".spec.backend",description="Backend"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Age"
// Result is the Schema for the results API
type Result struct {
metav1.TypeMeta `json:",inline"`
Expand Down
46 changes: 23 additions & 23 deletions api/v1alpha1/result_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ var _ = Describe("The test cases for the K8sGPT CRDs result types", func() {
var (
ctx context.Context
Namespace = "k8sGPT"
Kind = "Result"
Kind = "ResultRef"
Unmasked = "This is unmasked"
Masked = "This is masked"
Text = "This is a failure"
Expand Down Expand Up @@ -79,59 +79,59 @@ var _ = Describe("The test cases for the K8sGPT CRDs result types", func() {
ctx = context.Background()
})

Context("Create a new Result object", func() {
It("Should create a new Result object", func() {
Context("Create a new ResultRef object", func() {
It("Should create a new ResultRef object", func() {
Expect(fakeClient.Create(ctx, &result)).Should(Succeed())
})
})

// Get the Result object
Context("Get the Result object", func() {
It("Should get the Result object", func() {
// Get the ResultRef object
Context("Get the ResultRef object", func() {
It("Should get the ResultRef object", func() {
Expect(fakeClient.Get(ctx, typeNamespace, &result)).Should(Succeed())
})
// Check the Result object's filed values
It("Should check the Result object's filed values", func() {
// Check the ResultRef object's filed values
It("Should check the ResultRef object's filed values", func() {
Expect(result.Spec.Kind).Should(Equal(Kind))
Expect(result.Spec.Name).Should(Equal(Name))
Expect(result.Spec.Error[0].Text).Should(Equal(Text))
Expect(result.Spec.Details).Should(Equal(Details))
Expect(result.Spec.ParentObject).Should(Equal(ParentObject))
})
})
// Update the Result object
Context("Update the Result object", func() {
It("Should update the Result object", func() {
// Update the ResultRef object
Context("Update the ResultRef object", func() {
It("Should update the ResultRef object", func() {
result.Spec.Details = "This is a new result"
Expect(fakeClient.Update(ctx, &result)).Should(Succeed())
})
// Check the Result object's filed values
It("Should check the Result object's filed values", func() {
// Check the ResultRef object's filed values
It("Should check the ResultRef object's filed values", func() {
Expect(result.Spec.Kind).Should(Equal(Kind))
Expect(result.Spec.Name).Should(Equal(Name))
Expect(result.Spec.Error[0].Text).Should(Equal(Text))
Expect(result.Spec.Details).Should(Equal("This is a new result"))
Expect(result.Spec.ParentObject).Should(Equal(ParentObject))
})
})
// Get the Result object by list
Context("Get the Result object by list", func() {
It("Should get the Result object by list", func() {
// Get the ResultRef object by list
Context("Get the ResultRef object by list", func() {
It("Should get the ResultRef object by list", func() {
resultList := ResultList{}
Expect(fakeClient.List(ctx, &resultList)).Should(Succeed())
})
// Check the length of Result object list
It("Should check the length of Result object list", func() {
// Check the length of ResultRef object list
It("Should check the length of ResultRef object list", func() {
Expect(len(result.Spec.Error)).Should(Equal(1))
})
})
// delete the Result object
Context("Delete the Result object", func() {
It("Should delete the Result object", func() {
// delete the ResultRef object
Context("Delete the ResultRef object", func() {
It("Should delete the ResultRef object", func() {
Expect(fakeClient.Delete(ctx, &result)).Should(Succeed())
})
// Check the Result object has been deleted
It("Should check the Result object has been deleted", func() {
// Check the ResultRef object has been deleted
It("Should check the ResultRef object has been deleted", func() {
Expect(fakeClient.Get(ctx, typeNamespace, &result)).ShouldNot(Succeed())
})
})
Expand Down
6 changes: 3 additions & 3 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

114 changes: 51 additions & 63 deletions config/crd/bases/core.k8sgpt.ai_mutations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -116,78 +116,66 @@ spec:
resourceGVK:
type: string
result:
description: Result is the Schema for the results API
description: |-
ObjectReference contains enough information to let you inspect or modify the referred object.
---
New uses of this type are discouraged because of difficulty describing its usage when embedded in APIs.
1. Ignored fields. It includes many fields which are not generally honored. For instance, ResourceVersion and FieldPath are both very rarely valid in actual usage.
2. Invalid usage help. It is impossible to add specific help for individual usage. In most embedded usages, there are particular
restrictions like, "must refer only to types A and B" or "UID not honored" or "name must be restricted".
Those cannot be well described when embedded.
3. Inconsistent validation. Because the usages are different, the validation rules are different by usage, which makes it hard for users to predict what will happen.
4. The fields are both imprecise and overly precise. Kind is not a precise mapping to a URL. This can produce ambiguity
during interpretation and require a REST mapping. In most cases, the dependency is on the group,resource tuple
and the version of the actual struct is irrelevant.
5. We cannot easily change it. Because this type is embedded in many locations, updates to this type
will affect numerous schemas. Don't make new APIs embed an underspecified API type they do not control.
Instead of using this type, create a locally provided and used type that is well-focused on your reference.
For example, ServiceReferences for admission registration: https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533 .
properties:
apiVersion:
description: API version of the referent.
type: string
fieldPath:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
If referring to a piece of an object instead of an entire object, this string
should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2].
For example, if the object reference is to a container within a pod, this would take on a value like:
"spec.containers{name}" (where "name" refers to the name of the container that triggered
the event) or if no container name is specified "spec.containers[2]" (container with
index 2 in this pod). This syntax is chosen only to have some well-defined way of
referencing a part of an object.
TODO: this design is not final and this field is subject to change in the future.
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
Kind of the referent.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: ResultSpec defines the desired state of Result
properties:
autoRemediationStatus:
properties:
phase:
description: Enum for Phase
type: integer
type: object
backend:
type: string
details:
type: string
error:
items:
properties:
sensitive:
items:
properties:
masked:
type: string
unmasked:
type: string
type: object
type: array
text:
type: string
type: object
type: array
kind:
type: string
name:
type: string
parentObject:
type: string
required:
- autoRemediationStatus
- backend
- details
- error
- kind
- name
- parentObject
type: object
status:
description: ResultStatus defines the observed state of Result
properties:
lifecycle:
type: string
webhook:
type: string
type: object
name:
description: |-
Name of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
type: string
namespace:
description: |-
Namespace of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
type: string
resourceVersion:
description: |-
Specific resourceVersion to which this reference is made, if any.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency
type: string
uid:
description: |-
UID of the referent.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids
type: string
type: object
x-kubernetes-map-type: atomic
similarityScore:
description: |-
INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
Expand Down
2 changes: 1 addition & 1 deletion internal/controller/k8sgpt/analysis_step.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ func (step *AnalysisStep) processRawResults(rawResults map[string]corev1alpha1.R
if err != nil {
return err
}
// Rather than using the raw corev1alpha.Result from the RPC, we log on the v1alpha.Result from KubeBuilder
// Rather than using the raw corev1alpha.ResultRef from the RPC, we log on the v1alpha.ResultRef from KubeBuilder
if step.enableResultLogging {

// check if result.spec.error is nil
Expand Down
25 changes: 15 additions & 10 deletions internal/controller/k8sgpt/calculate_remediation_step.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
)

type eligibleResource struct {
Result corev1alpha1.Result
ResultRef corev1.ObjectReference
ObjectRef corev1.ObjectReference
GVK string
OriginConfiguration string
Expand Down Expand Up @@ -54,13 +54,13 @@ func (step *calculateRemediationStep) execute(instance *K8sGPTInstance) (ctrl.Re
for _, eligibleResource := range eligibleResources {
mutation := corev1alpha1.Mutation{
ObjectMeta: metav1.ObjectMeta{
Name: eligibleResource.Result.Name,
Name: eligibleResource.ResultRef.Name,
Namespace: instance.K8sgptConfig.Namespace,
},
Spec: corev1alpha1.MutationSpec{
Resource: eligibleResource.ObjectRef,
ResourceRef: eligibleResource.ObjectRef,
ResourceGVK: eligibleResource.GVK,
Result: eligibleResource.Result,
ResultRef: eligibleResource.ResultRef,
OriginConfiguration: eligibleResource.OriginConfiguration,
TargetConfiguration: "",
},
Expand All @@ -69,14 +69,14 @@ func (step *calculateRemediationStep) execute(instance *K8sGPTInstance) (ctrl.Re
},
}
// Check if the mutation exists, else create it
mutationKey := client.ObjectKey{Namespace: instance.K8sgptConfig.Namespace, Name: eligibleResource.Result.Name}
mutationKey := client.ObjectKey{Namespace: instance.K8sgptConfig.Namespace, Name: eligibleResource.ResultRef.Name}
var existingMutation corev1alpha1.Mutation
if err := instance.R.Get(instance.Ctx, mutationKey, &existingMutation); err != nil {
if client.IgnoreNotFound(err) != nil {
return instance.R.FinishReconcile(err, false, eligibleResource.Result.Name)
return instance.R.FinishReconcile(err, false, eligibleResource.ResultRef.Name)
}
if err := instance.R.Create(instance.Ctx, &mutation); err != nil {
return instance.R.FinishReconcile(err, false, eligibleResource.Result.Name)
return instance.R.FinishReconcile(err, false, eligibleResource.ResultRef.Name)
}
}
}
Expand All @@ -97,9 +97,14 @@ func (step *calculateRemediationStep) parseEligibleResources(instance *K8sGPTIns
namespace := names[0]
name := names[1]
if len(names) != 2 {
instance.logger.Error(fmt.Errorf("invalid resource name"), "unable to parse resource name", "Resource", item.Name)
instance.logger.Error(fmt.Errorf("invalid resource name"), "unable to parse resource name", "ResourceRef", item.Name)
continue
}
// create reference from the result
resultRef, err := reference.GetReference(instance.R.Scheme, &item)
if err != nil {
k8sgptControllerLog.Error(err, "Unable to create reference for ResultRef", "Name", item.Name)
}
// Support Service/Ingress currently
switch item.Spec.Kind {
case "Service":
Expand All @@ -116,7 +121,7 @@ func (step *calculateRemediationStep) parseEligibleResources(instance *K8sGPTIns
if err != nil {
step.logger.Error(err, "unable to marshal Service to yaml", "Service", item.Name)
}
eligibleResources = append(eligibleResources, eligibleResource{Result: item, ObjectRef: *serviceRef, OriginConfiguration: string(yamlData),
eligibleResources = append(eligibleResources, eligibleResource{ResultRef: *resultRef, ObjectRef: *serviceRef, OriginConfiguration: string(yamlData),
GVK: serviceRef.GroupVersionKind().String()})

case "Ingress":
Expand All @@ -133,7 +138,7 @@ func (step *calculateRemediationStep) parseEligibleResources(instance *K8sGPTIns
if err != nil {
step.logger.Error(err, "unable to marshal Ingress to yaml", "Service", item.Name)
}
eligibleResources = append(eligibleResources, eligibleResource{Result: item, ObjectRef: *ingressRef, OriginConfiguration: string(yamlData),
eligibleResources = append(eligibleResources, eligibleResource{ResultRef: *resultRef, ObjectRef: *ingressRef, OriginConfiguration: string(yamlData),
GVK: ingressRef.GroupVersionKind().String()})
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/controller/k8sgpt/k8sgpt_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func (r *K8sGPTReconciler) FinishReconcile(err error, requeueImmediate bool, nam
if requeueImmediate {
interval = 0
}
k8sgptControllerLog.Info("Finished Reconciling k8sGPT with error: %s\n", err.Error())
k8sgptControllerLog.Info("Finished Reconciling k8sGPT with error: %s\n", "error", err.Error())
reconcileErrorCounter := r.MetricsBuilder.GetCounterVec("k8sgpt_reconcile_error_count")
if reconcileErrorCounter != nil {
reconcileErrorCounter.WithLabelValues(name).Inc()
Expand Down
Loading

0 comments on commit 4ffef10

Please sign in to comment.