Repository: brimsec/zq (go)

Issue:
Remove crop from shape
Currently, `shape()` is the composition of the four primitive shaping functions cast, fill, crop, and order. Having now implemented some shaper scripts, recalled #1841, and consulted with @philrz, it seems like it would be better for `shape()` not to crop. This makes `shape()` less strict and more in-line with the overall schema flexibility of the system, as opposed to discarding any extra fields from the get-go. The fact that `shape()`  will likely be the default go-to shaping function for most users makes this change especially relevant. Of course, `crop()` remains available as a standalone function for situations that require it.

Pull request:
Remove cropping from shape()
close #2308 

Patch:
diff --git a/charts/eks-fluentbit/.helmignore b/charts/eks-fluentbit/.helmignore
new file mode 100644
index 000000000..0e8a0eb36
--- /dev/null
+++ b/charts/eks-fluentbit/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/charts/eks-fluentbit/Chart.yaml b/charts/eks-fluentbit/Chart.yaml
new file mode 100644
index 000000000..726aaabde
--- /dev/null
+++ b/charts/eks-fluentbit/Chart.yaml
@@ -0,0 +1,6 @@
+apiVersion: v2
+name: eks-fluentbit
+description: Helm chart to deploy fluentbit for sending kubernetes logs to aws cloudwatch.
+type: application
+version: 0.1.0
+appVersion: 1.16.0
diff --git a/charts/eks-fluentbit/templates/_helpers.tpl b/charts/eks-fluentbit/templates/_helpers.tpl
new file mode 100644
index 000000000..385ef200d
--- /dev/null
+++ b/charts/eks-fluentbit/templates/_helpers.tpl
@@ -0,0 +1,60 @@
+{{/* vim: set filetype=mustache: */}}
+
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "eks-fluentbit.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "eks-fluentbit.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "eks-fluentbit.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "eks-fluentbit.labels" -}}
+helm.sh/chart: {{ include "eks-fluentbit.chart" . }}
+{{ include "eks-fluentbit.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "eks-fluentbit.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "eks-fluentbit.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "eks-fluentbit.serviceAccountName" -}}
+  {{ default (include "eks-fluentbit.fullname" .) .Values.serviceAccount.name }}
+{{- end -}}
diff --git a/charts/eks-fluentbit/templates/clusterrole.yaml b/charts/eks-fluentbit/templates/clusterrole.yaml
new file mode 100644
index 000000000..70d8bcf85
--- /dev/null
+++ b/charts/eks-fluentbit/templates/clusterrole.yaml
@@ -0,0 +1,15 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: {{ include "eks-fluentbit.fullname" . }}
+rules:
+  - nonResourceURLs:
+      - /metrics
+    verbs:
+      - get
+  - apiGroups: [""]
+    resources:
+      - namespaces
+      - pods
+      - pods/logs
+    verbs: ["get", "list", "watch"]
diff --git a/charts/eks-fluentbit/templates/clusterrolebinding.yaml b/charts/eks-fluentbit/templates/clusterrolebinding.yaml
new file mode 100644
index 000000000..2e946c50b
--- /dev/null
+++ b/charts/eks-fluentbit/templates/clusterrolebinding.yaml
@@ -0,0 +1,12 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: {{ include "eks-fluentbit.fullname" . }}
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: {{ include "eks-fluentbit.fullname" . }}
+subjects:
+  - kind: ServiceAccount
+    name: {{ include "eks-fluentbit.serviceAccountName" . }}
+    namespace: {{ .Release.Namespace }}
diff --git a/charts/eks-fluentbit/templates/configmap.yaml b/charts/eks-fluentbit/templates/configmap.yaml
new file mode 100644
index 000000000..3bbdc7d48
--- /dev/null
+++ b/charts/eks-fluentbit/templates/configmap.yaml
@@ -0,0 +1,200 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: {{ include "eks-fluentbit.fullname" . }}
+  labels:
+    {{- include "eks-fluentbit.labels" . | nindent 4 }}
+data:
+  fluent-bit.conf: |
+    [SERVICE]
+        Flush                     5
+        Log_Level                 info
+        Daemon                    off
+        Parsers_File              parsers.conf
+        storage.path              /var/fluent-bit/state/flb-storage/
+        storage.sync              normal
+        storage.checksum          off
+        storage.backlog.mem_limit 5M
+        
+    @INCLUDE application-log.conf
+    @INCLUDE dataplane-log.conf
+    @INCLUDE host-log.conf
+  
+  application-log.conf: |
+    [INPUT]
+        Name                tail
+        Tag                 application.*
+        Exclude_Path        /var/log/containers/cloudwatch-agent*, /var/log/containers/fluent-bit*, /var/log/containers/aws-node*, /var/log/containers/kube-proxy*
+        Path                /var/log/containers/*.log
+        Docker_Mode         On
+        Docker_Mode_Flush   5
+        Docker_Mode_Parser  container_firstline
+        Parser              docker
+        DB                  /var/fluent-bit/state/flb_container.db
+        Mem_Buf_Limit       50MB
+        Skip_Long_Lines     On
+        Refresh_Interval    10
+        Rotate_Wait         30
+        storage.type        filesystem
+
+    [INPUT]
+        Name                tail
+        Tag                 application.*
+        Path                /var/log/containers/fluent-bit*
+        Parser              docker
+        DB                  /var/fluent-bit/state/flb_log.db
+        Mem_Buf_Limit       5MB
+        Skip_Long_Lines     On
+        Refresh_Interval    10
+
+    [INPUT]
+        Name                tail
+        Tag                 application.*
+        Path                /var/log/containers/cloudwatch-agent*
+        Docker_Mode         On
+        Docker_Mode_Flush   5
+        Docker_Mode_Parser  cwagent_firstline
+        Parser              docker
+        DB                  /var/fluent-bit/state/flb_cwagent.db
+        Mem_Buf_Limit       5MB
+        Skip_Long_Lines     On
+        Refresh_Interval    10
+
+    [FILTER]
+        Name                kubernetes
+        Match               application.*
+        Kube_URL            https://kubernetes.default.svc:443
+        Kube_Tag_Prefix     application.var.log.containers.
+        Merge_Log           On
+        Merge_Log_Key       log_processed
+        K8S-Logging.Parser  On
+        K8S-Logging.Exclude Off
+        Labels              Off
+        Annotations         Off
+
+    [OUTPUT]
+        Name                cloudwatch_logs
+        Match               application.*
+        region              {{ .Values.aws.region }}
+        log_group_name      /aws/containerinsights/{{ .Values.aws.clusterName }}/application
+        log_stream_prefix   ${HOST_NAME}-
+        auto_create_group   true
+        extra_user_agent    container-insights
+
+  dataplane-log.conf: |
+    [INPUT]
+        Name                systemd
+        Tag                 dataplane.systemd.*
+        Systemd_Filter      _SYSTEMD_UNIT=docker.service
+        DB                  /var/fluent-bit/state/systemd.db
+        Path                /var/log/journal
+
+    [INPUT]
+        Name                tail
+        Tag                 dataplane.tail.*
+        Path                /var/log/containers/aws-node*, /var/log/containers/kube-proxy*
+        Docker_Mode         On
+        Docker_Mode_Flush   5
+        Docker_Mode_Parser  container_firstline
+        Parser              docker
+        DB                  /var/fluent-bit/state/flb_dataplane_tail.db
+        Mem_Buf_Limit       50MB
+        Skip_Long_Lines     On
+        Refresh_Interval    10
+        Rotate_Wait         30
+        storage.type        filesystem
+
+    [FILTER]
+        Name                modify
+        Match               dataplane.systemd.*
+        Rename              _HOSTNAME                   hostname
+        Rename              _SYSTEMD_UNIT               systemd_unit
+        Rename              MESSAGE                     message
+        Remove_regex        ^((?!hostname|systemd_unit|message).)*$
+
+    [FILTER]
+        Name                aws
+        Match               dataplane.*
+        imds_version        v1
+
+    [OUTPUT]
+        Name                cloudwatch_logs
+        Match               dataplane.*
+        region              {{ .Values.aws.region }}
+        log_group_name      /aws/containerinsights/{{ .Values.aws.clusterName }}/dataplane
+        log_stream_prefix   ${HOST_NAME}-
+        auto_create_group   true
+        extra_user_agent    container-insights
+    
+  host-log.conf: |
+    [INPUT]
+        Name                tail
+        Tag                 host.dmesg
+        Path                /var/log/dmesg
+        Parser              syslog
+        DB                  /var/fluent-bit/state/flb_dmesg.db
+        Mem_Buf_Limit       5MB
+        Skip_Long_Lines     On
+        Refresh_Interval    10
+
+    [INPUT]
+        Name                tail
+        Tag                 host.messages
+        Path                /var/log/messages
+        Parser              syslog
+        DB                  /var/fluent-bit/state/flb_messages.db
+        Mem_Buf_Limit       5MB
+        Skip_Long_Lines     On
+        Refresh_Interval    10
+
+    [INPUT]
+        Name                tail
+        Tag                 host.secure
+        Path                /var/log/secure
+        Parser              syslog
+        DB                  /var/fluent-bit/state/flb_secure.db
+        Mem_Buf_Limit       5MB
+        Skip_Long_Lines     On
+        Refresh_Interval    10
+
+    [FILTER]
+        Name                aws
+        Match               host.*
+        imds_version        v1
+
+    [OUTPUT]
+        Name                cloudwatch_logs
+        Match               host.*
+        region              {{ .Values.aws.region }}
+        log_group_name      /aws/containerinsights/{{ .Values.aws.clusterName }}/host
+        log_stream_prefix   ${HOST_NAME}.
+        auto_create_group   true
+        extra_user_agent    container-insights
+
+  parsers.conf: |
+    [PARSER]
+        Name                docker
+        Format              json
+        Time_Key            time
+        Time_Format         %Y-%m-%dT%H:%M:%S.%LZ
+
+    [PARSER]
+        Name                syslog
+        Format              regex
+        Regex               ^(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$
+        Time_Key            time
+        Time_Format         %b %d %H:%M:%S
+
+    [PARSER]
+        Name                container_firstline
+        Format              regex
+        Regex               (?<log>(?<="log":")\S(?!\.).*?)(?<!\\)".*(?<stream>(?<="stream":").*?)".*(?<time>\d{4}-\d{1,2}-\d{1,2}T\d{2}:\d{2}:\d{2}\.\w*).*(?=})
+        Time_Key            time
+        Time_Format         %Y-%m-%dT%H:%M:%S.%LZ
+
+    [PARSER]
+        Name                cwagent_firstline
+        Format              regex
+        Regex               (?<log>(?<="log":")\d{4}[\/-]\d{1,2}[\/-]\d{1,2}[ T]\d{2}:\d{2}:\d{2}(?!\.).*?)(?<!\\)".*(?<stream>(?<="stream":").*?)".*(?<time>\d{4}-\d{1,2}-\d{1,2}T\d{2}:\d{2}:\d{2}\.\w*).*(?=})
+        Time_Key            time
+        Time_Format         %Y-%m-%dT%H:%M:%S.%LZ
diff --git a/charts/eks-fluentbit/templates/daemonset.yaml b/charts/eks-fluentbit/templates/daemonset.yaml
new file mode 100644
index 000000000..edb16f8a0
--- /dev/null
+++ b/charts/eks-fluentbit/templates/daemonset.yaml
@@ -0,0 +1,63 @@
+apiVersion: apps/v1
+kind: DaemonSet
+metadata:
+  name: {{ include "eks-fluentbit.fullname" . }}
+  labels:
+    {{- include "eks-fluentbit.labels" . | nindent 4 }}
+spec:
+  selector:
+    matchLabels:
+      {{- include "eks-fluentbit.selectorLabels" . | nindent 6 }}
+  template:
+    metadata:
+      labels:
+        {{- include "eks-fluentbit.selectorLabels" . | nindent 8 }}
+    spec:
+      containers:
+      - name: {{ .Chart.Name }}
+        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
+        imagePullPolicy: {{ .Values.image.pullPolicy }}
+        env:
+            - name: HOST_NAME
+              valueFrom:
+                fieldRef:
+                  fieldPath: spec.nodeName
+        volumeMounts:
+        # Please don't change below read-only permissions
+        - name: fluentbitstate
+          mountPath: /var/fluent-bit/state
+        - name: varlog
+          mountPath: /var/log
+          readOnly: true
+        - name: varlibdockercontainers
+          mountPath: /var/lib/docker/containers
+          readOnly: true
+        - name: fluentbit-config
+          mountPath: /fluent-bit/etc/
+        - name: runlogjournal
+          mountPath: /run/log/journal
+          readOnly: true
+        - name: dmesg
+          mountPath: /var/log/dmesg
+          readOnly: true
+      terminationGracePeriodSeconds: 10
+      volumes:
+      - name: fluentbitstate
+        hostPath:
+          path: /var/fluent-bit/state
+      - name: varlog
+        hostPath:
+          path: /var/log
+      - name: varlibdockercontainers
+        hostPath:
+          path: /var/lib/docker/containers
+      - name: fluentbit-config
+        configMap:
+          name: {{ include "eks-fluentbit.fullname" . }}
+      - name: runlogjournal
+        hostPath:
+          path: /run/log/journal
+      - name: dmesg
+        hostPath:
+          path: /var/log/dmesg
+      serviceAccountName: {{ include "eks-fluentbit.serviceAccountName" . }}
diff --git a/charts/eks-fluentbit/templates/serviceaccount.yaml b/charts/eks-fluentbit/templates/serviceaccount.yaml
new file mode 100644
index 000000000..d6492172e
--- /dev/null
+++ b/charts/eks-fluentbit/templates/serviceaccount.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.serviceAccount.create -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: {{ include "eks-fluentbit.serviceAccountName" . }}
+  labels: 
+    {{- include "eks-fluentbit.labels" . | nindent 4 }}
+  {{- with .Values.serviceAccount.annotations }}
+  annotations:
+    {{- toYaml . | nindent 4 }}
+  {{- end }}
+{{- end -}}
diff --git a/charts/eks-fluentbit/values.yaml b/charts/eks-fluentbit/values.yaml
new file mode 100644
index 000000000..a044f6d5a
--- /dev/null
+++ b/charts/eks-fluentbit/values.yaml
@@ -0,0 +1,14 @@
+image:
+  repository: amazon/aws-for-fluent-bit
+  tag: 2.10.0
+  pullPolicy: Always
+
+serviceAccount:
+  create: true
+  annotations: {}
+  name:
+
+aws:
+  region: us-east-2
+  clusterName:
+
diff --git a/compiler/kernel/expr.go b/compiler/kernel/expr.go
index db7f963fc..910aa6aff 100644
--- a/compiler/kernel/expr.go
+++ b/compiler/kernel/expr.go
@@ -352,7 +352,7 @@ func shaperOps(name string) expr.ShaperTransform {
 	case "order":
 		return expr.Order
 	case "shape":
-		return expr.Cast | expr.Crop | expr.Fill | expr.Order
+		return expr.Cast | expr.Fill | expr.Order
 	default:
 		return 0
 	}
diff --git a/expr/eval.go b/expr/eval.go
index 2c6576d8a..a3315f32a 100644
--- a/expr/eval.go
+++ b/expr/eval.go
@@ -616,7 +616,7 @@ func (i *Index) Eval(rec *zng.Record) (zng.Value, error) {
 	case *zng.TypeMap:
 		return indexMap(typ, container.Bytes, index)
 	default:
-		return zng.Value{}, fmt.Errorf("cannot index type \"%s\" with key \"%s\"", typ, index)
+		return zng.Value{}, zng.ErrMissing
 	}
 }
 
diff --git a/expr/function/time.go b/expr/function/time.go
index 5068e260d..8bac9b689 100644
--- a/expr/function/time.go
+++ b/expr/function/time.go
@@ -21,8 +21,14 @@ func (i *iso) Call(args []zng.Value) (zng.Value, error) {
 	if zv.Bytes == nil {
 		return zng.Value{zng.TypeTime, nil}, nil
 	}
-	ts, e := time.Parse(time.RFC3339Nano, string(zv.Bytes))
-	if e != nil {
+	// Handles ISO 8601 with time zone of Z or an offset not containing a colon.
+	format := "2006-01-02T15:04:05.999999999Z0700"
+	if l := len(zv.Bytes); l > 2 && zv.Bytes[l-3] == ':' {
+		// Handles ISO 8601 with time zone of Z or an offset containing a colon.
+		format = time.RFC3339Nano
+	}
+	ts, err := time.Parse(format, string(zv.Bytes))
+	if err != nil {
 		return badarg("iso")
 	}
 	return zng.Value{zng.TypeTime, i.Time(nano.Ts(ts.UnixNano()))}, nil
diff --git a/k8s/Makefile b/k8s/Makefile
new file mode 100644
index 000000000..b4462e7c4
--- /dev/null
+++ b/k8s/Makefile
@@ -0,0 +1,14 @@
+.PHONY: helm-install-fluentbit
+helm-install-fluentbit:
+	-kubectl create namespace logging
+	@helm upgrade eks-fluentbit ../charts/eks-fluentbit \
+		--install \
+		--namespace logging \
+		--set aws.region=us-east-2 \
+		--set aws.clusterName=zq-test
+
+.PHONY: helm-uninstall-fluentbit
+helm-uninstall-fluentbit:
+	@helm uninstall eks-fluentbit \
+		--namespace logging
+
diff --git a/k8s/cluster.yaml b/k8s/cluster.yaml
index ca421f3e5..0560c7e51 100644
--- a/k8s/cluster.yaml
+++ b/k8s/cluster.yaml
@@ -32,6 +32,8 @@ managedNodeGroups:
         - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
         # S3 access policies needed for zqd
         - arn:aws:iam::aws:policy/AmazonS3FullAccess
+        # CloudWatch access policies needed for eks logging agent
+        - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy
       withAddonPolicies:
         autoScaler: true
 
diff --git a/k8s/eks-setup.md b/k8s/eks-setup.md
index fa50b7c5a..fd15e073b 100644
--- a/k8s/eks-setup.md
+++ b/k8s/eks-setup.md
@@ -46,6 +46,7 @@ The following optional steps enable the following capablities:
 * Use cluster autoscaling on EKS
 * Install a Linkerd service mesh with sidecar proxies for observability
 * Uses Prometheus and Grafana to monitor zqd
+* Send application and control plane logs to CloudWatch using fluent bit
 
 ## Enabling the EKS Cluster Autoscaler
 Instructions based on:
@@ -86,6 +87,28 @@ Then use kubectl to deploy Linkerd with:
 linkerd install | kubectl apply -f -
 ```
 
+## Send logs to CloudWatch with fluent bit
+
+To view logs generated by applications and the kubernetes control plane in
+CloudWatch, we must install a fluent bit daemonset that runs on each node and
+sends logs to CloudWatch. Fortunately this can be easily done using the helm
+chart available in charts/eks-fluentbit.
+
+There is a simple Makefile rule in the k8s directory that will generate the
+namespace logging and run helm install on this chart on the cluster zq-test. cd
+into the k8s directory and run:
+
+``` 
+make helm-install-fluentbit
+```
+
+That is it! You can go to CloudWatch in us-east-2 and see that logs are now
+generated in three log groups:
+
+- /aws/containerinsights/zq-test/application
+- /aws/containerinsights/zq-test/dataplane
+- /aws/containerinsights/zq-test/host
+
 ## Tear-down
 Using the AWS console is the most convenient way to delete your EKS cluster, since you will want to double-check to make sure it is not running after you are done.
 
diff --git a/proc/groupby/groupby.go b/proc/groupby/groupby.go
index c8b8ef222..4d7780603 100644
--- a/proc/groupby/groupby.go
+++ b/proc/groupby/groupby.go
@@ -271,8 +271,8 @@ func (a *Aggregator) Consume(r *zng.Record) error {
 			if errors.Is(err, zng.ErrMissing) {
 				// block this input type
 				a.block[id] = struct{}{}
-				return nil
 			}
+			return nil
 		}
 		if i == 0 && a.inputSortDir != 0 {
 			prim = a.updateMaxTableKey(zv)


Test patch:
diff --git a/expr/agg/ztests/groupby-missing.yaml b/expr/agg/ztests/groupby-missing.yaml
new file mode 100644
index 000000000..badcf98c3
--- /dev/null
+++ b/expr/agg/ztests/groupby-missing.yaml
@@ -0,0 +1,10 @@
+zql: |
+  by a=a[0], b=b.c
+
+input: |
+   {a: "a", b: "b"}
+   {a: [], b: {c: 1}}
+   {a: [0], b: {c: 1}}
+
+output: |
+  {a:0,b:1}
diff --git a/expr/function/ztests/iso.yaml b/expr/function/ztests/iso.yaml
new file mode 100644
index 000000000..64278901c
--- /dev/null
+++ b/expr/function/ztests/iso.yaml
@@ -0,0 +1,8 @@
+zql: |
+   put z=iso(z), offsetWithoutColon=iso(offsetWithoutColon), offsetWithColon=iso(offsetWithColon)
+
+input: |
+  {"z": "2018-03-24T17:15:21.255387Z", "offsetWithoutColon": "2015-03-05T16:07:13.154566+0100", "offsetWithColon": "2015-03-05T16:07:13.154566+01:00"}
+
+output: |
+  {z:2018-03-24T17:15:21.255387Z,offsetWithoutColon:2015-03-05T15:07:13.154566Z,offsetWithColon:2015-03-05T15:07:13.154566Z}
diff --git a/expr/ztests/shape-nested.yaml b/expr/ztests/shape-nested.yaml
index 978456e24..a4bfe6931 100644
--- a/expr/ztests/shape-nested.yaml
+++ b/expr/ztests/shape-nested.yaml
@@ -12,4 +12,5 @@ output: |
   #0:record[x:record[a:record[a:float64,b:float64]]]
   0:[[[1;-;]]]
   0:[[[-;1;]]]
-  0:[[[-;1;]]]
+  #1:record[x:record[a:record[a:float64,b:float64,c:int64]]]
+  1:[[[-;1;1;]]]
diff --git a/expr/ztests/shape.yaml b/expr/ztests/shape.yaml
index 24152912d..5ce4f8f15 100644
--- a/expr/ztests/shape.yaml
+++ b/expr/ztests/shape.yaml
@@ -12,5 +12,5 @@ output-flags: -f tzng
 
 output: |
    #port=uint16
-   #0:record[id:record[orig_h:ip,orig_p:port,vlan:uint16,resp_h:ip,resp_p:port]]
-   0:[[1.2.3.4;1234;-;5.6.7.8;53;]]
+   #0:record[id:record[orig_h:ip,orig_p:port,vlan:uint16,resp_h:ip,resp_p:port,tag:uint16]]
+   0:[[1.2.3.4;1234;-;5.6.7.8;53;0;]]