diff --git a/docs/tables/kubernetes_pod_disruption_budget.md b/docs/tables/kubernetes_pod_disruption_budget.md new file mode 100644 index 0000000..31e325d --- /dev/null +++ b/docs/tables/kubernetes_pod_disruption_budget.md @@ -0,0 +1,40 @@ +# Table: kubernetes_pod_disruption_budget + +A Pod Disruption Budget (PDB) limits the number of Pods of a replicated application that are down simultaneously from voluntary disruptions. + +## Examples + +### Basic info + +```sql +select + name, + namespace, + min_available, + max_unavailable, + selector +from + kubernetes_pod_disruption_budget +order by + namespace, + name; +``` + +### List deployments and their matching PDB + +```sql +select + d.namespace, + d.name, + min_available, + replicas +from + kubernetes_pod_disruption_budget pdb + inner join + kubernetes_deployment d + on d.selector = pdb.selector + and d.namespace = pdb.namespace +order by + d.namespace, + d.name +``` diff --git a/kubernetes/plugin.go b/kubernetes/plugin.go index aff82a6..c096a3f 100644 --- a/kubernetes/plugin.go +++ b/kubernetes/plugin.go @@ -45,6 +45,7 @@ func Plugin(ctx context.Context) *plugin.Plugin { "kubernetes_persistent_volume": tableKubernetesPersistentVolume(ctx), "kubernetes_persistent_volume_claim": tableKubernetesPersistentVolumeClaim(ctx), "kubernetes_pod": tableKubernetesPod(ctx), + "kubernetes_pod_disruption_budget": tableKubernetesPDB(ctx), "kubernetes_pod_security_policy": tableKubernetesPodSecurityPolicy(ctx), "kubernetes_replicaset": tableKubernetesReplicaSet(ctx), "kubernetes_replication_controller": tableKubernetesReplicaController(ctx), diff --git a/kubernetes/table_kubernetes_pod_disruption_budget.go b/kubernetes/table_kubernetes_pod_disruption_budget.go new file mode 100644 index 0000000..ad646ae --- /dev/null +++ b/kubernetes/table_kubernetes_pod_disruption_budget.go @@ -0,0 +1,158 @@ +package kubernetes + +import ( + "context" + "strings" + + v1beta1 "k8s.io/api/policy/v1beta1" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/turbot/steampipe-plugin-sdk/v3/grpc/proto" + "github.com/turbot/steampipe-plugin-sdk/v3/plugin" + "github.com/turbot/steampipe-plugin-sdk/v3/plugin/transform" +) + +func tableKubernetesPDB(ctx context.Context) *plugin.Table { + return &plugin.Table{ + Name: "kubernetes_pod_disruption_budget", + Description: "A Pod Disruption Budget limits the number of Pods of a replicated application that are down simultaneously from voluntary disruptions.", + Get: &plugin.GetConfig{ + KeyColumns: plugin.AllColumns([]string{"name", "namespace"}), + Hydrate: getPDB, + }, + List: &plugin.ListConfig{ + Hydrate: listPDBs, + KeyColumns: getCommonOptionalKeyQuals(), + }, + Columns: k8sCommonColumns([]*plugin.Column{ + + // PodDisruptionBudgetSpec + { + Name: "min_available", + Type: proto.ColumnType_STRING, + Description: "An eviction is allowed if at least 'minAvailable' pods selected by 'selector' will still be available after the eviction.", + Transform: transform.FromField("Spec.MinAvailable"), + }, + { + Name: "selector", + Type: proto.ColumnType_JSON, + Description: "Label query over pods whose evictions are managed by the disruption budget.", + Transform: transform.FromField("Spec.Selector"), + }, + { + Name: "max_unavailable", + Type: proto.ColumnType_STRING, + Description: "An eviction is allowed if at most 'maxAvailable' pods selected by 'selector' will still be unavailable after the eviction.", + Transform: transform.FromField("Spec.MaxUnavailable"), + }, + + // Steampipe Standard Columns + { + Name: "title", + Type: proto.ColumnType_STRING, + Description: ColumnDescriptionTitle, + Transform: transform.FromField("Name"), + }, + { + Name: "tags", + Type: proto.ColumnType_JSON, + Description: ColumnDescriptionTags, + Transform: transform.From(transformPDBTags), + }, + }), + } +} + +//// HYDRATE FUNCTIONS + +func listPDBs(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) { + logger := plugin.Logger(ctx) + logger.Trace("listPDBs") + + clientset, err := GetNewClientset(ctx, d) + if err != nil { + return nil, err + } + + input := metav1.ListOptions{ + Limit: 500, + } + + // Limiting the results + limit := d.QueryContext.Limit + if d.QueryContext.Limit != nil { + if *limit < input.Limit { + if *limit < 1 { + input.Limit = 1 + } else { + input.Limit = *limit + } + } + } + + commonFieldSelectorValue := getCommonOptionalKeyQualsValueForFieldSelector(d) + + if len(commonFieldSelectorValue) > 0 { + input.FieldSelector = strings.Join(commonFieldSelectorValue, ",") + } + + var response *v1beta1.PodDisruptionBudgetList + pageLeft := true + + for pageLeft { + response, err = clientset.PolicyV1beta1().PodDisruptionBudgets("").List(ctx, input) + if err != nil { + return nil, err + } + + if response.GetContinue() != "" { + input.Continue = response.Continue + } else { + pageLeft = false + } + + for _, item := range response.Items { + d.StreamListItem(ctx, item) + + // Context can be cancelled due to manual cancellation or the limit has been hit + if d.QueryStatus.RowsRemaining(ctx) == 0 { + return nil, nil + } + } + } + + return nil, nil +} + +func getPDB(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) { + logger := plugin.Logger(ctx) + logger.Trace("getPDB") + + clientset, err := GetNewClientset(ctx, d) + if err != nil { + return nil, err + } + + name := d.KeyColumnQuals["name"].GetStringValue() + namespace := d.KeyColumnQuals["namespace"].GetStringValue() + + // return if namespace or name is empty + if namespace == "" || name == "" { + return nil, nil + } + + pdb, err := clientset.PolicyV1beta1().PodDisruptionBudgets(namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil && !isNotFoundError(err) { + return nil, err + } + + return *pdb, nil +} + +//// TRANSFORM FUNCTIONS + +func transformPDBTags(_ context.Context, d *transform.TransformData) (interface{}, error) { + obj := d.HydrateItem.(v1beta1.PodDisruptionBudget) + return mergeTags(obj.Labels, obj.Annotations), nil +} diff --git a/kubernetes/table_kubernetes_pod_security_policy.go b/kubernetes/table_kubernetes_pod_security_policy.go index 311ecd7..b109f5e 100644 --- a/kubernetes/table_kubernetes_pod_security_policy.go +++ b/kubernetes/table_kubernetes_pod_security_policy.go @@ -242,7 +242,7 @@ func listPodSecurityPolicy(ctx context.Context, d *plugin.QueryData, _ *plugin.H func getPodSecurityPolicy(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) { logger := plugin.Logger(ctx) - logger.Trace("getK8sSecret") + logger.Trace("getPodSecurityPolicy") clientset, err := GetNewClientset(ctx, d) if err != nil {