calicoclt go-template output doesn't work

Created on 15 Feb 2019  路  2Comments  路  Source: projectcalico/calico

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.

Current Behavior

> 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
  }
}

Steps to Reproduce (for bugs)

> 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

Context

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.

Your Environment

  • Calico version: 3.5.1
  • Orchestrator version: kubernetes 1.10.12
  • Operating System and version: Linux 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
    I also tried with v3.1.4 (https://github.com/projectcalico/calicoctl/releases/download/v3.1.4/calicoctl), but it has the same issue.
> 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
help wanted kinsupport

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:

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}}"

All 2 comments

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.

Was this page helpful?
0 / 5 - 0 ratings