diff --git a/extensions/pod-menu/package-lock.json b/extensions/pod-menu/package-lock.json index ea98213fb3..4409d2a89d 100644 --- a/extensions/pod-menu/package-lock.json +++ b/extensions/pod-menu/package-lock.json @@ -626,7 +626,644 @@ }, "@k8slens/extensions": { "version": "file:../../src/extensions/npm/extensions", - "dev": true + "dev": true, + "requires": { + "@material-ui/core": "*", + "@types/node": "*", + "@types/react-select": "*", + "conf": "^7.0.1" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "dev": true + }, + "@material-ui/core": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.2.tgz", + "integrity": "sha512-/D1+AQQeYX/WhT/FUk78UCRj8ch/RCglsQLYujYTIqPSJlwZHKcvHidNeVhODXeApojeXjkl0tWdk5C9ofwOkQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/styles": "^4.11.2", + "@material-ui/system": "^4.11.2", + "@material-ui/types": "^5.1.0", + "@material-ui/utils": "^4.11.2", + "@types/react-transition-group": "^4.2.0", + "clsx": "^1.0.4", + "hoist-non-react-statics": "^3.3.2", + "popper.js": "1.16.1-lts", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0", + "react-transition-group": "^4.4.0" + } + }, + "@material-ui/styles": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.2.tgz", + "integrity": "sha512-xbItf8zkfD3FuGoD9f2vlcyPf9jTEtj9YTJoNNV+NMWaSAHXgrW6geqRoo/IwBuMjqpwqsZhct13e2nUyU9Ljw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "@emotion/hash": "^0.8.0", + "@material-ui/types": "^5.1.0", + "@material-ui/utils": "^4.11.2", + "clsx": "^1.0.4", + "csstype": "^2.5.2", + "hoist-non-react-statics": "^3.3.2", + "jss": "^10.0.3", + "jss-plugin-camel-case": "^10.0.3", + "jss-plugin-default-unit": "^10.0.3", + "jss-plugin-global": "^10.0.3", + "jss-plugin-nested": "^10.0.3", + "jss-plugin-props-sort": "^10.0.3", + "jss-plugin-rule-value-function": "^10.0.3", + "jss-plugin-vendor-prefixer": "^10.0.3", + "prop-types": "^15.7.2" + } + }, + "@material-ui/system": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.11.2.tgz", + "integrity": "sha512-BELFJEel5E+5DMiZb6XXT3peWRn6UixRvBtKwSxqntmD0+zwbbfCij6jtGwwdJhN1qX/aXrKu10zX31GBaeR7A==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.11.2", + "csstype": "^2.5.2", + "prop-types": "^15.7.2" + } + }, + "@material-ui/types": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", + "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==", + "dev": true + }, + "@material-ui/utils": { + "version": "4.11.2", + "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.2.tgz", + "integrity": "sha512-Uul8w38u+PICe2Fg2pDKCaIG7kOyhowZ9vjiC1FsVwPABTW8vPPKfF6OvxRq3IiBaI1faOJmgdvMG7rMJARBhA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.4.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0 || ^17.0.0" + } + }, + "@types/node": { + "version": "14.14.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.21.tgz", + "integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==", + "dev": true + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", + "dev": true + }, + "@types/react": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.0.tgz", + "integrity": "sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + }, + "dependencies": { + "csstype": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.6.tgz", + "integrity": "sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw==", + "dev": true + } + } + }, + "@types/react-dom": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.0.tgz", + "integrity": "sha512-lUqY7OlkF/RbNtD5nIq7ot8NquXrdFrjSOR6+w9a9RFQevGi1oZO1dcJbXMeONAPKtZ2UrZOEJ5UOCVsxbLk/g==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/react-select": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/react-select/-/react-select-3.1.2.tgz", + "integrity": "sha512-ygvR/2FL87R2OLObEWFootYzkvm67LRA+URYEAcBuvKk7IXmdsnIwSGm60cVXGaqkJQHozb2Cy1t94tCYb6rJA==", + "dev": true, + "requires": { + "@types/react": "*", + "@types/react-dom": "*", + "@types/react-transition-group": "*" + } + }, + "@types/react-transition-group": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz", + "integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "atomically": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", + "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==", + "dev": true + }, + "clsx": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", + "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==", + "dev": true + }, + "conf": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/conf/-/conf-7.1.2.tgz", + "integrity": "sha512-r8/HEoWPFn4CztjhMJaWNAe5n+gPUCSaJ0oufbqDLFKsA1V8JjAG7G+p0pgoDFAws9Bpk2VtVLLXqOBA7WxLeg==", + "dev": true, + "requires": { + "ajv": "^6.12.2", + "atomically": "^1.3.1", + "debounce-fn": "^4.0.0", + "dot-prop": "^5.2.0", + "env-paths": "^2.2.0", + "json-schema-typed": "^7.0.3", + "make-dir": "^3.1.0", + "onetime": "^5.1.0", + "pkg-up": "^3.1.0", + "semver": "^7.3.2" + } + }, + "css-vendor": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" + } + }, + "csstype": { + "version": "2.6.14", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.14.tgz", + "integrity": "sha512-2mSc+VEpGPblzAxyeR+vZhJKgYg0Og0nnRi7pmRXFYYxSfnOnW8A5wwQb4n4cE2nIOzqKOAzLCaEX6aBmNEv8A==", + "dev": true + }, + "debounce-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", + "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", + "dev": true, + "requires": { + "mimic-fn": "^3.0.0" + } + }, + "dom-helpers": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.0.tgz", + "integrity": "sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + }, + "dependencies": { + "csstype": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.6.tgz", + "integrity": "sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw==", + "dev": true + } + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "env-paths": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", + "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dev": true, + "requires": { + "react-is": "^16.7.0" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + } + } + }, + "hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==", + "dev": true + }, + "indefinite-observable": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/indefinite-observable/-/indefinite-observable-2.0.1.tgz", + "integrity": "sha512-G8vgmork+6H9S8lUAg1gtXEj2JxIQTo0g2PbFiYOdjkziSI0F7UYBiVwhZRuixhBCNGczAls34+5HJPyZysvxQ==", + "dev": true, + "requires": { + "symbol-observable": "1.2.0" + } + }, + "is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=", + "dev": true + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-schema-typed": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", + "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==", + "dev": true + }, + "jss": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.5.0.tgz", + "integrity": "sha512-B6151NvG+thUg3murLNHRPLxTLwQ13ep4SH5brj4d8qKtogOx/jupnpfkPGSHPqvcwKJaCLctpj2lEk+5yGwMw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "csstype": "^3.0.2", + "indefinite-observable": "^2.0.1", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + }, + "dependencies": { + "csstype": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.6.tgz", + "integrity": "sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw==", + "dev": true + } + } + }, + "jss-plugin-camel-case": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.5.0.tgz", + "integrity": "sha512-GSjPL0adGAkuoqeYiXTgO7PlIrmjv5v8lA6TTBdfxbNYpxADOdGKJgIEkffhlyuIZHlPuuiFYTwUreLUmSn7rg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.5.0" + } + }, + "jss-plugin-default-unit": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.5.0.tgz", + "integrity": "sha512-rsbTtZGCMrbcb9beiDd+TwL991NGmsAgVYH0hATrYJtue9e+LH/Gn4yFD1ENwE+3JzF3A+rPnM2JuD9L/SIIWw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.0" + } + }, + "jss-plugin-global": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.5.0.tgz", + "integrity": "sha512-FZd9+JE/3D7HMefEG54fEC0XiQ9rhGtDHAT/ols24y8sKQ1D5KIw6OyXEmIdKFmACgxZV2ARQ5pAUypxkk2IFQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.0" + } + }, + "jss-plugin-nested": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.5.0.tgz", + "integrity": "sha512-ejPlCLNlEGgx8jmMiDk/zarsCZk+DV0YqXfddpgzbO9Toamo0HweCFuwJ3ZO40UFOfqKwfpKMVH/3HUXgxkTMg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.0", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-props-sort": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.5.0.tgz", + "integrity": "sha512-kTLRvrOetFKz5vM88FAhLNeJIxfjhCepnvq65G7xsAQ/Wgy7HwO1BS/2wE5mx8iLaAWC6Rj5h16mhMk9sKdZxg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.0" + } + }, + "jss-plugin-rule-value-function": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.5.0.tgz", + "integrity": "sha512-jXINGr8BSsB13JVuK274oEtk0LoooYSJqTBCGeBu2cG/VJ3+4FPs1gwLgsq24xTgKshtZ+WEQMVL34OprLidRA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.5.0", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-vendor-prefixer": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.5.0.tgz", + "integrity": "sha512-rux3gmfwDdOKCLDx0IQjTwTm03IfBa+Rm/hs747cOw5Q7O3RaTUIMPKjtVfc31Xr/XI9Abz2XEupk1/oMQ7zRA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.8", + "jss": "10.5.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + }, + "dependencies": { + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + } + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "popper.js": { + "version": "1.16.1-lts", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz", + "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==", + "dev": true + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "react-is": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", + "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", + "dev": true + }, + "react-transition-group": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", + "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true + }, + "tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } }, "@sinonjs/commons": { "version": "1.8.1", @@ -2796,7 +3433,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true + "dev": true, + "optional": true }, "har-schema": { "version": "2.0.0", @@ -3226,6 +3864,7 @@ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, + "optional": true, "requires": { "is-docker": "^2.0.0" } @@ -4382,6 +5021,7 @@ "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz", "integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==", "dev": true, + "optional": true, "requires": { "growly": "^1.3.0", "is-wsl": "^2.2.0", @@ -4396,6 +5036,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, + "optional": true, "requires": { "yallist": "^4.0.0" } @@ -4405,6 +5046,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", "dev": true, + "optional": true, "requires": { "lru-cache": "^6.0.0" } @@ -4414,6 +5056,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "optional": true, "requires": { "isexe": "^2.0.0" } @@ -4422,7 +5065,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "optional": true } } }, @@ -5438,7 +6082,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true + "dev": true, + "optional": true }, "signal-exit": { "version": "3.0.3", @@ -6315,7 +6960,8 @@ "version": "8.3.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", - "dev": true + "dev": true, + "optional": true }, "v8-to-istanbul": { "version": "7.0.0", diff --git a/extensions/pod-menu/src/logs-menu.tsx b/extensions/pod-menu/src/logs-menu.tsx index 706efcf128..1063207d0c 100644 --- a/extensions/pod-menu/src/logs-menu.tsx +++ b/extensions/pod-menu/src/logs-menu.tsx @@ -9,13 +9,9 @@ export class PodLogsMenu extends React.Component { Navigation.hideDetails(); const pod = this.props.object; - Component.createPodLogsTab({ - pod, - containers: pod.getContainers(), - initContainers: pod.getInitContainers(), + Component.logTabStore.createPodTab({ + selectedPod: pod, selectedContainer: container, - showTimestamps: false, - previous: false, }); } diff --git a/package.json b/package.json index 1b3cc13c32..3a06db621b 100644 --- a/package.json +++ b/package.json @@ -328,6 +328,7 @@ "react-refresh": "^0.9.0", "react-router-dom": "^5.2.0", "react-select": "^3.1.0", + "react-select-event": "^5.1.0", "react-window": "^1.8.5", "sass-loader": "^8.0.2", "sharp": "^0.26.1", diff --git a/src/common/search-store.ts b/src/common/search-store.ts index eb2517ca0f..86a6054af3 100644 --- a/src/common/search-store.ts +++ b/src/common/search-store.ts @@ -1,4 +1,5 @@ -import { action, computed, observable } from "mobx"; +import { action, computed, observable,reaction } from "mobx"; +import { dockStore } from "../renderer/components/dock/dock.store"; import { autobind } from "../renderer/utils"; export class SearchStore { @@ -6,6 +7,12 @@ export class SearchStore { @observable occurrences: number[] = []; // Array with line numbers, eg [0, 0, 10, 21, 21, 40...] @observable activeOverlayIndex = -1; // Index withing the occurences array. Showing where is activeOverlay currently located + constructor() { + reaction(() => dockStore.selectedTabId, () => { + searchStore.reset(); + }); + } + /** * Sets default activeOverlayIndex * @param text An array of any textual data (logs, for example) diff --git a/src/extensions/renderer-api/components.ts b/src/extensions/renderer-api/components.ts index a9a519498b..49c747da3a 100644 --- a/src/extensions/renderer-api/components.ts +++ b/src/extensions/renderer-api/components.ts @@ -38,4 +38,4 @@ export * from "../../renderer/components/+events/kube-event-details"; // specific exports export * from "../../renderer/components/status-brick"; export { terminalStore, createTerminalTab } from "../../renderer/components/dock/terminal.store"; -export { createPodLogsTab } from "../../renderer/components/dock/log.store"; +export { logTabStore } from "../../renderer/components/dock/log-tab.store"; diff --git a/src/renderer/api/kube-json-api.ts b/src/renderer/api/kube-json-api.ts index 3026a9a956..362ee5438e 100644 --- a/src/renderer/api/kube-json-api.ts +++ b/src/renderer/api/kube-json-api.ts @@ -21,7 +21,7 @@ export interface KubeJsonApiData extends JsonApiData { resourceVersion: string; continue?: string; finalizers?: string[]; - selfLink: string; + selfLink?: string; labels?: { [label: string]: string; }; diff --git a/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts b/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts index ad9713e96b..b8c8dee573 100644 --- a/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts +++ b/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts @@ -18,7 +18,7 @@ export class DaemonSetStore extends KubeObjectStore { } getChildPods(daemonSet: DaemonSet): Pod[] { - return podsStore.getPodsByOwner(daemonSet); + return podsStore.getPodsByOwnerId(daemonSet.getId()); } getStatuses(daemonSets?: DaemonSet[]) { diff --git a/src/renderer/components/+workloads-jobs/job.store.ts b/src/renderer/components/+workloads-jobs/job.store.ts index 569c9efb13..41d514df8d 100644 --- a/src/renderer/components/+workloads-jobs/job.store.ts +++ b/src/renderer/components/+workloads-jobs/job.store.ts @@ -10,7 +10,7 @@ export class JobStore extends KubeObjectStore { api = jobApi; getChildPods(job: Job): Pod[] { - return podsStore.getPodsByOwner(job); + return podsStore.getPodsByOwnerId(job.getId()); } getJobsByOwner(cronJob: CronJob) { diff --git a/src/renderer/components/+workloads-pods/pods.store.ts b/src/renderer/components/+workloads-pods/pods.store.ts index 9cd3c3b2f9..5a535cec66 100644 --- a/src/renderer/components/+workloads-pods/pods.store.ts +++ b/src/renderer/components/+workloads-pods/pods.store.ts @@ -3,8 +3,8 @@ import { action, observable } from "mobx"; import { KubeObjectStore } from "../../kube-object.store"; import { autobind, cpuUnitsToNumber, unitsToBytes } from "../../utils"; import { IPodMetrics, Pod, PodMetrics, podMetricsApi, podsApi } from "../../api/endpoints"; -import { WorkloadKubeObject } from "../../api/workload-kube-object"; import { apiManager } from "../../api/api-manager"; +import { WorkloadKubeObject } from "../../api/workload-kube-object"; @autobind() export class PodsStore extends KubeObjectStore { @@ -44,6 +44,12 @@ export class PodsStore extends KubeObjectStore { }); } + getPodsByOwnerId(workloadId: string): Pod[] { + return this.items.filter(pod => { + return pod.getOwnerRefs().find(owner => owner.uid === workloadId); + }); + } + getPodsByNode(node: string) { if (!this.isLoaded) return []; diff --git a/src/renderer/components/+workloads-replicasets/replicasets.store.ts b/src/renderer/components/+workloads-replicasets/replicasets.store.ts index 337f9c0ae1..ca58006930 100644 --- a/src/renderer/components/+workloads-replicasets/replicasets.store.ts +++ b/src/renderer/components/+workloads-replicasets/replicasets.store.ts @@ -18,7 +18,7 @@ export class ReplicaSetStore extends KubeObjectStore { } getChildPods(replicaSet: ReplicaSet) { - return podsStore.getPodsByOwner(replicaSet); + return podsStore.getPodsByOwnerId(replicaSet.getId()); } getStatuses(replicaSets: ReplicaSet[]) { diff --git a/src/renderer/components/+workloads-statefulsets/statefulset.store.ts b/src/renderer/components/+workloads-statefulsets/statefulset.store.ts index 12f1f663b9..6ee4bb5c28 100644 --- a/src/renderer/components/+workloads-statefulsets/statefulset.store.ts +++ b/src/renderer/components/+workloads-statefulsets/statefulset.store.ts @@ -17,7 +17,7 @@ export class StatefulSetStore extends KubeObjectStore { } getChildPods(statefulSet: StatefulSet) { - return podsStore.getPodsByOwner(statefulSet); + return podsStore.getPodsByOwnerId(statefulSet.getId()); } getStatuses(statefulSets: StatefulSet[]) { diff --git a/src/renderer/components/dock/__test__/log-resource-selector.test.tsx b/src/renderer/components/dock/__test__/log-resource-selector.test.tsx new file mode 100644 index 0000000000..22d97b7216 --- /dev/null +++ b/src/renderer/components/dock/__test__/log-resource-selector.test.tsx @@ -0,0 +1,103 @@ +/** + * @jest-environment jsdom + */ + +import React from "react"; +import "@testing-library/jest-dom/extend-expect"; +import { render } from "@testing-library/react"; +import selectEvent from "react-select-event"; + +import { Pod } from "../../../api/endpoints"; +import { LogResourceSelector } from "../log-resource-selector"; +import { LogTabData } from "../log-tab.store"; +import { dockerPod, deploymentPod1 } from "./pod.mock"; + +const getComponent = (tabData: LogTabData) => { + return ( + + ); +}; + +const getOnePodTabData = (): LogTabData => { + const selectedPod = new Pod(dockerPod); + + return { + pods: [] as Pod[], + selectedPod, + selectedContainer: selectedPod.getContainers()[0], + }; +}; + +const getFewPodsTabData = (): LogTabData => { + const selectedPod = new Pod(deploymentPod1); + const anotherPod = new Pod(dockerPod); + + return { + pods: [anotherPod], + selectedPod, + selectedContainer: selectedPod.getContainers()[0], + }; +}; + +describe("", () => { + it("renders w/o errors", () => { + const tabData = getOnePodTabData(); + const { container } = render(getComponent(tabData)); + + expect(container).toBeInstanceOf(HTMLElement); + }); + + it("renders proper namespace", () => { + const tabData = getOnePodTabData(); + const { getByTestId } = render(getComponent(tabData)); + const ns = getByTestId("namespace-badge"); + + expect(ns).toHaveTextContent("default"); + }); + + it("renders proper selected items within dropdowns", () => { + const tabData = getOnePodTabData(); + const { getByText } = render(getComponent(tabData)); + + expect(getByText("dockerExporter")).toBeInTheDocument(); + expect(getByText("docker-exporter")).toBeInTheDocument(); + }); + + it("renders sibling pods in dropdown", () => { + const tabData = getFewPodsTabData(); + const { container, getByText } = render(getComponent(tabData)); + const podSelector: HTMLElement = container.querySelector(".pod-selector"); + + selectEvent.openMenu(podSelector); + + expect(getByText("dockerExporter")).toBeInTheDocument(); + expect(getByText("deploymentPod1")).toBeInTheDocument(); + }); + + it("renders sibling containers in dropdown", () => { + const tabData = getFewPodsTabData(); + const { getByText, container } = render(getComponent(tabData)); + const containerSelector: HTMLElement = container.querySelector(".container-selector"); + + selectEvent.openMenu(containerSelector); + + expect(getByText("node-exporter-1")).toBeInTheDocument(); + expect(getByText("init-node-exporter")).toBeInTheDocument(); + expect(getByText("init-node-exporter-1")).toBeInTheDocument(); + }); + + it("renders pod owner as dropdown title", () => { + const tabData = getFewPodsTabData(); + const { getByText, container } = render(getComponent(tabData)); + const podSelector: HTMLElement = container.querySelector(".pod-selector"); + + selectEvent.openMenu(podSelector); + + expect(getByText("super-deployment")).toBeInTheDocument(); + }); +}); diff --git a/src/renderer/components/dock/__test__/log-tab.store.test.ts b/src/renderer/components/dock/__test__/log-tab.store.test.ts new file mode 100644 index 0000000000..79b93af623 --- /dev/null +++ b/src/renderer/components/dock/__test__/log-tab.store.test.ts @@ -0,0 +1,113 @@ +/** + * @jest-environment jsdom + */ + +import { podsStore } from "../../+workloads-pods/pods.store"; +import { Pod } from "../../../api/endpoints"; +import { dockStore } from "../dock.store"; +import { logTabStore } from "../log-tab.store"; +import { deploymentPod1, deploymentPod2, deploymentPod3, dockerPod } from "./pod.mock"; + + +podsStore.items.push(new Pod(dockerPod)); +podsStore.items.push(new Pod(deploymentPod1)); +podsStore.items.push(new Pod(deploymentPod2)); + +describe("log tab store", () => { + afterEach(() => { + logTabStore.reset(); + dockStore.reset(); + }); + + it("creates log tab without sibling pods", () => { + const selectedPod = new Pod(dockerPod); + const selectedContainer = selectedPod.getAllContainers()[0]; + + logTabStore.createPodTab({ + selectedPod, + selectedContainer + }); + + expect(logTabStore.getData(dockStore.selectedTabId)).toEqual({ + pods: [selectedPod], + selectedPod, + selectedContainer, + showTimestamps: false, + previous: false + }); + }); + + it("creates log tab with sibling pods", () => { + const selectedPod = new Pod(deploymentPod1); + const siblingPod = new Pod(deploymentPod2); + const selectedContainer = selectedPod.getInitContainers()[0]; + + logTabStore.createPodTab({ + selectedPod, + selectedContainer + }); + + expect(logTabStore.getData(dockStore.selectedTabId)).toEqual({ + pods: [selectedPod, siblingPod], + selectedPod, + selectedContainer, + showTimestamps: false, + previous: false + }); + }); + + it("removes item from pods list if pod deleted from store", () => { + const selectedPod = new Pod(deploymentPod1); + const selectedContainer = selectedPod.getInitContainers()[0]; + + logTabStore.createPodTab({ + selectedPod, + selectedContainer + }); + + podsStore.items.pop(); + + expect(logTabStore.getData(dockStore.selectedTabId)).toEqual({ + pods: [selectedPod], + selectedPod, + selectedContainer, + showTimestamps: false, + previous: false + }); + }); + + it("adds item into pods list if new sibling pod added to store", () => { + const selectedPod = new Pod(deploymentPod1); + const selectedContainer = selectedPod.getInitContainers()[0]; + + logTabStore.createPodTab({ + selectedPod, + selectedContainer + }); + + podsStore.items.push(new Pod(deploymentPod3)); + + expect(logTabStore.getData(dockStore.selectedTabId)).toEqual({ + pods: [selectedPod, deploymentPod3], + selectedPod, + selectedContainer, + showTimestamps: false, + previous: false + }); + }); + + it("closes tab if no pods left in store", () => { + const selectedPod = new Pod(deploymentPod1); + const selectedContainer = selectedPod.getInitContainers()[0]; + + logTabStore.createPodTab({ + selectedPod, + selectedContainer + }); + + podsStore.items.clear(); + + expect(logTabStore.getData(dockStore.selectedTabId)).toBeUndefined(); + expect(dockStore.getTabById(dockStore.selectedTabId)).toBeUndefined(); + }); +}); diff --git a/src/renderer/components/dock/__test__/pod.mock.ts b/src/renderer/components/dock/__test__/pod.mock.ts new file mode 100644 index 0000000000..acb4704395 --- /dev/null +++ b/src/renderer/components/dock/__test__/pod.mock.ts @@ -0,0 +1,203 @@ +export const dockerPod = { + apiVersion: "v1", + kind: "dummy", + metadata: { + uid: "dockerExporter", + name: "dockerExporter", + creationTimestamp: "dummy", + resourceVersion: "dummy", + namespace: "default" + }, + spec: { + initContainers: [] as any, + containers: [ + { + name: "docker-exporter", + image: "docker.io/prom/node-exporter:v1.0.0-rc.0", + imagePullPolicy: "pull" + } + ], + serviceAccountName: "dummy", + serviceAccount: "dummy", + }, + status: { + phase: "Running", + conditions: [{ + type: "Running", + status: "Running", + lastProbeTime: 1, + lastTransitionTime: "Some time", + }], + hostIP: "dummy", + podIP: "dummy", + startTime: "dummy", + } +}; + +export const deploymentPod1 = { + apiVersion: "v1", + kind: "dummy", + metadata: { + uid: "deploymentPod1", + name: "deploymentPod1", + creationTimestamp: "dummy", + resourceVersion: "dummy", + namespace: "default", + ownerReferences: [{ + apiVersion: "v1", + kind: "Deployment", + name: "super-deployment", + uid: "uuid", + controller: true, + blockOwnerDeletion: true, + }] + }, + spec: { + initContainers: [ + { + name: "init-node-exporter", + image: "docker.io/prom/node-exporter:v1.0.0-rc.0", + imagePullPolicy: "pull" + }, + { + name: "init-node-exporter-1", + image: "docker.io/prom/node-exporter:v1.0.0-rc.0", + imagePullPolicy: "pull" + } + ], + containers: [ + { + name: "node-exporter", + image: "docker.io/prom/node-exporter:v1.0.0-rc.0", + imagePullPolicy: "pull" + }, + { + name: "node-exporter-1", + image: "docker.io/prom/node-exporter:v1.0.0-rc.0", + imagePullPolicy: "pull" + } + ], + serviceAccountName: "dummy", + serviceAccount: "dummy", + }, + status: { + phase: "Running", + conditions: [{ + type: "Running", + status: "Running", + lastProbeTime: 1, + lastTransitionTime: "Some time", + }], + hostIP: "dummy", + podIP: "dummy", + startTime: "dummy", + } +}; + +export const deploymentPod2 = { + apiVersion: "v1", + kind: "dummy", + metadata: { + uid: "deploymentPod2", + name: "deploymentPod2", + creationTimestamp: "dummy", + resourceVersion: "dummy", + namespace: "default", + ownerReferences: [{ + apiVersion: "v1", + kind: "Deployment", + name: "super-deployment", + uid: "uuid", + controller: true, + blockOwnerDeletion: true, + }] + }, + spec: { + initContainers: [ + { + name: "init-node-exporter", + image: "docker.io/prom/node-exporter:v1.0.0-rc.0", + imagePullPolicy: "pull" + }, + { + name: "init-node-exporter-1", + image: "docker.io/prom/node-exporter:v1.0.0-rc.0", + imagePullPolicy: "pull" + } + ], + containers: [ + { + name: "node-exporter", + image: "docker.io/prom/node-exporter:v1.0.0-rc.0", + imagePullPolicy: "pull" + }, + { + name: "node-exporter-1", + image: "docker.io/prom/node-exporter:v1.0.0-rc.0", + imagePullPolicy: "pull" + } + ], + serviceAccountName: "dummy", + serviceAccount: "dummy", + }, + status: { + phase: "Running", + conditions: [{ + type: "Running", + status: "Running", + lastProbeTime: 1, + lastTransitionTime: "Some time", + }], + hostIP: "dummy", + podIP: "dummy", + startTime: "dummy", + } +}; + +export const deploymentPod3 = { + apiVersion: "v1", + kind: "dummy", + metadata: { + uid: "deploymentPod3", + name: "deploymentPod3", + creationTimestamp: "dummy", + resourceVersion: "dummy", + namespace: "default", + ownerReferences: [{ + apiVersion: "v1", + kind: "Deployment", + name: "super-deployment", + uid: "uuid", + controller: true, + blockOwnerDeletion: true, + }] + }, + spec: { + containers: [ + { + name: "node-exporter", + image: "docker.io/prom/node-exporter:v1.0.0-rc.0", + imagePullPolicy: "pull" + }, + { + name: "node-exporter-1", + image: "docker.io/prom/node-exporter:v1.0.0-rc.0", + imagePullPolicy: "pull" + } + ], + serviceAccountName: "dummy", + serviceAccount: "dummy", + }, + status: { + phase: "Running", + conditions: [{ + type: "Running", + status: "Running", + lastProbeTime: 1, + lastTransitionTime: "Some time", + }], + hostIP: "dummy", + podIP: "dummy", + startTime: "dummy", + } +}; diff --git a/src/renderer/components/dock/dock-tabs.tsx b/src/renderer/components/dock/dock-tabs.tsx index 554411024b..d0a3c3d125 100644 --- a/src/renderer/components/dock/dock-tabs.tsx +++ b/src/renderer/components/dock/dock-tabs.tsx @@ -7,7 +7,7 @@ import { DockTab } from "./dock-tab"; import { IDockTab } from "./dock.store"; import { isEditResourceTab } from "./edit-resource.store"; import { isInstallChartTab } from "./install-chart.store"; -import { isLogsTab } from "./log.store"; +import { isLogsTab } from "./log-tab.store"; import { TerminalTab } from "./terminal-tab"; import { isTerminalTab } from "./terminal.store"; import { isUpgradeChartTab } from "./upgrade-chart.store"; diff --git a/src/renderer/components/dock/dock.store.ts b/src/renderer/components/dock/dock.store.ts index 91d72d98d9..423367093e 100644 --- a/src/renderer/components/dock/dock.store.ts +++ b/src/renderer/components/dock/dock.store.ts @@ -208,6 +208,12 @@ export class DockStore { this.closeTabs(tabs); } + renameTab(tabId: TabId, title: string) { + const tab = this.getTabById(tabId); + + tab.title = title; + } + @action selectTab(tabId: TabId) { this.selectedTabId = this.getTabById(tabId)?.id ?? null; diff --git a/src/renderer/components/dock/dock.tsx b/src/renderer/components/dock/dock.tsx index c8adf82992..45e294b3c6 100644 --- a/src/renderer/components/dock/dock.tsx +++ b/src/renderer/components/dock/dock.tsx @@ -17,7 +17,7 @@ import { isEditResourceTab } from "./edit-resource.store"; import { InstallChart } from "./install-chart"; import { isInstallChartTab } from "./install-chart.store"; import { Logs } from "./logs"; -import { isLogsTab } from "./log.store"; +import { isLogsTab } from "./log-tab.store"; import { TerminalWindow } from "./terminal-window"; import { createTerminalTab, isTerminalTab } from "./terminal.store"; import { UpgradeChart } from "./upgrade-chart"; diff --git a/src/renderer/components/dock/log-controls.tsx b/src/renderer/components/dock/log-controls.tsx index 06bbf12863..8400aef584 100644 --- a/src/renderer/components/dock/log-controls.tsx +++ b/src/renderer/components/dock/log-controls.tsx @@ -5,22 +5,23 @@ import { observer } from "mobx-react"; import { Pod } from "../../api/endpoints"; import { cssNames, saveFileDialog } from "../../utils"; -import { IPodLogsData, podLogsStore } from "./log.store"; +import { logStore } from "./log.store"; import { Checkbox } from "../checkbox"; import { Icon } from "../icon"; +import { LogTabData } from "./log-tab.store"; interface Props { - tabData: IPodLogsData + tabData: LogTabData logs: string[] - save: (data: Partial) => void + save: (data: Partial) => void reload: () => void } export const LogControls = observer((props: Props) => { const { tabData, save, reload, logs } = props; const { showTimestamps, previous } = tabData; - const since = logs.length ? podLogsStore.getTimestamps(logs[0]) : null; - const pod = new Pod(tabData.pod); + const since = logs.length ? logStore.getTimestamps(logs[0]) : null; + const pod = new Pod(tabData.selectedPod); const toggleTimestamps = () => { save({ showTimestamps: !showTimestamps }); @@ -33,7 +34,7 @@ export const LogControls = observer((props: Props) => { const downloadLogs = () => { const fileName = pod.getName(); - const logsToDownload = showTimestamps ? logs : podLogsStore.logsWithoutTimestamps; + const logsToDownload = showTimestamps ? logs : logStore.logsWithoutTimestamps; saveFileDialog(`${fileName}.log`, logsToDownload.join("\n"), "text/plain"); }; diff --git a/src/renderer/components/dock/log-list.tsx b/src/renderer/components/dock/log-list.tsx index 74d64d2f58..3b66f42d86 100644 --- a/src/renderer/components/dock/log-list.tsx +++ b/src/renderer/components/dock/log-list.tsx @@ -14,7 +14,8 @@ import { Button } from "../button"; import { Icon } from "../icon"; import { Spinner } from "../spinner"; import { VirtualList } from "../virtual-list"; -import { podLogsStore } from "./log.store"; +import { logStore } from "./log.store"; +import { logTabStore } from "./log-tab.store"; interface Props { logs: string[] @@ -77,10 +78,10 @@ export class LogList extends React.Component { */ @computed get logs() { - const showTimestamps = podLogsStore.getData(this.props.id).showTimestamps; + const showTimestamps = logTabStore.getData(this.props.id).showTimestamps; if (!showTimestamps) { - return podLogsStore.logsWithoutTimestamps; + return logStore.logsWithoutTimestamps; } return this.props.logs; diff --git a/src/renderer/components/dock/log-resource-selector.tsx b/src/renderer/components/dock/log-resource-selector.tsx index a4b79bcfad..c6f1bee300 100644 --- a/src/renderer/components/dock/log-resource-selector.tsx +++ b/src/renderer/components/dock/log-resource-selector.tsx @@ -1,27 +1,30 @@ import "./log-resource-selector.scss"; -import React from "react"; +import React, { useEffect } from "react"; import { observer } from "mobx-react"; -import { IPodContainer, Pod } from "../../api/endpoints"; +import { Pod } from "../../api/endpoints"; import { Badge } from "../badge"; import { Select, SelectOption } from "../select"; -import { IPodLogsData } from "./log.store"; +import { LogTabData, logTabStore } from "./log-tab.store"; +import { podsStore } from "../+workloads-pods/pods.store"; +import { TabId } from "./dock.store"; interface Props { - tabData: IPodLogsData - save: (data: Partial) => void + tabId: TabId + tabData: LogTabData + save: (data: Partial) => void reload: () => void } export const LogResourceSelector = observer((props: Props) => { - const { tabData, save, reload } = props; - const { selectedContainer, containers, initContainers } = tabData; - const pod = new Pod(tabData.pod); + const { tabData, save, reload, tabId } = props; + const { selectedPod, selectedContainer, pods } = tabData; + const pod = new Pod(selectedPod); + const containers = pod.getContainers(); + const initContainers = pod.getInitContainers(); const onContainerChange = (option: SelectOption) => { - const { containers, initContainers } = tabData; - save({ selectedContainer: containers .concat(initContainers) @@ -30,11 +33,18 @@ export const LogResourceSelector = observer((props: Props) => { reload(); }; - const getSelectOptions = (containers: IPodContainer[]) => { - return containers.map(container => { + const onPodChange = (option: SelectOption) => { + const selectedPod = podsStore.getByName(option.value, pod.getNs()); + + save({ selectedPod }); + logTabStore.renameTab(tabId); + }; + + const getSelectOptions = (items: string[]) => { + return items.map(item => { return { - value: container.name, - label: container.name + value: item, + label: item }; }); }; @@ -42,24 +52,43 @@ export const LogResourceSelector = observer((props: Props) => { const containerSelectOptions = [ { label: `Containers`, - options: getSelectOptions(containers) + options: getSelectOptions(containers.map(container => container.name)) }, { label: `Init Containers`, - options: getSelectOptions(initContainers), + options: getSelectOptions(initContainers.map(container => container.name)), } ]; + const podSelectOptions = [ + { + label: pod.getOwnerRefs()[0]?.name, + options: getSelectOptions(pods.map(pod => pod.metadata.name)) + } + ]; + + useEffect(() => { + reload(); + }, [selectedPod]); + return (
- Namespace - Pod + Namespace + Pod +
); diff --git a/src/renderer/components/dock/log-tab.store.ts b/src/renderer/components/dock/log-tab.store.ts new file mode 100644 index 0000000000..3eec7812be --- /dev/null +++ b/src/renderer/components/dock/log-tab.store.ts @@ -0,0 +1,123 @@ +import uniqueId from "lodash/uniqueId"; +import { reaction } from "mobx"; +import { podsStore } from "../+workloads-pods/pods.store"; + +import { IPodContainer, Pod } from "../../api/endpoints"; +import { WorkloadKubeObject } from "../../api/workload-kube-object"; +import { DockTabStore } from "./dock-tab.store"; +import { dockStore, IDockTab, TabKind } from "./dock.store"; + +export interface LogTabData { + pods: Pod[]; + selectedPod: Pod; + selectedContainer: IPodContainer + showTimestamps?: boolean + previous?: boolean +} + +interface PodLogsTabData { + selectedPod: Pod + selectedContainer: IPodContainer +} + +interface WorkloadLogsTabData { + workload: WorkloadKubeObject +} + +export class LogTabStore extends DockTabStore { + constructor() { + super({ + storageName: "pod_logs" + }); + + reaction(() => podsStore.items.length, () => { + this.updateTabsData(); + }); + } + + createPodTab({ selectedPod, selectedContainer }: PodLogsTabData): void { + const podOwner = selectedPod.getOwnerRefs()[0]; + const pods = podsStore.getPodsByOwnerId(podOwner?.uid); + const title = `Pod ${selectedPod.getName()}`; + + this.createLogsTab(title, { + pods: pods.length ? pods : [selectedPod], + selectedPod, + selectedContainer + }); + } + + createWorkloadTab({ workload }: WorkloadLogsTabData): void { + const pods = podsStore.getPodsByOwnerId(workload.getId()); + + if (!pods.length) return; + + const selectedPod = pods[0]; + const selectedContainer = selectedPod.getAllContainers()[0]; + const title = `${workload.kind} ${selectedPod.getName()}`; + + this.createLogsTab(title, { + pods, + selectedPod, + selectedContainer + }); + } + + renameTab(tabId: string) { + const { selectedPod } = this.getData(tabId); + + dockStore.renameTab(tabId, `Pod ${selectedPod.metadata.name}`); + } + + private createDockTab(tabParams: Partial) { + dockStore.createTab({ + kind: TabKind.POD_LOGS, + ...tabParams + }, false); + } + + private createLogsTab(title: string, data: LogTabData) { + const id = uniqueId("log-tab-"); + + this.createDockTab({ id, title }); + this.setData(id, { + ...data, + showTimestamps: false, + previous: false + }); + } + + private updateTabsData() { + this.data.forEach((tabData, tabId) => { + const pod = new Pod(tabData.selectedPod); + const pods = podsStore.getPodsByOwnerId(pod.getOwnerRefs()[0]?.uid); + const isSelectedPodInList = pods.find(item => item.getId() == pod.getId()); + const selectedPod = isSelectedPodInList ? pod : pods[0]; + const selectedContainer = isSelectedPodInList ? tabData.selectedContainer : pod.getAllContainers()[0]; + + if (pods.length) { + this.setData(tabId, { + ...tabData, + selectedPod, + selectedContainer, + pods + }); + + this.renameTab(tabId); + } else { + this.closeTab(tabId); + } + }); + } + + private closeTab(tabId: string) { + this.clearData(tabId); + dockStore.closeTab(tabId); + } +} + +export const logTabStore = new LogTabStore(); + +export function isLogsTab(tab: IDockTab) { + return tab && tab.kind === TabKind.POD_LOGS; +} diff --git a/src/renderer/components/dock/log.store.ts b/src/renderer/components/dock/log.store.ts index 4dcfccf981..14dc9efdd0 100644 --- a/src/renderer/components/dock/log.store.ts +++ b/src/renderer/components/dock/log.store.ts @@ -1,27 +1,16 @@ -import { autorun, computed, observable, reaction } from "mobx"; -import { Pod, IPodContainer, podsApi, IPodLogsQuery } from "../../api/endpoints"; +import { autorun, computed, observable } from "mobx"; + +import { IPodLogsQuery, Pod, podsApi } from "../../api/endpoints"; import { autobind, interval } from "../../utils"; -import { DockTabStore } from "./dock-tab.store"; -import { dockStore, IDockTab, TabKind } from "./dock.store"; -import { searchStore } from "../../../common/search-store"; +import { dockStore, TabId } from "./dock.store"; +import { isLogsTab, logTabStore } from "./log-tab.store"; -export interface IPodLogsData { - pod: Pod; - selectedContainer: IPodContainer - containers: IPodContainer[] - initContainers: IPodContainer[] - showTimestamps: boolean - previous: boolean -} - -type TabId = string; type PodLogLine = string; -// Number for log lines to load -export const logRange = 500; +const logLinesToLoad = 500; @autobind() -export class LogStore extends DockTabStore { +export class LogStore { private refresher = interval(10, () => { const id = dockStore.selectedTabId; @@ -30,12 +19,8 @@ export class LogStore extends DockTabStore { }); @observable podLogs = observable.map(); - @observable newLogSince = observable.map(); // Timestamp after which all logs are considered to be new constructor() { - super({ - storageName: "pod_logs" - }); autorun(() => { const { selectedTab, isOpen } = dockStore; @@ -45,15 +30,6 @@ export class LogStore extends DockTabStore { this.refresher.stop(); } }, { delay: 500 }); - - reaction(() => this.podLogs.get(dockStore.selectedTabId), () => { - this.setNewLogSince(dockStore.selectedTabId); - }); - - reaction(() => dockStore.selectedTabId, () => { - // Clear search query on tab change - searchStore.reset(); - }); } /** @@ -66,7 +42,7 @@ export class LogStore extends DockTabStore { load = async (tabId: TabId) => { try { const logs = await this.loadLogs(tabId, { - tailLines: this.lines + logRange + tailLines: this.lines + logLinesToLoad }); this.refresher.start(); @@ -107,9 +83,9 @@ export class LogStore extends DockTabStore { * @returns {Promise} A fetch request promise */ loadLogs = async (tabId: TabId, params: Partial) => { - const data = this.getData(tabId); + const data = logTabStore.getData(tabId); const { selectedContainer, previous } = data; - const pod = new Pod(data.pod); + const pod = new Pod(data.selectedPod); const namespace = pod.getNs(); const name = pod.getName(); @@ -127,17 +103,6 @@ export class LogStore extends DockTabStore { }); }; - /** - * Sets newLogSince separator timestamp to split old logs from new ones - * @param tabId - */ - setNewLogSince(tabId: TabId) { - if (!this.podLogs.has(tabId) || !this.podLogs.get(tabId).length || this.newLogSince.has(tabId)) return; - const timestamp = this.getLastSinceTime(tabId); - - this.newLogSince.set(tabId, timestamp.split(".")[0]); // Removing milliseconds from string - } - /** * Converts logs into a string array * @returns {number} Length of log lines @@ -196,37 +161,6 @@ export class LogStore extends DockTabStore { clearLogs(tabId: TabId) { this.podLogs.delete(tabId); } - - clearData(tabId: TabId) { - this.data.delete(tabId); - this.clearLogs(tabId); - } } -export const podLogsStore = new LogStore(); - -export function createPodLogsTab(data: IPodLogsData, tabParams: Partial = {}) { - const podId = data.pod.getId(); - let tab = dockStore.getTabById(podId); - - if (tab) { - dockStore.open(); - dockStore.selectTab(tab.id); - - return; - } - // If no existent tab found - tab = dockStore.createTab({ - id: podId, - kind: TabKind.POD_LOGS, - title: data.pod.getName(), - ...tabParams - }, false); - podLogsStore.setData(tab.id, data); - - return tab; -} - -export function isLogsTab(tab: IDockTab) { - return tab && tab.kind === TabKind.POD_LOGS; -} +export const logStore = new LogStore(); diff --git a/src/renderer/components/dock/logs.tsx b/src/renderer/components/dock/logs.tsx index c8ccd6d249..0aa31f95fb 100644 --- a/src/renderer/components/dock/logs.tsx +++ b/src/renderer/components/dock/logs.tsx @@ -8,9 +8,10 @@ import { IDockTab } from "./dock.store"; import { InfoPanel } from "./info-panel"; import { LogResourceSelector } from "./log-resource-selector"; import { LogList } from "./log-list"; -import { IPodLogsData, podLogsStore } from "./log.store"; +import { logStore } from "./log.store"; import { LogSearch } from "./log-search"; import { LogControls } from "./log-controls"; +import { LogTabData, logTabStore } from "./log-tab.store"; interface Props { className?: string @@ -30,7 +31,7 @@ export class Logs extends React.Component { } get tabData() { - return podLogsStore.getData(this.tabId); + return logTabStore.getData(this.tabId); } get tabId() { @@ -38,18 +39,18 @@ export class Logs extends React.Component { } @autobind() - save(data: Partial) { - podLogsStore.setData(this.tabId, { ...this.tabData, ...data }); + save(data: Partial) { + logTabStore.setData(this.tabId, { ...this.tabData, ...data }); } load = async () => { this.isLoading = true; - await podLogsStore.load(this.tabId); + await logStore.load(this.tabId); this.isLoading = false; }; reload = async () => { - podLogsStore.clearLogs(this.tabId); + logStore.clearLogs(this.tabId); await this.load(); }; @@ -82,11 +83,12 @@ export class Logs extends React.Component { } renderResourceSelector() { - const logs = podLogsStore.logs; - const searchLogs = this.tabData.showTimestamps ? logs : podLogsStore.logsWithoutTimestamps; + const logs = logStore.logs; + const searchLogs = this.tabData.showTimestamps ? logs : logStore.logsWithoutTimestamps; const controls = (
{ } render() { - const logs = podLogsStore.logs; + const logs = logStore.logs; return (
diff --git a/src/renderer/kube-object.store.ts b/src/renderer/kube-object.store.ts index 956f5aa5f6..8a75bc7ae6 100644 --- a/src/renderer/kube-object.store.ts +++ b/src/renderer/kube-object.store.ts @@ -47,6 +47,10 @@ export abstract class KubeObjectStore extends ItemSt } } + getById(id: string) { + return this.items.find(item => item.getId() === id); + } + getByName(name: string, namespace?: string): T { return this.items.find(item => { return item.getName() === name && ( diff --git a/yarn.lock b/yarn.lock index 5e1cfa376d..dd9ec0c1c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -267,6 +267,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" + integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.10.1", "@babel/template@^7.3.3": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811" @@ -775,6 +782,17 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@jest/types@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" + integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + "@kubernetes/client-node@^0.12.0": version "0.12.0" resolved "https://registry.yarnpkg.com/@kubernetes/client-node/-/client-node-0.12.0.tgz#79120311bced206ac8fa36435fb4cc2c1828fff2" @@ -955,6 +973,20 @@ dependencies: defer-to-connect "^1.0.1" +"@testing-library/dom@>=7": + version "7.29.4" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.29.4.tgz#1647c2b478789621ead7a50614ad81ab5ae5b86c" + integrity sha512-CtrJRiSYEfbtNGtEsd78mk1n1v2TUbeABlNIcOCJdDfkN5/JTOwQEbbQpoSRxGqzcWPgStMvJ4mNolSuBRv1NA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^4.2.0" + aria-query "^4.2.2" + chalk "^4.1.0" + dom-accessibility-api "^0.5.4" + lz-string "^1.4.4" + pretty-format "^26.6.2" + "@testing-library/dom@^7.26.0": version "7.26.3" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.26.3.tgz#5554ee985f712d621bd676104b879f85d9a7a0ef" @@ -4552,7 +4584,7 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-accessibility-api@^0.5.1: +dom-accessibility-api@^0.5.1, dom-accessibility-api@^0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.4.tgz#b06d059cdd4a4ad9a79275f9d414a5c126241166" integrity sha512-TvrjBckDy2c6v6RLxPv5QXOnU+SmF9nBII5621Ve5fu6Z/BDrENurBEvlC1f44lKEUVqOpK4w9E5Idc5/EgkLQ== @@ -10822,6 +10854,16 @@ pretty-format@^26.0.1: ansi-styles "^4.0.0" react-is "^16.12.0" +pretty-format@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" + integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== + dependencies: + "@jest/types" "^26.6.2" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^17.0.1" + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -11198,6 +11240,13 @@ react-router@5.2.0, react-router@^5.2.0: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" +react-select-event@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/react-select-event/-/react-select-event-5.1.0.tgz#d45ef68f2a9c872903e8c9725f3ae6e7576f7be0" + integrity sha512-D5DzJlYCdZsGbDVFMQFynrG0OLalJM3ZzDT7KQADNVWE604JCeQF9bIuvPZqVD7IzhnPsFzOUCsilzDA6w6WRQ== + dependencies: + "@testing-library/dom" ">=7" + react-select@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/react-select/-/react-select-3.1.0.tgz#ab098720b2e9fe275047c993f0d0caf5ded17c27"