calicoctl go-template output option doesn't work when running it either as a binary on the host or as a container in a kubernetes cluster.
The json output works fine, but the go-template or go-template-file options return what looks like a memory address.
> ETCD_ENDPOINTS=http://10.254.178.138:6666 ./calicoctl get node ip-10-50-101-137 -o go-template="{{.metadata.name}}"
template: get:1:11: executing "get" at <.metadata.name>: can't evaluate field metadata in type []runtime.Object
> ETCD_ENDPOINTS=http://10.254.178.138:6666 ./calicoctl get node ip-10-50-101-137 -o go-template="{{.}}"
[0xc0001f8140]
Here is the output I get when using the json format:
ETCD_ENDPOINTS=http://10.254.178.138:6666 ./calicoctl get node ip-10-50-101-137 -o json
{
"kind": "Node",
"apiVersion": "projectcalico.org/v3",
"metadata": {
"name": "ip-10-50-101-137",
"uid": "dfb8f897-3140-11e9-9b9c-02eba05e7984",
[...] removed
}
}
> curl -O -L https://github.com/projectcalico/calicoctl/releases/download/v3.5.1/calicoctl-linux-amd64
> chmod +x calicoctl-linux-amd64
> ETCD_ENDPOINTS=http://10.254.178.138:6666 ./calicoctl-linux-amd64 get node ip-10-50-101-137 -o go-template="{{.metadata.name}}"
template: get:1:11: executing "get" at <.metadata.name>: can't evaluate field metadata in type []runtime.Object
Using go-template is especially useful when running calicoctl as a pod, as the filtering would work out of the box and so avoid having to install json parsing tool such as jq.
3.5.1kubernetes 1.10.12Linux ip-10-50-101-0 4.4.0-101-generic #124-Ubuntu SMP Fri Nov 10 18:29:59 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux> ETCD_ENDPOINTS=http://10.254.178.138:6666 ./calicoctl version
Client Version: v3.5.1
Git commit: e669d8ea
Cluster Version: v3.5.1
Cluster Type: k8s,bgp
After looking into it seems like there's technically nothing broken here, but possible something a little inconsistent.
First, this is an array of objects, []runtime.Object (actual implementation is []api.Node), so your template will need to treat it as an array. Now, this is a little inconsistent from how the json output works, as if there's only one result it will just grab the first object of the []runtime.Object array, and json format that result. It may be a good idea to do that in the go template case as well.
Second, the go templating works on the actual field name, not the json tag name like the json library uses. The name of the field you want to access is ObjectMetadata, not metadata (you'd never be able to access the "metadata" field if it did exist, as it would be unexported). You can find the definition of the node object in the libcalico-go library, but here it is just so you can see this:
type Node struct {
metav1.TypeMeta `json:",inline"`
// Standard object's metadata.
metav1.ObjectMeta `json:"metadata,omitempty"`
// Specification of the Node.
Spec NodeSpec `json:"spec,omitempty"`
}
So as of right now if you wanted to get the node name you would modify your command to:
ETCD_ENDPOINTS=http://10.254.178.138:6666 ./calicoctl-linux-amd64 get node ip-10-50-101-137 -o go-template="{{(index . 0).ObjectMeta.Name}}"
Yeah, I agree this is non-obvious behavior and ideally we'd align the go output with the json output in terms of structure.
However, I think changing this now would break existing users of this command. The behavior is documented here: https://docs.projectcalico.org/v3.8/reference/calicoctl/get
So, I think we should close this.
Most helpful comment
After looking into it seems like there's technically nothing broken here, but possible something a little inconsistent.
First, this is an array of objects, []runtime.Object (actual implementation is []api.Node), so your template will need to treat it as an array. Now, this is a little inconsistent from how the json output works, as if there's only one result it will just grab the first object of the []runtime.Object array, and json format that result. It may be a good idea to do that in the go template case as well.
Second, the go templating works on the actual field name, not the json tag name like the json library uses. The name of the field you want to access is ObjectMetadata, not metadata (you'd never be able to access the "metadata" field if it did exist, as it would be unexported). You can find the definition of the node object in the libcalico-go library, but here it is just so you can see this:
So as of right now if you wanted to get the node name you would modify your command to: