kuberneteskubernetes-custom-resourcesoperator-sdkkubebuilder

How to display a kubectl column using a fraction format (i.e. X/Y) for a Custom Resource


In Kubernetes, is it possible to display a column using a fraction format (i.e. X/Y) using the "additionalPrinterColumns" field of a CRD?

More precisely, I would like kubectl to display the description of a CR field using the same format than the READY field below:

kubectl get statefulsets.apps <my-statefulset>
NAME              READY   AGE
<my-statefulset>  2/2     18m

Could you provide the content of the "additionalPrinterColumns" section?


Solution

  • Unfortunately additionalPrinterColumns only supports Simple JsonPath and so we cannot use .status.readyReplicas/.status.replicas in JsonPath and the GET Operation is handled by kube-apiserver so there is no involvement of Operator in GET Operation. So, easiest way I would recommend is to create a new Field in .status called ready with value "readyReplicas/replicas" and update it every time you update readyReplicas and replicas.

    Then put below in additionalPrinterColumns

       additionalPrinterColumns:
        - jsonPath: .status.ready
          name: Ready
          type: string
    

    For StatefulSet, they are using below code in https://github.com/kubernetes/kubernetes/blob/master/pkg/printers/internalversion/printers.go to print value of READY Column

        statefulSetColumnDefinitions := []metav1.TableColumnDefinition{
            {Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
            {Name: "Ready", Type: "string", Description: "Number of the pod with ready state"},
            {Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
            {Name: "Containers", Type: "string", Priority: 1, Description: "Names of each container in the template."},
            {Name: "Images", Type: "string", Priority: 1, Description: "Images referenced by each container in the template."},
        }
        h.TableHandler(statefulSetColumnDefinitions, printStatefulSet)
        h.TableHandler(statefulSetColumnDefinitions, printStatefulSetList)
    
    
    func printStatefulSet(obj *apps.StatefulSet, options printers.GenerateOptions) ([]metav1.TableRow, error) {
        row := metav1.TableRow{
            Object: runtime.RawExtension{Object: obj},
        }
        desiredReplicas := obj.Spec.Replicas
        readyReplicas := obj.Status.ReadyReplicas
        createTime := translateTimestampSince(obj.CreationTimestamp)
        row.Cells = append(row.Cells, obj.Name, fmt.Sprintf("%d/%d", int64(readyReplicas), int64(desiredReplicas)), createTime)
        if options.Wide {
            names, images := layoutContainerCells(obj.Spec.Template.Spec.Containers)
            row.Cells = append(row.Cells, names, images)
        }
        return []metav1.TableRow{row}, nil
    }
    
    func printStatefulSetList(list *apps.StatefulSetList, options printers.GenerateOptions) ([]metav1.TableRow, error) {
        rows := make([]metav1.TableRow, 0, len(list.Items))
        for i := range list.Items {
            r, err := printStatefulSet(&list.Items[i], options)
            if err != nil {
                return nil, err
            }
            rows = append(rows, r...)
        }
        return rows, nil
    }