mikewilliamson
mikewilliamson

Reputation: 24793

How to delete a Kubernetes resource entirely with jq

I'm trying to use jq (and yq) to delete a single Kubernetes resource. I thought I could do with this with del but it is leaving behind a null.

$ cat test.yaml | yq -y '. | del(select(.kind == "Namespace" and .metadata.name == "bar"))' 
apiVersion: v1
kind: Namespace
metadata:
  name: foo
spec: {}
status: {}
--- null
---
apiVersion: v1
kind: Namespace
metadata:
  name: baz
spec: {}
status: {}

My test data:

$ cat test.yaml 
apiVersion: v1
kind: Namespace
metadata:
  name: foo
spec: {}
status: {}
---
apiVersion: v1
kind: Namespace
metadata:
  name: bar
spec: {}
status: {}
---
apiVersion: v1
kind: Namespace
metadata:
  name: baz
spec: {}
status: {}

How do I get this output?

apiVersion: v1
kind: Namespace
metadata:
  name: foo
spec: {}
status: {}
---
apiVersion: v1
kind: Namespace
metadata:
  name: baz
spec: {}
status: {}

Upvotes: 0

Views: 588

Answers (2)

Weeble
Weeble

Reputation: 17930

I am assuming from the syntax that you are using kislyuk yq.

del is not the right tool for this job. del takes in a stream of N values and returns a stream of N modified values. In your case it takes in a stream of three values and returns a stream of three values. You observe that it leaves the first and last objects unchanged, but replaces the second with null. For each value it inputs, it evaluates its argument filter to decide how to change that value - every location emitted by that filter will be removed. You have provided as its argument a select filter. A select filter takes in a stream of values, and emits a stream of some of those values unmodified, while discarding others, depending on its filter argument. (Actually, it's a little more complicated than that - if you pass it a filter that returns multiple values, it can actually emit more values than it received, but that's not relevant here.)

In this case, your select filter returns nothing for the first object, because it doesn't match, so the del changes nothing. For the second object, the select filter does match the object, so the del filter is equivalent to del(.). The docs aren't terribly clear about what to expect when the filter you pass to del doesn't pick a key (or index) in the object (or array), but instead the whole thing, but clearly the behaviour in practice is that it replaces it with null.

If you followed along with that explanation, you may see where this is going. select is already the tool you want! There is no need for del here at all. select is the mechanism to keep only some of a stream of objects. Try:

$ cat ~/scratch/test.yaml | yq -y 'select(.kind != "Namespace" or .metadata.name != "bar")'
apiVersion: v1
kind: Namespace
metadata:
  name: foo
spec: {}
status: {}
---
apiVersion: v1
kind: Namespace
metadata:
  name: baz
spec: {}
status: {}

Upvotes: 2

peak
peak

Reputation: 116850

You could use gojq, the Go implementation of jq:

gojq --yaml-input --yaml-output 'select(.metadata.name != "bar")' test.yaml

Upvotes: 0

Related Questions