├── .all-contributorsrc ├── .github └── workflows │ └── lualint.yml ├── README.md ├── lua └── kube-utils-nvim │ ├── command.lua │ ├── formatjson.lua │ ├── helm.lua │ ├── init.lua │ ├── k9s.lua │ ├── kubectl.lua │ ├── repository.lua │ ├── telescope_picker.lua │ ├── toggle_lsp.lua │ └── utils.lua └── renovate.json /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "commitType": "docs", 8 | "commitConvention": "angular", 9 | "contributors": [ 10 | { 11 | "login": "h4ckm1n-dev", 12 | "name": "h4ckm1n", 13 | "avatar_url": "https://avatars.githubusercontent.com/u/97511408?v=4", 14 | "profile": "https://github.com/h4ckm1n-dev", 15 | "contributions": [ 16 | "code" 17 | ] 18 | }, 19 | { 20 | "login": "ohdearaugustin", 21 | "name": "ohdearaugustin", 22 | "avatar_url": "https://avatars.githubusercontent.com/u/14001491?v=4", 23 | "profile": "https://github.com/ohdearaugustin", 24 | "contributions": [ 25 | "code" 26 | ] 27 | } 28 | ], 29 | "contributorsPerLine": 7, 30 | "skipCi": true, 31 | "repoType": "github", 32 | "repoHost": "https://github.com", 33 | "projectName": "kube-utils-nvim", 34 | "projectOwner": "h4ckm1n-dev" 35 | } 36 | -------------------------------------------------------------------------------- /.github/workflows/lualint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | lint: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v4 18 | 19 | - name: Install dependencies 20 | run: | 21 | # Install LuaRocks 22 | sudo apt-get update 23 | sudo apt-get install -y luarocks 24 | 25 | # Install luacheck using LuaRocks 26 | sudo luarocks install luacheck 27 | 28 | - name: Lint code 29 | run: | 30 | # Run linting command and store the exit code 31 | luacheck . --globals vim || exit 0 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Neovim Kubernetes Plugin 2 | 3 | For the lazy, you can install my full config lazyvim with kube-utils-nvim installed [[template]](https://github.com/h4ckm1n-dev/h4ckm1n-lazyvim-template) 4 | 5 | ![All Contributors](https://img.shields.io/badge/all_contributors-2-orange.svg?style=flat-square) 6 | 7 | This Neovim plugin provides seamless integration with Kubernetes and Helm, allowing you to deploy and manage Kubernetes resources directly from your editor. 8 | 9 | ## Features 10 | 11 | - **Kubernetes Context and Namespace Management**: Easily switch between different Kubernetes contexts and namespaces. 12 | - **CRD Viewer:** View Custom Resource Definitions directly in Neovim. 13 | - **Helm Integration:** Manage Helm charts, update dependencies, and deploy directly from Neovim. 14 | - **Log Viewer and Formatting:** Transform Kubernetes log files into a more readable JSON format. 15 | - **Telescope Integration:** Utilize Telescope for picking Kubernetes contexts, namespaces, and viewing Helm releases. 16 | - **LSP Integration:** Toggle YAML and Helm LSP settings based on the context within Neovim. 17 | - **K9s Integration:** Open k9s from nvim 18 | 19 | #### Screenshots 20 | 21 | Here are some visual previews of the plugin in action 22 | 23 | Telescope NS selector : 24 | 25 | - ![Screenshot 1](https://github.com/h4ckm1n-dev/kube-utils-nvim/assets/97511408/bbfe3a51-6117-413f-9d31-9f66517994c2) 26 | K9S module: 27 | - ![Screenshot 2](https://github.com/h4ckm1n-dev/kube-utils-nvim/assets/97511408/c6139ddf-e9af-4665-bd57-a829b236bac2) 28 | K9S in a vertical split view : 29 | - ![Screenshot 3](https://github.com/h4ckm1n-dev/kube-utils-nvim/assets/97511408/8c3cbaf8-d3c0-44a8-b487-4858e06b86f7) 30 | Get CRD function : 31 | - ![Screenshot 4](https://github.com/h4ckm1n-dev/kube-utils-nvim/assets/97511408/b5c1158e-5c93-41aa-b9ee-6fa5e2d0cb2b) 32 | Get Log and FormatJson : 33 | - ![image](https://github.com/h4ckm1n-dev/kube-utils-nvim/assets/97511408/52c7ecc8-9bb7-4dc0-a0cb-8e886d4ce645) 34 | 35 | ![Capture Video Preview](https://github.com/h4ckm1n-dev/kube-utils-nvim/assets/97511408/d575048c-2f88-415a-a62f-90db935d6951) 36 | 37 | ## Installation 38 | 39 | Install the plugin using your preferred Neovim package manager. Example for `lazy.nvim`: 40 | 41 | ```lua 42 | -- ~/.config/nvim/lua/plugins/kube-utils.lua 43 | return { 44 | { 45 | "h4ckm1n-dev/kube-utils-nvim", 46 | dependencies = { "nvim-telescope/telescope.nvim" }, 47 | lazy = true, 48 | event = "VeryLazy" 49 | }, 50 | } 51 | 52 | ``` 53 | 54 | ## Key Bindings 55 | 56 | Use the following mappings to access Kubernetes features efficiently: 57 | 58 | ```lua 59 | -- ~/.config/nvim/lua/config/keymaps.lua 60 | local kube_utils_mappings = { 61 | { "k", group = "Kubernetes" }, -- Main title for all Kubernetes related commands 62 | -- Helm Commands 63 | { "kh", group = "Helm" }, 64 | { "khT", "HelmDryRun", desc = "Helm DryRun Buffer" }, 65 | { "khb", "HelmDependencyBuildFromBuffer", desc = "Helm Dependency Build" }, 66 | { "khd", "HelmDeployFromBuffer", desc = "Helm Deploy Buffer to Context" }, 67 | { "khr", "RemoveDeployment", desc = "Helm Remove Deployment From Buffer" }, 68 | { "kht", "HelmTemplateFromBuffer", desc = "Helm Template From Buffer" }, 69 | { "khu", "HelmDependencyUpdateFromBuffer", desc = "Helm Dependency Update" }, 70 | -- Kubectl Commands 71 | { "kk", group = "Kubectl" }, 72 | { "kkC", "SelectSplitCRD", desc = "Download CRD Split" }, 73 | { "kkD", "DeleteNamespace", desc = "Kubectl Delete Namespace" }, 74 | { "kkK", "OpenK9s", desc = "Open K9s" }, 75 | { "kka", "KubectlApplyFromBuffer", desc = "Kubectl Apply From Buffer" }, 76 | { "kkc", "SelectCRD", desc = "Download CRD" }, 77 | { "kkk", "OpenK9sSplit", desc = "Split View K9s" }, 78 | { "kkl", "ToggleYamlHelm", desc = "Toggle YAML/Helm" }, 79 | -- Logs Commands 80 | { "kl", group = "Logs" }, 81 | { "klf", "JsonFormatLogs", desc = "Format JSON" }, 82 | { "klv", "ViewPodLogs", desc = "View Pod Logs" }, 83 | } 84 | -- Register the Kube Utils keybindings 85 | require('which-key').add(kube_utils_mappings) 86 | ``` 87 | 88 | ## Requirements 89 | 90 | - Neovim 0.9.0 or higher 91 | - Helm 92 | - kubectl 93 | - k9s 94 | 95 | ## Configuration 96 | 97 | No additional configuration is required. Simply install the plugin and start using the commands. 98 | 99 | ## License 100 | 101 | This plugin is licensed under the MIT License. See the LICENSE file for details., also feel free to submit a PR 102 | 103 | ## Contributors ✨ 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 |
h4ckm1n
h4ckm1n

💻
ohdearaugustin
ohdearaugustin

💻
116 | 117 | 118 | 119 | 120 | 121 | 122 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 123 | -------------------------------------------------------------------------------- /lua/kube-utils-nvim/command.lua: -------------------------------------------------------------------------------- 1 | -- kube-utils-nvim/command.lua 2 | 3 | local Utils = require("kube-utils-nvim.utils") 4 | 5 | local Command = {} 6 | 7 | Command.run_shell_command = function(cmd) 8 | -- Attempt to open a pipe to run the command and capture both stdout and stderr 9 | local handle, err = io.popen(cmd .. " 2>&1", "r") 10 | if not handle then 11 | -- Log the error and return nil along with the error message 12 | Utils.log_error("Failed to run command: " .. cmd .. "\n" .. tostring(err)) 13 | return nil, "Error running command: " .. tostring(err) 14 | end 15 | 16 | -- Read the output of the command 17 | local output = handle:read("*a") 18 | -- Always ensure the handle is closed to avoid resource leaks 19 | local success, close_err = handle:close() 20 | if not success then 21 | -- Log the error and return nil along with the error message 22 | Utils.log_error("Failed to close command handle for: " .. cmd .. "\n" .. tostring(close_err)) 23 | return nil, "Error closing command handle: " .. tostring(close_err) 24 | end 25 | 26 | -- Check if the output is nil or empty 27 | if not output or output == "" then 28 | return nil, "Command returned no output" 29 | end 30 | 31 | -- Return the output normally 32 | return output, nil 33 | end 34 | 35 | return Command 36 | -------------------------------------------------------------------------------- /lua/kube-utils-nvim/formatjson.lua: -------------------------------------------------------------------------------- 1 | -- kube-utils-nvim/formatjson.lua 2 | 3 | local M = {} 4 | -- TODO: Add more patterns for modules 5 | local module_patterns = { 6 | "%w+%.py%[%a+%]", -- Python modules with log level in square brackets 7 | "%a[%a%d._/-]+%[%d+%]", -- Generic module with numbers (e.g., CRON jobs, systemd, etc.) 8 | "pam_unix%([^:]+%):", -- pam_unix specific pattern 9 | "[%a_]+%.[%a_]+%[%a+%]", -- General modules with log level in square brackets 10 | "%S+/%S+", -- Generic paths and modules with various separators 11 | "[%w_.-]+:[%w_.-]+:[%w_.-]+", -- Modules with multiple components separated by colons 12 | "[%a_][%a%d._-]*%[%a+%]", -- More general module patterns with log level 13 | "%w+%.%w+%[%w+%]", -- PostgreSQL logs: include database and function names 14 | "[%w_/]+%.go:%d+", -- Go modules with file paths 15 | "[%w$.]+%.java:%d+", -- Java logs with package and class names 16 | "[%w_/%.]+%.%a+:%d+", -- Generic modules with file extensions and line numbers 17 | } 18 | 19 | local timestamp_patterns = { 20 | "%d%d%d%d%-%d%d%-%d%d[T ]%d%d:%d%d:%d%d[%.%d]*[+-]%d%d:?%d%d", -- ISO 8601 with milliseconds and timezone 21 | "%d%d%d%d%-%d%d%-%d%d[T ]%d%d:%d%d:%d%d[%.%d]*Z?", -- ISO 8601 with optional milliseconds and 'T' 22 | "%d%d%d%d%-%d%d%-%d%d[T ]%d%d:%d%d:%d%d[+-]%d%d:?%d%d", -- ISO 8601 with time zone 23 | "%d%d%d%d%-%d%d%-%d%d %d%d:%d%d:%d%d,%d%d%d", -- YYYY-MM-DD HH:MM:SS,SSS 24 | "%d%d%d%d%-%d%d%-%d%d %d%d:%d%d:%d%d", -- YYYY-MM-DD HH:MM:SS 25 | "%d+%.%d+", -- Unix timestamp with milliseconds 26 | "%d+", -- Unix timestamp (seconds since Unix epoch) 27 | "%d%d/%d%d/%d%d%d%d %d%d:%d%d:%d%d", -- MM/DD/YYYY HH:MM:SS 28 | "%d%d%-%d%d%-%d%d%d%d %d%d:%d%d:%d%d", -- MM-DD-YYYY HH:MM:SS 29 | "%a%a%a %d%d %d%d:%d%d:%d%d %d%d%d%d", -- RFC 2822 with day of the week 30 | "%a%a%a %a%a%a %d%d %d%d:%d%d:%d%d %d%d%d%d", -- Full RFC 2822 31 | "%d%d%a%a%a%d%d%d%d %d%d:%d%d:%d%d", -- Custom Kubernetes format 32 | } 33 | 34 | local function parseTimestamp(line) 35 | for _, pattern in ipairs(timestamp_patterns) do 36 | local timestamp = line:match(pattern) 37 | if timestamp then 38 | return timestamp 39 | end 40 | end 41 | return "unknown" 42 | end 43 | 44 | local function parseLogLevel(line) 45 | local log_level_patterns = { 46 | { level = "CRITICAL", pattern = "%[%s*critical%s*%]" }, 47 | { level = "ERROR", pattern = "%[%s*error%s*%]" }, 48 | { level = "WARNING", pattern = "%[%s*warning%s*%]" }, 49 | { level = "INFO", pattern = "%[%s*info%s*%]" }, 50 | { level = "DEBUG", pattern = "%[%s*debug%s*%]" }, 51 | { level = "LEVEL", pattern = "level%((%-?%d+)%)" }, 52 | } 53 | local lower_line = line:lower() 54 | for _, log_level in ipairs(log_level_patterns) do 55 | if lower_line:find(log_level.pattern) then 56 | if log_level.level == "LEVEL" then 57 | local number = lower_line:match("level%((%-?%d+)%)") 58 | return "LEVEL(" .. number .. ")" 59 | else 60 | return log_level.level 61 | end 62 | end 63 | end 64 | return "INFO" 65 | end 66 | 67 | -- Function to parse log metadata, ensuring modules are not mistaken for timestamps 68 | local function parseLogMetadata(line) 69 | local log_level = parseLogLevel(line) 70 | local module = "unknown" 71 | local json_start = line:find("{") 72 | local structured_data = nil 73 | local message = line 74 | local plain_text_message = line 75 | 76 | -- Parse structured JSON data if present 77 | if json_start then 78 | local json_possible = line:sub(json_start) 79 | local status, json_data = pcall(vim.fn.json_decode, json_possible) 80 | if status then 81 | structured_data = json_data 82 | message = "Structured JSON data present" 83 | plain_text_message = line:sub(1, json_start - 1) 84 | end 85 | end 86 | 87 | -- Extract the timestamp first 88 | local timestamp = parseTimestamp(line) 89 | 90 | -- Remove the timestamp from the line to prevent it from being matched as a module 91 | if timestamp ~= "unknown" then 92 | line = line:gsub(timestamp, "") 93 | end 94 | 95 | -- Match module patterns, ensuring no common timestamp formats are matched 96 | local parsed = false 97 | for _, pattern in ipairs(module_patterns) do 98 | local matched_module = line:match(pattern) 99 | if matched_module and not matched_module:match("%d%d:%d%d:%d%d") then -- Avoid matching timestamps 100 | module = matched_module 101 | parsed = true 102 | break 103 | end 104 | end 105 | 106 | -- If nothing could be parsed, keep the full message 107 | if not parsed then 108 | message = line 109 | end 110 | 111 | return log_level, module, message, structured_data, plain_text_message 112 | end 113 | 114 | local function FormatJsonLogs() 115 | local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) 116 | local formatted_lines = { "[" } 117 | for i, line in ipairs(lines) do 118 | local timestamp = parseTimestamp(line) or "unknown" 119 | local log_level, module, message, structured_data, plain_text_message = parseLogMetadata(line) 120 | local log_entry = { 121 | timestamp = timestamp, 122 | log_level = log_level, 123 | module = module or "unknown", 124 | message = message, 125 | plain_text_message = plain_text_message, 126 | } 127 | if structured_data then 128 | log_entry.structured_data = structured_data 129 | end 130 | -- Remove plain_text_message if it is the same as message 131 | if log_entry.message == log_entry.plain_text_message then 132 | log_entry.plain_text_message = nil 133 | end 134 | local json_text = vim.fn.json_encode(log_entry) 135 | table.insert(formatted_lines, "\t" .. json_text .. (i < #lines and "," or "")) 136 | end 137 | table.insert(formatted_lines, "]") 138 | vim.api.nvim_buf_set_lines(0, 0, -1, false, formatted_lines) 139 | end 140 | 141 | M.format = FormatJsonLogs 142 | 143 | return M 144 | -------------------------------------------------------------------------------- /lua/kube-utils-nvim/helm.lua: -------------------------------------------------------------------------------- 1 | -- kube-utils-nvim/helm.lua 2 | 3 | local Command = require("kube-utils-nvim.command") 4 | local Repository = require("kube-utils-nvim.repository") 5 | local TelescopePicker = require("kube-utils-nvim.telescope_picker") 6 | local Kubectl = require("kube-utils-nvim.kubectl") 7 | local Utils = require("kube-utils-nvim.utils") 8 | 9 | local Helm = {} 10 | 11 | local function fetch_releases(namespace) 12 | -- Construct the helm list command with the provided namespace 13 | local releases_cmd = string.format("helm list -n %s -q", namespace) 14 | local releases, err = Command.run_shell_command(releases_cmd) 15 | 16 | -- Check if the command was successful 17 | if not releases or releases == "" then 18 | Utils.log_error(err or "Failed to fetch Helm releases.") 19 | return nil 20 | end 21 | 22 | -- Split the releases into a list 23 | local release_list = vim.split(releases, "\n", { trimempty = true }) 24 | 25 | -- Check if the list is empty 26 | if #release_list == 0 then 27 | Utils.log_error("No Helm releases available.") 28 | return nil 29 | end 30 | 31 | return release_list 32 | end 33 | 34 | Helm.dependency_update_from_buffer = function() 35 | local file_path = vim.api.nvim_buf_get_name(0) 36 | if file_path == "" then 37 | Utils.log_error("No file selected") 38 | return 39 | end 40 | 41 | local chart_directory = file_path:match("(.*/)") 42 | if not chart_directory then 43 | Utils.log_error("Failed to determine chart directory from file path") 44 | return 45 | end 46 | 47 | local helm_cmd = string.format("helm dependency update %s", chart_directory) 48 | 49 | -- Extract repository information from Chart.yaml 50 | local chart_yaml_path = chart_directory .. "Chart.yaml" 51 | local repo_name, repo_url = Repository.get_repository_info(chart_yaml_path) 52 | 53 | if repo_name and repo_url then 54 | -- Check if the repository is missing and add it 55 | local repo_check_cmd = 56 | string.format("helm repo list | grep -q %s || helm repo add %s %s", repo_name, repo_name, repo_url) 57 | local _, repo_check_err = Command.run_shell_command(repo_check_cmd) 58 | 59 | if repo_check_err then 60 | print("Adding missing repository: " .. repo_check_err) 61 | end 62 | else 63 | Utils.log_error("Repository information is missing in Chart.yaml") 64 | end 65 | 66 | -- Execute the dependency update command 67 | local result, err = Command.run_shell_command(helm_cmd) 68 | if result then 69 | print("Helm dependency update successful: \n" .. result) 70 | else 71 | Utils.log_error("Helm dependency update failed: " .. (err or "Unknown error")) 72 | end 73 | end 74 | 75 | Helm.dependency_build_from_buffer = function() 76 | local file_path = vim.api.nvim_buf_get_name(0) 77 | if file_path == "" then 78 | Utils.log_error("No file selected") 79 | return 80 | end 81 | 82 | local chart_directory = file_path:match("(.*/)") 83 | if not chart_directory then 84 | Utils.log_error("Failed to determine chart directory from file path") 85 | return 86 | end 87 | 88 | local helm_cmd = string.format("helm dependency build %s", chart_directory) 89 | local result, err = Command.run_shell_command(helm_cmd) 90 | if result then 91 | print("Helm dependency build successful: \n" .. result) 92 | else 93 | Utils.log_error("Helm dependency build failed: " .. (err or "Unknown error")) 94 | end 95 | end 96 | 97 | local function generate_helm_template(chart_directory) 98 | -- Change the current working directory to the chart directory 99 | -- TODO: Fix the issue with the undefined-field diagnostic 100 | ---@diagnostic disable-next-line: undefined-field 101 | local original_directory = vim.loop.cwd() or "" 102 | 103 | -- Handle the case where original_directory is nil 104 | if original_directory == "" then 105 | Utils.log_error("Failed to get the current working directory") 106 | return "Error: Failed to get the current working directory" 107 | end 108 | 109 | -- TODO: Fix the issue with the undefined-field diagnostic 110 | ---@diagnostic disable-next-line: undefined-field 111 | vim.loop.chdir(chart_directory) 112 | 113 | local helm_cmd = "helm template ." 114 | local result, err = Command.run_shell_command(helm_cmd) 115 | 116 | -- Change back to the original directory 117 | -- TODO: Fix the issue with the undefined-field diagnostic 118 | ---@diagnostic disable-next-line: undefined-field 119 | vim.loop.chdir(original_directory) 120 | 121 | if result and result ~= "" then 122 | return result 123 | else 124 | local error_message = "Helm template generation failed: " .. (err or "Unknown error") 125 | Utils.log_error(error_message) 126 | return error_message 127 | end 128 | end 129 | 130 | Helm.template_from_buffer = function() 131 | local file_path = vim.api.nvim_buf_get_name(0) 132 | if file_path == "" then 133 | Utils.log_error("No file selected") 134 | return 135 | end 136 | 137 | local chart_directory = file_path:match("(.*/)") 138 | if not chart_directory then 139 | Utils.log_error("Failed to determine chart directory from file path") 140 | return 141 | end 142 | 143 | local template = generate_helm_template(chart_directory) 144 | if template then 145 | -- Open a new tab and create a buffer 146 | vim.cmd("tabnew") 147 | local bufnr = vim.api.nvim_create_buf(false, true) 148 | vim.bo[bufnr].filetype = "yaml" 149 | vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, vim.split(template, "\n")) 150 | -- Switch to the new buffer 151 | vim.api.nvim_set_current_buf(bufnr) 152 | end 153 | end 154 | 155 | Helm.deploy_from_buffer = function() 156 | Kubectl.select_context(function() 157 | Kubectl.select_namespace(function(namespace) 158 | local file_path = vim.api.nvim_buf_get_name(0) 159 | if file_path == "" then 160 | Utils.log_error("No file selected") 161 | return 162 | end 163 | local chart_directory = file_path:match("(.*/)") 164 | if not chart_directory then 165 | Utils.log_error("Failed to determine chart directory from file path") 166 | return 167 | end 168 | 169 | TelescopePicker.input("Enter Release Name", function(chart_name) 170 | local helm_cmd = string.format( 171 | "helm upgrade --install %s %s --values %s -n %s --create-namespace", 172 | chart_name, 173 | chart_directory, 174 | file_path, 175 | namespace 176 | ) 177 | local result, helm_err = Command.run_shell_command(helm_cmd) 178 | if result and result ~= "" then 179 | print("Deployment successful: \n" .. result) 180 | else 181 | Utils.log_error("Deployment failed: " .. (helm_err or "Unknown error")) 182 | end 183 | end) 184 | end) 185 | end) 186 | end 187 | 188 | Helm.dryrun_from_buffer = function() 189 | Kubectl.select_context(function() 190 | Kubectl.select_namespace(function(namespace) 191 | local file_path = vim.api.nvim_buf_get_name(0) 192 | if file_path == "" then 193 | Utils.log_error("No file selected") 194 | return 195 | end 196 | local chart_directory = file_path:match("(.*/)") 197 | if not chart_directory then 198 | Utils.log_error("Failed to determine chart directory from file path") 199 | return 200 | end 201 | 202 | TelescopePicker.input("Enter Release Name", function(chart_name) 203 | local helm_cmd = string.format( 204 | "helm upgrade --install --dry-run %s %s --values %s -n %s", 205 | chart_name, 206 | chart_directory, 207 | file_path, 208 | namespace 209 | ) 210 | local result, ns_err = Command.run_shell_command(helm_cmd) 211 | 212 | -- Check if the result is nil or empty 213 | if not result or result == "" then 214 | Utils.log_error("Dry run failed: " .. (ns_err or "Unknown error")) 215 | return 216 | end 217 | 218 | -- Extract only the console log output 219 | local console_output = {} 220 | for _, line in ipairs(vim.split(result, "\n")) do 221 | if line:match("^(Error:|WARNING:|INFO:)") then 222 | table.insert(console_output, line) 223 | end 224 | end 225 | 226 | -- Open a new tab and create a buffer 227 | vim.cmd("tabnew") 228 | local bufnr = vim.api.nvim_create_buf(false, true) 229 | vim.bo[bufnr].filetype = "yaml" 230 | 231 | -- Prepare the lines to set in the buffer 232 | local buffer_lines = vim.split(result, "\n") 233 | 234 | -- Add console output as comments at the end of the buffer lines 235 | if #console_output > 0 then 236 | table.insert(buffer_lines, "") 237 | table.insert(buffer_lines, "# Console output:") 238 | for _, line in ipairs(console_output) do 239 | table.insert(buffer_lines, "# " .. line) 240 | end 241 | end 242 | 243 | vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, buffer_lines) 244 | 245 | -- Switch to the new buffer 246 | vim.api.nvim_set_current_buf(bufnr) 247 | end) 248 | end) 249 | end) 250 | end 251 | 252 | Helm.remove_deployment = function() 253 | Kubectl.select_context(function() 254 | Kubectl.select_namespace(function(namespace) 255 | local release_list = fetch_releases(namespace) 256 | if not release_list then 257 | return 258 | end 259 | TelescopePicker.select_from_list("Select Release to Remove", release_list, function(release_name) 260 | local helm_cmd = string.format("helm uninstall %s -n %s", release_name, namespace) 261 | local result, helm_err = Command.run_shell_command(helm_cmd) 262 | if result and result ~= "" then 263 | print("Deployment removal successful: \n" .. result) 264 | else 265 | Utils.log_error("Deployment removal failed: " .. (helm_err or "Unknown error")) 266 | end 267 | end) 268 | end) 269 | end) 270 | end 271 | 272 | return Helm 273 | -------------------------------------------------------------------------------- /lua/kube-utils-nvim/init.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local Helm = require("kube-utils-nvim.helm") 4 | local Kubectl = require("kube-utils-nvim.kubectl") 5 | local K9s = require("kube-utils-nvim.k9s") 6 | local toggle_lsp = require("kube-utils-nvim.toggle_lsp") 7 | local JsonLogFormatter = require("kube-utils-nvim.formatjson") 8 | 9 | local default_opts = { 10 | toggle_lsp = { 11 | schemas = { 12 | -- ArgoCD ApplicationSet CRD 13 | ["https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/crds/applicationset-crd.yaml"] = "", 14 | -- ArgoCD Application CRD 15 | ["https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/crds/application-crd.yaml"] = "", 16 | -- Kubernetes strict schemas 17 | ["https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.29.3-standalone-strict/all.json"] = "", 18 | }, 19 | }, 20 | } 21 | 22 | M.setup_commands = function() 23 | -- Define a command to call the Helm.template_from_buffer function 24 | vim.api.nvim_create_user_command("HelmTemplateFromBuffer", function() 25 | Helm.template_from_buffer() 26 | end, {}) 27 | vim.api.nvim_create_user_command("HelmDeployFromBuffer", function() 28 | Helm.deploy_from_buffer() 29 | end, {}) 30 | vim.api.nvim_create_user_command("RemoveDeployment", function() 31 | Helm.remove_deployment() 32 | end, {}) 33 | vim.api.nvim_create_user_command("HelmDryRun", function() 34 | Helm.dryrun_from_buffer() 35 | end, {}) 36 | vim.api.nvim_create_user_command("KubectlApplyFromBuffer", function() 37 | Kubectl.apply_from_buffer() 38 | end, {}) 39 | vim.api.nvim_create_user_command("DeleteNamespace", function() 40 | Kubectl.delete_namespace() 41 | end, {}) 42 | vim.api.nvim_create_user_command("HelmDependencyUpdateFromBuffer", function() 43 | Helm.dependency_update_from_buffer() 44 | end, {}) 45 | vim.api.nvim_create_user_command("HelmDependencyBuildFromBuffer", function() 46 | Helm.dependency_build_from_buffer() 47 | end, {}) 48 | vim.api.nvim_create_user_command("OpenK9s", function() 49 | K9s.open() 50 | end, {}) 51 | vim.api.nvim_create_user_command("OpenK9sSplit", function() 52 | K9s.open_split() 53 | end, {}) 54 | vim.api.nvim_create_user_command("StopYamlls", function() 55 | toggle_lsp.stop_yamlls() 56 | end, {}) 57 | vim.api.nvim_create_user_command("StartYamlls", function() 58 | toggle_lsp.start_yamlls() 59 | end, {}) 60 | vim.api.nvim_create_user_command("ToggleYamlHelm", function() 61 | toggle_lsp.toggle_yaml_helm() 62 | end, {}) 63 | vim.api.nvim_create_user_command("SelectCRD", function() 64 | Kubectl.select_crd() 65 | end, {}) 66 | vim.api.nvim_create_user_command("SelectSplitCRD", function() 67 | Kubectl.select_crd_split() 68 | end, {}) 69 | vim.api.nvim_create_user_command("JsonFormatLogs", function() 70 | JsonLogFormatter.format() 71 | end, {}) 72 | vim.api.nvim_create_user_command("ViewPodLogs", function() 73 | Kubectl.view_pod_logs() 74 | end, {}) 75 | end 76 | 77 | M.setup = function(config) 78 | local opts = config and vim.tbl_deep_extend("force", default_opts, config) or default_opts 79 | 80 | require("kube-utils-nvim.toggle_lsp").setup(opts) 81 | M.setup_commands() 82 | end 83 | 84 | return M 85 | -------------------------------------------------------------------------------- /lua/kube-utils-nvim/k9s.lua: -------------------------------------------------------------------------------- 1 | -- kube-utils-nvim/k9s.lua 2 | 3 | local K9s = {} 4 | 5 | -- Helper function to set up terminal key mappings 6 | local function set_terminal_keymaps(bufnr) 7 | local opts = { noremap = true, silent = true } 8 | local keymaps = { 9 | { "t", "q", ":q", opts }, 10 | { "t", "c", ":q", opts }, 11 | { "t", "", "", opts }, 12 | } 13 | 14 | for _, keymap in ipairs(keymaps) do 15 | vim.api.nvim_buf_set_keymap(bufnr, keymap[1], keymap[2], keymap[3], keymap[4]) 16 | end 17 | end 18 | 19 | -- Open K9s in a floating window 20 | K9s.open = function() 21 | local k9s_cmd = "k9s" 22 | 23 | -- Calculate window dimensions and position 24 | local width = 0.8 25 | local height = 0.8 26 | local x = (1 - width) / 2 27 | local y = (1 - height) / 2 28 | local opts = { 29 | relative = "editor", 30 | width = math.floor(vim.o.columns * width), 31 | height = math.floor(vim.o.lines * height), 32 | col = math.floor(vim.o.columns * x), 33 | row = math.floor(vim.o.lines * y), 34 | style = "minimal", 35 | } 36 | 37 | -- Create a new terminal buffer and open it in a floating window 38 | local bufnr = vim.api.nvim_create_buf(false, true) 39 | if not bufnr then 40 | print("Failed to create buffer") 41 | return 42 | end 43 | 44 | vim.api.nvim_open_win(bufnr, true, opts) 45 | 46 | -- Run K9s in the newly created terminal buffer 47 | vim.fn.termopen(k9s_cmd) 48 | 49 | -- Set key mappings for terminal buffer 50 | set_terminal_keymaps(bufnr) 51 | end 52 | 53 | -- Open K9s in a vertical split 54 | K9s.open_split = function() 55 | vim.cmd("vnew | terminal k9s") 56 | local bufnr = vim.api.nvim_get_current_buf() 57 | set_terminal_keymaps(bufnr) 58 | end 59 | 60 | return K9s 61 | -------------------------------------------------------------------------------- /lua/kube-utils-nvim/kubectl.lua: -------------------------------------------------------------------------------- 1 | -- kube-utils-nvim/kubectl.lua 2 | 3 | local Utils = require("kube-utils-nvim.utils") 4 | local Command = require("kube-utils-nvim.command") 5 | local TelescopePicker = require("kube-utils-nvim.telescope_picker") 6 | 7 | local Kubectl = {} 8 | 9 | local function fetch_contexts() 10 | -- Run the shell command to get Kubernetes contexts 11 | local contexts, err = Command.run_shell_command("kubectl config get-contexts -o name") 12 | 13 | -- Check if the command was successful 14 | if not contexts or contexts == "" then 15 | Utils.log_error(err or "Failed to fetch Kubernetes contexts.") 16 | return nil 17 | end 18 | 19 | -- Split the contexts into a list 20 | local context_list = vim.split(contexts, "\n", { trimempty = true }) 21 | 22 | -- Check if the list is empty 23 | if #context_list == 0 then 24 | Utils.log_error("No Kubernetes contexts available.") 25 | return nil 26 | end 27 | 28 | return context_list 29 | end 30 | 31 | local function fetch_namespaces() 32 | -- Run the shell command to get namespaces 33 | local namespaces, err = Command.run_shell_command("kubectl get namespaces | awk 'NR>1 {print $1}'") 34 | 35 | -- Check if the command was successful 36 | if not namespaces or namespaces == "" then 37 | Utils.log_error("Failed to fetch namespaces: " .. (err or "No namespaces found.")) 38 | return nil 39 | end 40 | 41 | -- Split the namespaces into a list 42 | local namespace_list = vim.split(namespaces, "\n", { trimempty = true }) 43 | 44 | -- Check if the list is empty 45 | if #namespace_list == 0 then 46 | Utils.log_error("No namespaces available.") 47 | return nil 48 | end 49 | 50 | return namespace_list 51 | end 52 | 53 | local function fetch_crds() 54 | -- Run the shell command to get CRDs 55 | local crds, err = Command.run_shell_command("kubectl get crd -o name") 56 | 57 | -- Check if the command was successful 58 | if not crds or crds == "" then 59 | Utils.log_error(err or "Failed to fetch CRDs: Command returned no output.") 60 | return nil 61 | end 62 | 63 | -- Split the CRDs into a list 64 | local crd_list = vim.split(crds, "\n", { trimempty = true }) 65 | 66 | -- Check if the list is empty 67 | if #crd_list == 0 then 68 | Utils.log_error("No CRDs available.") 69 | return nil 70 | end 71 | 72 | return crd_list 73 | end 74 | 75 | local function fetch_crd_details(crd_name) 76 | -- Run the shell command to get CRD details 77 | local crd_details, err = Command.run_shell_command("kubectl get " .. crd_name .. " -o yaml") 78 | 79 | -- Check if the command was successful 80 | if not crd_details or crd_details == "" then 81 | Utils.log_error(err or "Failed to fetch CRD details: Command returned no output.") 82 | return nil 83 | end 84 | 85 | return crd_details 86 | end 87 | 88 | Kubectl.select_crd = function() 89 | -- Step 1: Select a context 90 | local context_list = fetch_contexts() 91 | if not context_list then 92 | return 93 | end 94 | TelescopePicker.select_from_list("Select Kubernetes Context", context_list, function(selected_context) 95 | Command.run_shell_command("kubectl config use-context " .. selected_context) 96 | -- Step 3: Select a CRD 97 | local crd_list = fetch_crds() 98 | if not crd_list then 99 | return 100 | end 101 | TelescopePicker.select_from_list("Select CRD", crd_list, function(selected_crd) 102 | -- Step 4: Fetch the selected CRD details 103 | local crd_details = fetch_crd_details(selected_crd) 104 | if not crd_details then 105 | return 106 | end 107 | 108 | -- Function to safely rename buffer 109 | local function safe_set_buf_name(buf, name) 110 | local ok, err = pcall(vim.api.nvim_buf_set_name, buf, name) 111 | if not ok then 112 | print("Failed to rename buffer: " .. err) 113 | end 114 | end 115 | 116 | -- Step 5: Open the CRD details in a new buffer with a vertical split and save it 117 | vim.api.nvim_command("new") 118 | local buf = vim.api.nvim_create_buf(false, true) 119 | vim.api.nvim_set_current_buf(buf) 120 | vim.api.nvim_buf_set_lines(buf, 0, -1, false, vim.split(crd_details, "\n")) 121 | safe_set_buf_name(buf, selected_crd .. ".yaml") 122 | vim.bo[buf].filetype = "yaml" 123 | end) 124 | end) 125 | end 126 | 127 | Kubectl.select_crd_split = function() 128 | -- Step 1: Select a context 129 | local context_list = fetch_contexts() 130 | if not context_list then 131 | return 132 | end 133 | TelescopePicker.select_from_list("Select Kubernetes Context", context_list, function(selected_context) 134 | Command.run_shell_command("kubectl config use-context " .. selected_context) 135 | -- Step 3: Select a CRD 136 | local crd_list = fetch_crds() 137 | if not crd_list then 138 | return 139 | end 140 | TelescopePicker.select_from_list("Select CRD", crd_list, function(selected_crd) 141 | -- Step 4: Fetch the selected CRD details 142 | local crd_details = fetch_crd_details(selected_crd) 143 | if not crd_details then 144 | return 145 | end 146 | 147 | -- Function to safely rename buffer 148 | local function safe_set_buf_name(buf, name) 149 | local ok, err = pcall(vim.api.nvim_buf_set_name, buf, name) 150 | if not ok then 151 | print("Failed to rename buffer: " .. err) 152 | end 153 | end 154 | 155 | -- Step 5: Open the CRD details in a new buffer with a vertical split and save it 156 | vim.api.nvim_command("vsplit") 157 | local buf = vim.api.nvim_create_buf(false, true) 158 | vim.api.nvim_set_current_buf(buf) 159 | vim.api.nvim_buf_set_lines(buf, 0, -1, false, vim.split(crd_details, "\n")) 160 | safe_set_buf_name(buf, selected_crd .. ".yaml") 161 | vim.bo[buf].filetype = "yaml" 162 | end) 163 | end) 164 | end 165 | 166 | Kubectl.select_context = function(callback) 167 | local context_list = fetch_contexts() 168 | if not context_list then 169 | return 170 | end 171 | TelescopePicker.select_from_list("Select Kubernetes Context", context_list, function(selected_context) 172 | Command.run_shell_command("kubectl config use-context " .. selected_context) 173 | callback(selected_context) 174 | end) 175 | end 176 | 177 | Kubectl.select_namespace = function(callback) 178 | local namespace_list = fetch_namespaces() 179 | if not namespace_list then 180 | return 181 | end 182 | table.insert(namespace_list, 1, "[Create New Namespace]") 183 | TelescopePicker.select_from_list("Select Namespace", namespace_list, function(selected_namespace) 184 | if selected_namespace == "[Create New Namespace]" then 185 | TelescopePicker.input("Enter Namespace Name", function(new_ns_name) 186 | local create_ns_cmd = string.format("kubectl create namespace %s", new_ns_name) 187 | local create_ns_result, create_ns_err = Command.run_shell_command(create_ns_cmd) 188 | if create_ns_result then 189 | print(string.format("Namespace %s created successfully.", new_ns_name)) 190 | callback(new_ns_name) 191 | else 192 | Utils.log_error("Failed to create namespace: " .. (create_ns_err or "Unknown error")) 193 | end 194 | end) 195 | else 196 | callback(selected_namespace) 197 | end 198 | end) 199 | end 200 | 201 | Kubectl.apply_from_buffer = function() 202 | local context_list = fetch_contexts() 203 | if not context_list then 204 | return 205 | end 206 | 207 | TelescopePicker.select_from_list("Select Kubernetes Context", context_list, function(selected_context) 208 | Command.run_shell_command("kubectl config use-context " .. selected_context) 209 | 210 | local namespace_list = fetch_namespaces() 211 | if not namespace_list then 212 | return 213 | end 214 | 215 | TelescopePicker.select_from_list("Select Namespace", namespace_list, function(selected_namespace) 216 | local file_path = vim.api.nvim_buf_get_name(0) 217 | if file_path == "" then 218 | Utils.log_error("No file selected") 219 | return 220 | end 221 | 222 | local result, apply_err = 223 | Command.run_shell_command("kubectl apply -f " .. file_path .. " -n " .. selected_namespace) 224 | if result and result ~= "" then 225 | print("kubectl apply successful: \n" .. result) 226 | else 227 | Utils.log_error("kubectl apply failed: " .. (apply_err or "Unknown error")) 228 | end 229 | end) 230 | end) 231 | end 232 | 233 | Kubectl.delete_namespace = function() 234 | local context_list = fetch_contexts() 235 | if not context_list then 236 | return 237 | end 238 | 239 | TelescopePicker.select_from_list("Select Kubernetes Context", context_list, function(selected_context) 240 | Command.run_shell_command("kubectl config use-context " .. selected_context) 241 | Kubectl.select_and_delete_namespace() 242 | end) 243 | end 244 | 245 | Kubectl.select_and_delete_namespace = function() 246 | local namespace_list = fetch_namespaces() 247 | if not namespace_list then 248 | return 249 | end 250 | 251 | TelescopePicker.select_from_list("Select Namespace to Delete", namespace_list, function(selected_namespace) 252 | local confirm_delete = vim.fn.input("Delete namespace " .. selected_namespace .. "? [y/N]: ") 253 | if confirm_delete == "y" or confirm_delete == "Y" then 254 | local delete_cmd = "kubectl delete namespace " .. selected_namespace 255 | local result, err = Command.run_shell_command(delete_cmd) 256 | if result then 257 | print("Namespace " .. selected_namespace .. " successfully deleted.") 258 | else 259 | Utils.log_error("Failed to delete namespace " .. selected_namespace .. ": " .. (err or "Unknown error")) 260 | end 261 | else 262 | print("Deletion cancelled.") 263 | end 264 | end) 265 | end 266 | 267 | local function fetch_pods(namespace) 268 | -- Run the shell command to get pods in the given namespace 269 | local pods, err = Command.run_shell_command("kubectl get pods -n " .. namespace .. " -o name") 270 | 271 | -- Check if the command was successful 272 | if not pods or pods == "" then 273 | Utils.log_error(err or "Failed to fetch pods: Command returned no output.") 274 | return nil 275 | end 276 | 277 | -- Split the pods into a list 278 | local pod_list = vim.split(pods, "\n", { trimempty = true }) 279 | 280 | -- Check if the list is empty 281 | if #pod_list == 0 then 282 | Utils.log_error("No pods available.") 283 | return nil 284 | end 285 | 286 | return pod_list 287 | end 288 | 289 | local function fetch_pod_logs(pod_name, namespace) 290 | -- Fetch current logs 291 | local current_logs, current_err = Command.run_shell_command("kubectl logs " .. pod_name .. " -n " .. namespace) 292 | if not current_logs or current_logs == "" then 293 | Utils.log_error(current_err or "Failed to fetch current logs: Command returned no output.") 294 | end 295 | 296 | -- Fetch previous logs if the pod has restarted 297 | local previous_logs, previous_err = 298 | Command.run_shell_command("kubectl logs " .. pod_name .. " -n " .. namespace .. " --previous") 299 | if not previous_logs or previous_logs == "" then 300 | if previous_err and not string.find(previous_err, "previous terminated container") then 301 | Utils.log_error(previous_err or "Failed to fetch previous logs: No previous containers.") 302 | end 303 | end 304 | 305 | -- Combine logs if both are available, else return what is available 306 | if current_logs and previous_logs then 307 | return previous_logs .. "\n-- Current Logs --\n" .. current_logs 308 | elseif current_logs then 309 | return current_logs 310 | elseif previous_logs then 311 | return previous_logs 312 | end 313 | 314 | return nil 315 | end 316 | 317 | Kubectl.view_pod_logs = function() 318 | -- Step 1: Select a context 319 | local context_list = fetch_contexts() 320 | if not context_list then 321 | return 322 | end 323 | TelescopePicker.select_from_list("Select Kubernetes Context", context_list, function(selected_context) 324 | Command.run_shell_command("kubectl config use-context " .. selected_context) 325 | -- Step 2: Select a namespace 326 | local namespace_list = fetch_namespaces() 327 | if not namespace_list then 328 | return 329 | end 330 | TelescopePicker.select_from_list("Select Namespace", namespace_list, function(selected_namespace) 331 | -- Step 3: Select a pod 332 | local pod_list = fetch_pods(selected_namespace) 333 | if not pod_list then 334 | return 335 | end 336 | TelescopePicker.select_from_list("Select Pod", pod_list, function(selected_pod) 337 | -- Step 4: Fetch the selected pod logs 338 | local logs = fetch_pod_logs(selected_pod, selected_namespace) 339 | if not logs then 340 | return 341 | end 342 | 343 | -- Function to safely rename buffer 344 | local function safe_set_buf_name(buf, name) 345 | local ok, err = pcall(vim.api.nvim_buf_set_name, buf, name) 346 | if not ok then 347 | Utils.log_error("Failed to rename buffer: " .. err) 348 | end 349 | end 350 | 351 | -- Step 5: Open the logs in a new buffer with a vertical split and save it 352 | vim.api.nvim_command("vsplit") 353 | local buf = vim.api.nvim_create_buf(false, true) 354 | vim.api.nvim_set_current_buf(buf) 355 | 356 | -- Ensure the buffer can be written to 357 | vim.bo[buf].buftype = "" 358 | 359 | -- Set the split to take 70% of the width 360 | local total_width = vim.o.columns 361 | local split_width = math.floor(total_width * 0.6) 362 | vim.api.nvim_win_set_width(0, split_width) 363 | 364 | vim.api.nvim_buf_set_lines(buf, 0, -1, false, vim.split(logs, "\n")) 365 | safe_set_buf_name(buf, selected_pod .. ".json") 366 | vim.bo[buf].filetype = "json" 367 | end) 368 | end) 369 | end) 370 | end 371 | 372 | return Kubectl 373 | -------------------------------------------------------------------------------- /lua/kube-utils-nvim/repository.lua: -------------------------------------------------------------------------------- 1 | -- kube-utils-nvim/repository.lua 2 | -- 3 | local Utils = require("kube-utils-nvim.utils") 4 | 5 | local Repository = {} 6 | 7 | -- Function to read and parse the Chart.yaml file 8 | Repository.get_repository_info = function(chart_yaml_path) 9 | local repo_name = "" 10 | local repo_url = "" 11 | 12 | -- Check if Chart.yaml exists 13 | if vim.fn.filereadable(chart_yaml_path) ~= 1 then 14 | Utils.log_error("Chart.yaml not found at " .. chart_yaml_path) 15 | return nil, nil 16 | end 17 | 18 | -- Read the contents of Chart.yaml 19 | local chart_yaml_contents = vim.fn.readfile(chart_yaml_path) 20 | if not chart_yaml_contents then 21 | Utils.log_error("Failed to read Chart.yaml") 22 | return nil, nil 23 | end 24 | 25 | -- Use a simple pattern matching approach to extract the required fields 26 | for _, line in ipairs(chart_yaml_contents) do 27 | local key, value = line:match("^%s*(%S+)%s*:%s*(.+)$") 28 | if key == "repository" then 29 | repo_url = value 30 | elseif key == "name" then 31 | repo_name = value 32 | end 33 | end 34 | 35 | if repo_name == "" then 36 | Utils.log_error("Repository name not found in Chart.yaml") 37 | end 38 | if repo_url == "" then 39 | Utils.log_error("Repository URL not found in Chart.yaml") 40 | end 41 | 42 | return repo_name, repo_url 43 | end 44 | 45 | return Repository 46 | -------------------------------------------------------------------------------- /lua/kube-utils-nvim/telescope_picker.lua: -------------------------------------------------------------------------------- 1 | -- kube-utils-nvim/telescope_picker.lua 2 | -- 3 | local Utils = require("kube-utils-nvim.utils") 4 | 5 | local TelescopePicker = {} 6 | 7 | local function invoke_callback(callback, value) 8 | if type(callback) == "function" then 9 | callback(value) 10 | else 11 | Utils.log_error("Callback is not a function.") 12 | end 13 | end 14 | 15 | local function check_telescope() 16 | if not pcall(require, "telescope") then 17 | error("Cannot find telescope!") 18 | end 19 | return true 20 | end 21 | 22 | function TelescopePicker.select_from_list(prompt_title, list, callback) 23 | local pickers, finders, config, actions, state 24 | if check_telescope() then 25 | pickers = require("telescope.pickers") 26 | finders = require("telescope.finders") 27 | config = require("telescope.config") 28 | actions = require("telescope.actions") 29 | state = require("telescope.actions.state") 30 | end 31 | 32 | if type(list) ~= "table" or #list == 0 then 33 | Utils.log_error("List must be a non-empty table.") 34 | return 35 | end 36 | 37 | if type(callback) ~= "function" then 38 | Utils.log_error("Callback must be a function.") 39 | return 40 | end 41 | 42 | pickers 43 | .new({}, { 44 | prompt_title = prompt_title, 45 | finder = finders.new_table({ results = list }), 46 | sorter = config.values.generic_sorter({}), 47 | attach_mappings = function(_, map) 48 | map("i", "", function(prompt_bufnr) 49 | local selection = state.get_selected_entry() 50 | actions.close(prompt_bufnr) 51 | if selection then 52 | invoke_callback(callback, selection.value) 53 | end 54 | end) 55 | return true 56 | end, 57 | }) 58 | :find() 59 | end 60 | 61 | TelescopePicker.input = function(prompt_title, callback) 62 | if type(callback) ~= "function" then 63 | Utils.log_error("Callback must be a function.") 64 | return 65 | end 66 | 67 | local input = vim.fn.input(prompt_title .. ": ") 68 | if input ~= "" then 69 | invoke_callback(callback, input) 70 | else 71 | Utils.log_error("" .. prompt_title .. " cannot be empty.") 72 | end 73 | end 74 | 75 | return TelescopePicker 76 | -------------------------------------------------------------------------------- /lua/kube-utils-nvim/toggle_lsp.lua: -------------------------------------------------------------------------------- 1 | -- module/toggle_lsp.lua 2 | local M = {} 3 | 4 | local lspconfig = require("lspconfig") 5 | local lsp = vim.lsp 6 | local config 7 | 8 | M.setup = function(opts) 9 | config = opts 10 | end 11 | 12 | -- Function to stop yamlls client 13 | M.stop_yamlls = function() 14 | for _, client in pairs(lsp.get_clients()) do 15 | if client.name == "yamlls" then 16 | client.stop() 17 | end 18 | end 19 | end 20 | 21 | -- Function to stop helm_ls client 22 | M.stop_helm_ls = function() 23 | for _, client in pairs(lsp.get_clients()) do 24 | if client.name == "helm_ls" then 25 | client.stop() 26 | end 27 | end 28 | end 29 | 30 | -- Function to start yamlls client 31 | M.start_yamlls = function() 32 | M.stop_helm_ls() -- Ensure helm_ls is stopped before starting yamlls 33 | 34 | local yamlls_config = { 35 | on_attach = function() 36 | -- Add any custom on_attach behavior here if needed 37 | end, 38 | settings = { 39 | yaml = { 40 | schemaStore = { 41 | enable = true, 42 | url = "https://www.schemastore.org/api/json/catalog.json", 43 | }, 44 | schemas = config.toggle_lsp.schemas, 45 | validate = true, 46 | completion = true, 47 | hover = true, 48 | format = { 49 | enable = true, 50 | bracketSpacing = true, 51 | printWidth = 80, 52 | proseWrap = "preserve", 53 | singleQuote = true, 54 | }, 55 | customTags = { 56 | "!Ref", 57 | "!Sub sequence", 58 | "!Sub mapping", 59 | "!GetAtt", 60 | }, 61 | disableAdditionalProperties = false, 62 | maxItemsComputed = 5000, 63 | trace = { 64 | server = "verbose", 65 | }, 66 | }, 67 | redhat = { 68 | telemetry = { 69 | enabled = false, 70 | }, 71 | }, 72 | }, 73 | } 74 | 75 | lspconfig.yamlls.setup(yamlls_config) 76 | lsp.buf.add_workspace_folder(vim.fn.getcwd()) 77 | 78 | -- Attach the new yamlls client to the current buffer 79 | local client_id = lsp.start_client(yamlls_config) 80 | if client_id then 81 | lsp.buf_attach_client(0, client_id) 82 | end 83 | end 84 | 85 | -- Function to start helm_ls client 86 | M.start_helm_ls = function() 87 | M.stop_yamlls() -- Ensure yamlls is stopped before starting helm_ls 88 | 89 | local helm_ls_config = { 90 | cmd = { "helm_ls" }, 91 | filetypes = { "helm" }, 92 | on_attach = function() 93 | -- Add any custom on_attach behavior here if needed 94 | end, 95 | } 96 | 97 | lspconfig.helm_ls.setup(helm_ls_config) 98 | lsp.buf.add_workspace_folder(vim.fn.getcwd()) 99 | 100 | -- Attach the new helm_ls client to the current buffer 101 | local client_id = lsp.start_client(helm_ls_config) 102 | if client_id then 103 | lsp.buf_attach_client(0, client_id) 104 | end 105 | end 106 | 107 | -- Function to toggle between yaml and helm filetypes 108 | M.toggle_yaml_helm = function() 109 | if vim.bo.filetype == "yaml" then 110 | vim.bo.filetype = "helm" 111 | M.stop_yamlls() 112 | M.start_helm_ls() 113 | elseif vim.bo.filetype == "helm" then 114 | vim.bo.filetype = "yaml" 115 | M.stop_helm_ls() 116 | M.start_yamlls() 117 | end 118 | end 119 | 120 | return M 121 | -------------------------------------------------------------------------------- /lua/kube-utils-nvim/utils.lua: -------------------------------------------------------------------------------- 1 | -- kube-utils-nvim/utils.lua 2 | 3 | local Utils = {} 4 | 5 | -- Function log_error 6 | Utils.log_error = function(message) 7 | vim.notify("Error: " .. message, vim.log.levels.ERROR) 8 | end 9 | 10 | return Utils 11 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ], 6 | "lockFileMaintenance": { 7 | "enabled": true, 8 | "automerge": true 9 | }, 10 | "packageRules": [ 11 | { 12 | "groupName": "all patch dependencies", 13 | "groupSlug": "all-patch", 14 | "matchPackagePatterns": [ 15 | "*" 16 | ], 17 | "matchUpdateTypes": [ 18 | "patch" 19 | ], 20 | "automerge": true, 21 | "automergeSchedule": [] 22 | }, 23 | { 24 | "groupName": "all minor dependencies", 25 | "groupSlug": "all-minors", 26 | "matchPackagePatterns": [ 27 | "*" 28 | ], 29 | "matchUpdateTypes": [ 30 | "minor" 31 | ], 32 | "automerge": true, 33 | "automergeSchedule": [] 34 | } 35 | ] 36 | } 37 | --------------------------------------------------------------------------------