Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deep merge from @jkcfg/std/merge errors if one of the keys to merge is an empty array #318

Open
shimmerjs opened this issue Feb 7, 2020 · 0 comments

Comments

@shimmerjs
Copy link

I have really enjoyed building Kube manifests in TypeScript using jk for my home cluster and personal projects, but I think there is an issue with the deep merge function:

Bug Description

Attempting to perform a merge via deep when one of the child keys is an empty array results in the following error:

@jkcfg/std/internal/flatbuffers.js:686
        while (i < s.length) {
                     ^
TypeError: Cannot read property 'length' of undefined
    at flatbuffers.Builder.createString (@jkcfg/std/internal/flatbuffers.js:686:22)
    at write (@jkcfg/std/write.js:37:28)
    at print (@jkcfg/std/write.js:65:5)
    at /Users/shimmerjs/dev/megazord/kubernetes/dist/jkcfg-merge-bug.js:6:1

Reproduction Steps

  1. Create input-1.json:
{
  "image": "tautulli/tautulli",
  "ingress": {
    "annotations": {},
    "enabled": false,
    "tls": []
  },
  "name": "tautulli",
  "namespace": "default",
  "persistence": {
    "size": "1Gi"
  },
  "timezone": "EST5EDT"
}
  1. Create input-2.json:

{
  "ingress": {
    "annotations": {
      "cert-manager.io/cluster-issuer": "letsencrypt-prod"
    },
    "enabled": true,
    "tls": [
      {
        "hosts": [
          "tautulli.fakeurl.dev"
        ],
        "secretName": "tautulli-ingress"
      }
    ]
  },
  "persistence": {
    "storageClass": "local-path"
  }
}
  1. I ran the following (compiled) TypeScript file:
import { print } from '@jkcfg/std';
import { deep } from '@jkcfg/std/merge';
import { $INLINE_JSON } from 'ts-transformer-inline-file';

const input1 = $INLINE_JSON('./input-1.json');
const input2 = $INLINE_JSON('./input-2.json');
const result = deep(input1, input2);

print(result, {});

export default [
  {
    path: 'jkcfg-merg.json',
    value: result,
  },
];

Note that I am using the ts-transformer-inline-file transformer and the ttsc project to run tsc with plugins. Here is the resultant JS file for running without needing to set that up:

import { print } from '@jkcfg/std';
import { deep } from '@jkcfg/std/merge';
const input1 = { "image": "tautulli/tautulli", "ingress": { "annotations": {}, "enabled": false, "tls": [] }, "name": "tautulli", "namespace": "default", "persistence": { "size": "1Gi" }, "timezone": "EST5EDT" };
const input2 = { "ingress": { "annotations": { "cert-manager.io/cluster-issuer": "letsencrypt-prod" }, "enabled": true, "tls": [{ "hosts": ["tautulli.fakeurl.dev"], "secretName": "tautulli-ingress" }] }, "persistence": { "storageClass": "local-path" } };
const result = deep(input1, input2);
print(result, {});
export default [
    {
        path: 'jkcfg-merg.json',
        value: result,
    },
];
  1. Run jk generate to cause the error (ignore the file paths, I wrote the script in my kube workspace for convenience):
 jk generate -o kubernetes/generated -i kubernetes/ kubernetes/dist/jkcfg-merge-bug.js
@jkcfg/std/internal/flatbuffers.js:686
        while (i < s.length) {
                     ^
TypeError: Cannot read property 'length' of undefined
    at flatbuffers.Builder.createString (@jkcfg/std/internal/flatbuffers.js:686:22)
    at write (@jkcfg/std/write.js:37:28)
    at print (@jkcfg/std/write.js:65:5)
    at /Users/shimmerjs/dev/megazord/kubernetes/dist/jkcfg-merge-bug.js:6:1
Module (kubernetes/dist/jkcfg-merge-bug.js) has not been loaded

To double check my assumptions on what a standard deep merge would produce, I wrote the same script using lodash-es to verify the result:

import { print } from '@jkcfg/std';
import { merge } from 'lodash-es';
const input1 = { "image": "tautulli/tautulli", "ingress": { "annotations": {}, "enabled": false, "tls": [] }, "name": "tautulli", "namespace": "default", "persistence": { "size": "1Gi" }, "timezone": "EST5EDT" };
const input2 = { "ingress": { "annotations": { "cert-manager.io/cluster-issuer": "letsencrypt-prod" }, "enabled": true, "tls": [{ "hosts": ["tautulli.fakeurl.dev"], "secretName": "tautulli-ingress" }] }, "persistence": { "storageClass": "local-path" } };
const result = merge(input1, input2);
print(result, {});
export default [
    {
        path: 'lodash-merge.json',
        value: result,
    },
];

Which produced this result:

» jk generate -o kubernetes/generated -i kubernetes/ kubernetes/dist/lodash-merge.js
{
  "image": "tautulli/tautulli",
  "ingress": {
    "annotations": {
      "cert-manager.io/cluster-issuer": "letsencrypt-prod"
    },
    "enabled": true,
    "tls": [
      {
        "hosts": [
          "tautulli.fakeurl.dev"
        ],
        "secretName": "tautulli-ingress"
      }
    ]
  },
  "name": "tautulli",
  "namespace": "default",
  "persistence": {
    "size": "1Gi",
    "storageClass": "local-path"
  },
  "timezone": "EST5EDT"
}

Versions:

  • npm modules:
» cat package.json | grep @jkcfg                         1 ↵
    "@jkcfg/kubernetes": "0.5.1",
    "@jkcfg/std": "0.3.2",
  • jk binary:
» jk version                                             1 ↵
version: 0.3.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant