Skip to content

Commit

Permalink
Merge branch 'main' into improve-logic-for-new-runtime-inspection
Browse files Browse the repository at this point in the history
  • Loading branch information
tamirdavid1 authored Oct 27, 2024
2 parents 5bc9ddc + fd310cc commit 31ff4df
Show file tree
Hide file tree
Showing 9 changed files with 535 additions and 176 deletions.
7 changes: 6 additions & 1 deletion cli/cmd/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ var describeCmd = &cobra.Command{
if describeRemoteFlag {
describeText = executeRemoteOdigosDescribe(ctx, client, odigosNs)
} else {
describeText = describe.DescribeOdigos(ctx, client, client.OdigosClient, odigosNs)
describeAnalyze, err := describe.DescribeOdigos(ctx, client, client.OdigosClient, odigosNs)
if err != nil {
describeText = fmt.Sprintf("Failed to describe odigos: %s", err)
} else {
describeText = describe.DescribeOdigosToText(describeAnalyze)
}
}
fmt.Println(describeText)
},
Expand Down
24 changes: 24 additions & 0 deletions cli/cmd/uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ var uninstallCmd = &cobra.Command{

createKubeResourceWithLogging(ctx, "Uninstalling Odigos CRDs",
client, cmd, ns, uninstallCRDs)

createKubeResourceWithLogging(ctx, "Uninstalling Odigos MutatingWebhookConfigurations",
client, cmd, ns, uninstallMutatingWebhookConfigs)

fmt.Printf("\n\u001B[32mSUCCESS:\u001B[0m Odigos uninstalled.\n")
},
}
Expand Down Expand Up @@ -398,6 +402,26 @@ func uninstallCRDs(ctx context.Context, cmd *cobra.Command, client *kube.Client,
return nil
}

func uninstallMutatingWebhookConfigs(ctx context.Context, cmd *cobra.Command, client *kube.Client, ns string) error {
list, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(ctx, metav1.ListOptions{
LabelSelector: metav1.FormatLabelSelector(&metav1.LabelSelector{
MatchLabels: labels.OdigosSystem,
}),
})
if err != nil {
return err
}

for _, webhook := range list.Items {
err = client.AdmissionregistrationV1().MutatingWebhookConfigurations().Delete(ctx, webhook.Name, metav1.DeleteOptions{})
if err != nil {
return err
}
}

return nil
}

func uninstallRBAC(ctx context.Context, cmd *cobra.Command, client *kube.Client, ns string) error {
list, err := client.RbacV1().ClusterRoles().List(ctx, metav1.ListOptions{
LabelSelector: metav1.FormatLabelSelector(&metav1.LabelSelector{
Expand Down
28 changes: 26 additions & 2 deletions frontend/endpoints/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,32 @@ import (
func DescribeOdigos(c *gin.Context) {
ctx := c.Request.Context()
odiogosNs := env.GetCurrentNamespace()
describeText := describe.DescribeOdigos(ctx, kube.DefaultClient, kube.DefaultClient.OdigosClient, odiogosNs)
c.Writer.WriteString(describeText)
desc, err := describe.DescribeOdigos(ctx, kube.DefaultClient, kube.DefaultClient.OdigosClient, odiogosNs)
if err != nil {
c.JSON(500, gin.H{
"message": err.Error(),
})
return
}

// construct the http response code based on the status of the odigos
returnCode := 200
if desc.HasErrors {
returnCode = 500
} else if !desc.IsSettled {
returnCode = 202
}

// Check for the Accept header
acceptHeader := c.GetHeader("Accept")

if acceptHeader == "application/json" {
// Return JSON response if Accept header is "application/json"
c.JSON(returnCode, desc)
} else {
describeText := describe.DescribeOdigosToText(desc)
c.String(returnCode, describeText)
}
}

func DescribeSource(c *gin.Context, ns string, kind string, name string) {
Expand Down
4 changes: 4 additions & 0 deletions k8sutils/pkg/describe/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ func wrapTextInGreen(text string) string {
return "\033[32m" + text + "\033[0m"
}

func wrapTextInYellow(text string) string {
return "\033[33m" + text + "\033[0m"
}

func wrapTextSuccessOfFailure(text string, success bool) string {
if success {
return wrapTextInGreen(text)
Expand Down
219 changes: 46 additions & 173 deletions k8sutils/pkg/describe/odigos.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,206 +7,79 @@ import (

odigosclientset "github.com/odigos-io/odigos/api/generated/odigos/clientset/versioned/typed/odigos/v1alpha1"
odigos "github.com/odigos-io/odigos/k8sutils/pkg/describe/odigos"
"github.com/odigos-io/odigos/k8sutils/pkg/getters"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/odigos-io/odigos/k8sutils/pkg/describe/properties"
"k8s.io/client-go/kubernetes"
)

func printOdigosVersion(odigosVersion string, sb *strings.Builder) {
describeText(sb, 0, "Odigos Version: %s", odigosVersion)
}

func printClusterCollectorStatus(resources *odigos.OdigosResources, sb *strings.Builder) {

expectingClusterCollector := len(resources.Destinations.Items) > 0

describeText(sb, 1, "Cluster Collector:")
clusterCollector := resources.ClusterCollector

if expectingClusterCollector {
describeText(sb, 2, "Status: Cluster Collector is expected to be created because there are destinations")
} else {
describeText(sb, 2, "Status: Cluster Collector is not expected to be created because there are no destinations")
func printProperty(sb *strings.Builder, indent int, property *properties.EntityProperty) {
if property == nil {
return
}

if clusterCollector.CollectorsGroup == nil {
describeText(sb, 2, wrapTextSuccessOfFailure("Collectors Group Not Created", !expectingClusterCollector))
} else {
describeText(sb, 2, wrapTextSuccessOfFailure("Collectors Group Created", expectingClusterCollector))

var deployedCondition *metav1.Condition
for _, condition := range clusterCollector.CollectorsGroup.Status.Conditions {
if condition.Type == "Deployed" {
deployedCondition = &condition
break
}
}
if deployedCondition == nil {
describeText(sb, 2, wrapTextInRed("Deployed: Status Unavailable"))
} else {
if deployedCondition.Status == metav1.ConditionTrue {
describeText(sb, 2, wrapTextInGreen("Deployed: true"))
} else {
describeText(sb, 2, wrapTextInRed("Deployed: false"))
describeText(sb, 2, wrapTextInRed(fmt.Sprintf("Reason: %s", deployedCondition.Message)))
}
}

ready := clusterCollector.CollectorsGroup.Status.Ready
describeText(sb, 2, wrapTextSuccessOfFailure(fmt.Sprintf("Ready: %t", ready), ready))
}

expectedReplicas := int32(0)
if clusterCollector.Deployment == nil {
describeText(sb, 2, wrapTextSuccessOfFailure("Deployment: Not Found", !expectingClusterCollector))
} else {
describeText(sb, 2, wrapTextSuccessOfFailure("Deployment: Found", expectingClusterCollector))
expectedReplicas = *clusterCollector.Deployment.Spec.Replicas
describeText(sb, 2, fmt.Sprintf("Expected Replicas: %d", expectedReplicas))
}

if clusterCollector.LatestRevisionPods != nil {
runningReplicas := 0
failureReplicas := 0
var failureText string
for _, pod := range clusterCollector.LatestRevisionPods.Items {
var condition *corev1.PodCondition
for i := range pod.Status.Conditions {
c := pod.Status.Conditions[i]
if c.Type == corev1.PodReady {
condition = &c
break
}
}
if condition == nil {
failureReplicas++
} else {
if condition.Status == corev1.ConditionTrue {
runningReplicas++
} else {
failureReplicas++
failureText = condition.Message
}
}
}
podReplicasText := fmt.Sprintf("Actual Replicas: %d running, %d failed", runningReplicas, failureReplicas)
deploymentSuccessful := runningReplicas == int(expectedReplicas) && failureReplicas == 0
describeText(sb, 2, wrapTextSuccessOfFailure(podReplicasText, deploymentSuccessful))
if !deploymentSuccessful {
describeText(sb, 2, wrapTextInRed(fmt.Sprintf("Replicas Not Ready Reason: %s", failureText)))
}
text := fmt.Sprintf("%s: %v", property.Name, property.Value)
switch property.Status {
case properties.PropertyStatusSuccess:
text = wrapTextInGreen(text)
case properties.PropertyStatusError:
text = wrapTextInRed(text)
case properties.PropertyStatusTransitioning:
text = wrapTextInYellow(text)
}

describeText(sb, indent, text)
}

func printAndCalculateIsNodeCollectorStatus(resources *odigos.OdigosResources, sb *strings.Builder) bool {

numInstrumentationConfigs := len(resources.InstrumentationConfigs.Items)
if numInstrumentationConfigs == 0 {
describeText(sb, 2, "Status: Node Collectors not expected as there are no sources")
return false
}

if resources.ClusterCollector.CollectorsGroup == nil {
describeText(sb, 2, "Status: Node Collectors not expected as there are no destinations")
return false
}

if !resources.ClusterCollector.CollectorsGroup.Status.Ready {
describeText(sb, 2, "Status: Node Collectors not expected as the Cluster Collector is not ready")
return false
}

describeText(sb, 2, "Status: Node Collectors expected as cluster collector is ready and there are sources")
return true
func printClusterCollectorStatus(analyze *odigos.OdigosAnalyze, sb *strings.Builder) {
describeText(sb, 1, "Cluster Collector:")
printProperty(sb, 2, &analyze.ClusterCollector.Enabled)
printProperty(sb, 2, &analyze.ClusterCollector.CollectorGroup)
printProperty(sb, 2, analyze.ClusterCollector.Deployed)
printProperty(sb, 2, analyze.ClusterCollector.DeployedError)
printProperty(sb, 2, analyze.ClusterCollector.CollectorReady)
printProperty(sb, 2, &analyze.ClusterCollector.DeploymentCreated)
printProperty(sb, 2, analyze.ClusterCollector.ExpectedReplicas)
printProperty(sb, 2, analyze.ClusterCollector.HealthyReplicas)
printProperty(sb, 2, analyze.ClusterCollector.FailedReplicas)
printProperty(sb, 2, analyze.ClusterCollector.FailedReplicasReason)
}

func printNodeCollectorStatus(resources *odigos.OdigosResources, sb *strings.Builder) {

func printNodeCollectorStatus(analyze *odigos.OdigosAnalyze, sb *strings.Builder) {
describeText(sb, 1, "Node Collector:")
nodeCollector := resources.NodeCollector

expectingNodeCollector := printAndCalculateIsNodeCollectorStatus(resources, sb)

if nodeCollector.CollectorsGroup == nil {
describeText(sb, 2, wrapTextSuccessOfFailure("Collectors Group Not Created", !expectingNodeCollector))
} else {
describeText(sb, 2, wrapTextSuccessOfFailure("Collectors Group Created", expectingNodeCollector))

var deployedCondition *metav1.Condition
for _, condition := range nodeCollector.CollectorsGroup.Status.Conditions {
if condition.Type == "Deployed" {
deployedCondition = &condition
break
}
}
if deployedCondition == nil {
describeText(sb, 2, wrapTextInRed("Deployed: Status Unavailable"))
} else {
if deployedCondition.Status == metav1.ConditionTrue {
describeText(sb, 2, wrapTextInGreen("Deployed: True"))
} else {
describeText(sb, 2, wrapTextInRed("Deployed: False"))
describeText(sb, 2, wrapTextInRed(fmt.Sprintf("Reason: %s", deployedCondition.Message)))
}
}

ready := nodeCollector.CollectorsGroup.Status.Ready
describeText(sb, 2, wrapTextSuccessOfFailure(fmt.Sprintf("Ready: %t", ready), ready))
}

if nodeCollector.DaemonSet == nil {
describeText(sb, 2, wrapTextSuccessOfFailure("DaemonSet: Not Found", !expectingNodeCollector))
} else {
describeText(sb, 2, wrapTextSuccessOfFailure("DaemonSet: Found", expectingNodeCollector))

// this is copied from k8sutils/pkg/describe/describe.go
// I hope the info is accurate since there can be many edge cases
describeText(sb, 2, "Desired Number of Nodes Scheduled: %d", nodeCollector.DaemonSet.Status.DesiredNumberScheduled)
currentMeetsDesired := nodeCollector.DaemonSet.Status.DesiredNumberScheduled == nodeCollector.DaemonSet.Status.CurrentNumberScheduled
describeText(sb, 2, wrapTextSuccessOfFailure(fmt.Sprintf("Current Number of Nodes Scheduled: %d", nodeCollector.DaemonSet.Status.CurrentNumberScheduled), currentMeetsDesired))
updatedMeetsDesired := nodeCollector.DaemonSet.Status.DesiredNumberScheduled == nodeCollector.DaemonSet.Status.UpdatedNumberScheduled
describeText(sb, 2, wrapTextSuccessOfFailure(fmt.Sprintf("Number of Nodes Scheduled with Up-to-date Pods: %d", nodeCollector.DaemonSet.Status.UpdatedNumberScheduled), updatedMeetsDesired))
availableMeetsDesired := nodeCollector.DaemonSet.Status.DesiredNumberScheduled == nodeCollector.DaemonSet.Status.NumberAvailable
describeText(sb, 2, wrapTextSuccessOfFailure(fmt.Sprintf("Number of Nodes Scheduled with Available Pods: %d", nodeCollector.DaemonSet.Status.NumberAvailable), availableMeetsDesired))
noMisscheduled := nodeCollector.DaemonSet.Status.NumberMisscheduled == 0
describeText(sb, 2, wrapTextSuccessOfFailure(fmt.Sprintf("Number of Nodes Misscheduled: %d", nodeCollector.DaemonSet.Status.NumberMisscheduled), noMisscheduled))
}
printProperty(sb, 2, &analyze.NodeCollector.Enabled)
printProperty(sb, 2, &analyze.NodeCollector.CollectorGroup)
printProperty(sb, 2, analyze.NodeCollector.Deployed)
printProperty(sb, 2, analyze.NodeCollector.DeployedError)
printProperty(sb, 2, analyze.NodeCollector.CollectorReady)
printProperty(sb, 2, &analyze.NodeCollector.DaemonSet)
printProperty(sb, 2, analyze.NodeCollector.DesiredNodes)
printProperty(sb, 2, analyze.NodeCollector.CurrentNodes)
printProperty(sb, 2, analyze.NodeCollector.UpdatedNodes)
printProperty(sb, 2, analyze.NodeCollector.AvailableNodes)
}

func printOdigosPipeline(resources *odigos.OdigosResources, sb *strings.Builder) {
func printOdigosPipeline(analyze *odigos.OdigosAnalyze, sb *strings.Builder) {
describeText(sb, 0, "Odigos Pipeline:")
numDestinations := len(resources.Destinations.Items)
numInstrumentationConfigs := len(resources.InstrumentationConfigs.Items)

describeText(sb, 1, "Status: there are %d sources and %d destinations\n", numInstrumentationConfigs, numDestinations)
printClusterCollectorStatus(resources, sb)
describeText(sb, 1, "Status: there are %d sources and %d destinations\n", analyze.NumberOfSources, analyze.NumberOfDestinations)
printClusterCollectorStatus(analyze, sb)
sb.WriteString("\n")
printNodeCollectorStatus(resources, sb)
printNodeCollectorStatus(analyze, sb)
}

func printDescribeOdigos(odigosVersion string, resources *odigos.OdigosResources) string {
func DescribeOdigosToText(analyze *odigos.OdigosAnalyze) string {
var sb strings.Builder

printOdigosVersion(odigosVersion, &sb)
printProperty(&sb, 0, &analyze.OdigosVersion)
sb.WriteString("\n")
printOdigosPipeline(resources, &sb)
printOdigosPipeline(analyze, &sb)

return sb.String()
}

func DescribeOdigos(ctx context.Context, kubeClient kubernetes.Interface, odigosClient odigosclientset.OdigosV1alpha1Interface, odigosNs string) string {

odigosVersion, err := getters.GetOdigosVersionInClusterFromConfigMap(ctx, kubeClient, odigosNs)
if err != nil {
return fmt.Sprintf("Error: %v\n", err)
}
func DescribeOdigos(ctx context.Context, kubeClient kubernetes.Interface, odigosClient odigosclientset.OdigosV1alpha1Interface, odigosNs string) (*odigos.OdigosAnalyze, error) {

odigosResources, err := odigos.GetRelevantOdigosResources(ctx, kubeClient, odigosClient, odigosNs)
if err != nil {
return fmt.Sprintf("Error: %v\n", err)
return nil, err
}

return printDescribeOdigos(odigosVersion, odigosResources)
return odigos.AnalyzeOdigos(odigosResources), nil
}
Loading

0 comments on commit 31ff4df

Please sign in to comment.