├── .github ├── img │ ├── kubesql-162.png │ ├── kubesql-248.png │ ├── kubesql-500.png │ └── kubesql.svg └── workflows │ ├── pull_request.yml │ └── release.yml ├── pkg ├── eval │ ├── factory.go │ ├── string_test.go │ ├── string.go │ ├── eval.go │ └── eval_test.go ├── printers │ ├── orderby.go │ ├── name.go │ ├── json.go │ ├── yaml.go │ └── table.go ├── cmd │ ├── sql-version.go │ ├── sql-printer.go │ ├── config.go │ ├── constants.go │ ├── sql.go │ └── sql-sql.go ├── filter │ ├── filter.go │ └── filter_test.go └── client │ └── client.go ├── README_config.md ├── .gitignore ├── cmd └── kubectl-sql │ └── main.go ├── go.mod ├── .krew.yaml ├── kubectl-sql.spec ├── CONTRIBUTING.md ├── README.md ├── Makefile ├── README_language.md ├── CODE_OF_CONDUCT.md ├── README_examples.md ├── LICENSE └── go.sum /.github/img/kubesql-162.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yaacov/kubectl-sql/HEAD/.github/img/kubesql-162.png -------------------------------------------------------------------------------- /.github/img/kubesql-248.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yaacov/kubectl-sql/HEAD/.github/img/kubesql-248.png -------------------------------------------------------------------------------- /.github/img/kubesql-500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yaacov/kubectl-sql/HEAD/.github/img/kubesql-500.png -------------------------------------------------------------------------------- /pkg/eval/factory.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "github.com/yaacov/tree-search-language/v6/pkg/walkers/semantics" 5 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 6 | ) 7 | 8 | // EvalFunctionFactory build an evaluation method for one item that returns a value using a key. 9 | func EvalFunctionFactory(item unstructured.Unstructured) semantics.EvalFunc { 10 | return func(key string) (interface{}, bool) { 11 | return ExtractValue(item, key) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request 2 | 3 | on: 4 | pull_request: 5 | branches: [ main ] 6 | 7 | jobs: 8 | test: 9 | name: Test 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | 14 | - name: Set up Go 15 | uses: actions/setup-go@v4 16 | with: 17 | go-version: '1.23' 18 | 19 | - name: Install tools 20 | run: make install-tools 21 | 22 | - name: Run tests 23 | run: make test 24 | 25 | - name: Run lint 26 | run: make lint 27 | 28 | - name: Build 29 | run: make 30 | -------------------------------------------------------------------------------- /README_config.md: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
6 |
7 |
20 | 21 | ## More docs 22 | 23 | - [kubectl-sql's query language](https://github.com/yaacov/kubectl-sql/blob/master/README_language.md) 24 | - [More kubectl-sql examples](https://github.com/yaacov/kubectl-sql/blob/master/README_examples.md) 25 | - [Using the config file](https://github.com/yaacov/kubectl-sql/blob/master/README_config.md) 26 | 27 | ## Install 28 | 29 | Using [krew](https://sigs.k8s.io/krew) plugin manager to install: 30 | 31 | ``` bash 32 | # Available for linux-amd64 33 | kubectl krew install sql 34 | kubectl sql --help 35 | ``` 36 | 37 | Using Fedora Copr: 38 | 39 | ``` bash 40 | # Available for F41 and F42 (linux-amd64) 41 | dnf copr enable yaacov/kubesql 42 | dnf install kubectl-sql 43 | ``` 44 | 45 | From source: 46 | 47 | ``` bash 48 | # Clone code 49 | git clone git@github.com:yaacov/kubectl-sql.git 50 | cd kubectl-sql 51 | 52 | # Build kubectl-sql 53 | make 54 | 55 | # Install into local machine PATH 56 | sudo install ./kubectl-sql /usr/local/bin/ 57 | ``` 58 | 59 |
62 | 63 | ## What can I do with it ? 64 | 65 | kubectl-sql let you select Kubernetes resources based on the value of one or more resource fields, using 66 | human readable easy to use SQL like query language. 67 | 68 | [More kubectl-sql examples](https://github.com/yaacov/kubectl-sql/blob/master/README_examples.md) 69 | 70 | ``` bash 71 | # Get pods in namespace "openshift-multus" that hase name containing "cni" 72 | kubectl-sql "select name, status.phase as phase, status.podIP as ip \ 73 | from openshift-multus/pods \ 74 | where name ~= 'cni' and (ip ~= '5$' or phase = 'Running')" 75 | KIND: Pod COUNT: 2 76 | name phase ip 77 | multus-additional-cni-plugins-7kcsd Running 10.130.10.85 78 | multus-additional-cni-plugins-kc8sz Running 10.131.6.65 79 | ... 80 | ``` 81 | 82 | ``` bash 83 | # Get all persistant volume clames that are less then 20Gi, and output as json. 84 | kubectl-sql -o json "select * from pvc where spec.resources.requests.storage < 20Gi" 85 | ... 86 | ``` 87 | 88 | ```bash 89 | # Get only first 10 pods ordered by name 90 | kubectl-sql "SELECT name, status.phase FROM */pods ORDER BY name LIMIT 10" 91 | ``` 92 | 93 |
96 | 97 |
100 | 101 |
104 |
105 | #### Output formats
106 |
107 | | --output flag | Print format |
108 | |----|---|
109 | | table | Table |
110 | | name | Names only |
111 | | yaml | YAML |
112 | | json | JSON |
113 |
114 | ## Alternatives
115 |
116 | #### jq
117 |
118 | `jq` is a lightweight and flexible command-line JSON processor. It is possible to
119 | pipe the kubectl command output into the `jq` command to create complicated searches ( [Illustrated jq toturial](https://github.com/MoserMichael/jq-illustrated) )
120 |
121 |
2 |
2 |
12 |
13 | ---
14 |
15 | **Basic Selection & Namespace Filtering**
16 |
17 | * **Select all pods in `default`:**
18 |
19 | ```bash
20 | kubectl sql "SELECT * FROM default/pods"
21 | ```
22 |
23 | * **Names & namespaces of deployments:**
24 |
25 | ```bash
26 | kubectl sql "SELECT name, namespace FROM */deployments"
27 | ```
28 |
29 | * **Service names & types in `kube-system`:**
30 |
31 | ```bash
32 | kubectl sql "SELECT name, spec.type FROM kube-system/services"
33 | ```
34 |
35 | ---
36 |
37 | **Sorting and Limiting Results**
38 |
39 | * **Sort pods by creation time (newest first):**
40 |
41 | ```bash
42 | kubectl sql "SELECT name, metadata.creationTimestamp FROM */pods ORDER BY metadata.creationTimestamp DESC"
43 | ```
44 |
45 | * **Get the 5 oldest deployments:**
46 |
47 | ```bash
48 | kubectl sql "SELECT name, metadata.creationTimestamp FROM */deployments ORDER BY metadata.creationTimestamp ASC LIMIT 5"
49 | ```
50 |
51 | * **Sort pods by name and limit to 10 results:**
52 |
53 | ```bash
54 | kubectl sql "SELECT name, status.phase FROM */pods ORDER BY name LIMIT 10"
55 | ```
56 |
57 | * **Get pods with most restarts:**
58 |
59 | ```bash
60 | kubectl sql "SELECT name, status.containerStatuses[1].restartCount FROM */pods ORDER BY status.containerStatuses[1].restartCount DESC LIMIT 5"
61 | ```
62 |
63 | * **Sort services by number of ports (multiple-column sorting):**
64 |
65 | ```bash
66 | kubectl sql "SELECT name, namespace FROM */services ORDER BY namespace ASC, name DESC"
67 | ```
68 |
69 | ---
70 |
71 | **Filtering with `WHERE` Clause**
72 |
73 | * **Pods with label `app=my-app`:**
74 |
75 | ```bash
76 | kubectl sql "SELECT name FROM */pods WHERE metadata.labels.app = 'my-app'"
77 | ```
78 |
79 | * **Deployments with image `nginx.*`:**
80 |
81 | ```bash
82 | kubectl sql "SELECT name FROM */deployments WHERE spec.template.spec.containers[1].image ~= 'nginx.*'"
83 | ```
84 |
85 | * **Services of type `LoadBalancer`:**
86 |
87 | ```bash
88 | kubectl sql "SELECT name FROM */services WHERE spec.type = 'LoadBalancer'"
89 | ```
90 |
91 | * **Pods not `Running`:**
92 |
93 | ```bash
94 | kubectl sql "SELECT name, status.phase FROM */pods WHERE status.phase != 'Running'"
95 | ```
96 |
97 | * **Pods with container named nginx:**
98 |
99 | ```bash
100 | kubectl sql "SELECT name from */pods where spec.containers[1].name = 'nginx'"
101 | ```
102 |
103 | ---
104 |
105 | **Aliasing with `AS` Keyword**
106 |
107 | * **Alias `status.phase` to `pod_phase`:**
108 |
109 | ```bash
110 | kubectl sql "SELECT name, status.phase AS pod_phase FROM */pods"
111 | ```
112 |
113 | * **Alias container image to `container_image`:**
114 |
115 | ```bash
116 | kubectl sql "SELECT name, spec.template.spec.containers[1].image AS container_image FROM */deployments"
117 | ```
118 |
119 | ---
120 |
121 | **Time-Based Filtering (using `date`)**
122 |
123 | * **Pods created in last 24 hours:**
124 |
125 | ```bash
126 | kubectl sql "SELECT name, metadata.creationTimestamp FROM */pods WHERE metadata.creationTimestamp > '$(date -Iseconds -d "24 hours ago")'"
127 | ```
128 |
129 | * **Events related to pods in last 10 minutes:**
130 |
131 | ```bash
132 | kubectl sql "SELECT message, metadata.creationTimestamp, involvedObject.name FROM */events WHERE involvedObject.kind = 'Pod' AND metadata.creationTimestamp > '$(date -Iseconds -d "10 minutes ago")'"
133 | ```
134 |
135 | ---
136 |
137 | **SI Extension Filtering**
138 |
139 | * **Deployments with memory request < 512Mi:**
140 |
141 | ```bash
142 | kubectl sql "SELECT name, spec.template.spec.containers[1].resources.requests.memory FROM */deployments WHERE spec.template.spec.containers[1].resources.requests.memory < 512Mi"
143 | ```
144 |
145 | * **PVCs with storage request > 10Gi:**
146 |
147 | ```bash
148 | kubectl sql "SELECT name, spec.resources.requests.storage FROM */persistentvolumeclaims WHERE spec.resources.requests.storage > 10Gi"
149 | ```
150 |
151 | * **Pods with container memory limit > 1Gi:**
152 |
153 | ```bash
154 | kubectl sql "SELECT name, spec.containers[1].resources.limits.memory FROM */pods WHERE spec.containers[1].resources.limits.memory > 1Gi"
155 | ```
156 |
157 | ---
158 |
159 | **Array Operations (`any`, `all`, `len`)**
160 |
161 | * **Pods with any container using nginx image:**
162 |
163 | ```bash
164 | kubectl sql "SELECT name FROM */pods WHERE any(spec.containers[*].image ~= 'nginx')"
165 | ```
166 |
167 | * **Pods with any container requesting more than 1Gi memory:**
168 |
169 | ```bash
170 | kubectl sql "SELECT name FROM */pods WHERE any(spec.containers[*].resources.requests.memory > 1Gi)"
171 | ```
172 |
173 | * **Deployments where all containers have resource limits:**
174 |
175 | ```bash
176 | kubectl sql "SELECT name FROM */deployments WHERE all(spec.template.spec.containers[*].resources.limits is not null)"
177 | ```
178 |
179 | * **Pods where all containers are ready:**
180 |
181 | ```bash
182 | kubectl sql "SELECT name FROM */pods WHERE all(status.containerStatuses[*].ready = true)"
183 | ```
184 |
185 | * **Deployments with more than 2 containers:**
186 |
187 | ```bash
188 | kubectl sql "SELECT name FROM */deployments WHERE len(spec.template.spec.containers) > 2"
189 | ```
190 |
191 | * **Nodes with many pods:**
192 |
193 | ```bash
194 | kubectl sql "SELECT name FROM nodes WHERE len(status.conditions) > 5"
195 | ```
196 |
197 | * **Pods with empty volumes list:**
198 |
199 | ```bash
200 | kubectl sql "SELECT name FROM */pods WHERE len(spec.volumes) = 0"
201 | ```
202 |
203 | ---
204 |
205 | **All namespaces**
206 |
207 | * **Get pods that have name containing "ovs" using regular kubectl all namespaces arg:**
208 |
209 | ```bash
210 | kubectl-sql --all-namespaces "select * from pods where name ~= 'cni'"
211 | NAMESPACE NAME PHASE hostIP CREATION_TIME(RFC3339)
212 | openshift-cnv ovs-cni-amd64-5vgcg Running 192.168.126.58 2020-02-10T23:26:31+02:00
213 | openshift-cnv ovs-cni-amd64-8ts4w Running 192.168.126.12 2020-02-10T22:01:59+02:00
214 | openshift-cnv ovs-cni-amd64-d6vdb Running 192.168.126.53 2020-02-10T23:13:45+02:00
215 | ...
216 | ```
217 |
218 | ---
219 |
220 | **Namespaced**
221 |
222 | * **Get pods in namespace "openshift-multus" that have name containing "ovs":**
223 |
224 | ```bash
225 | kubectl-sql -n openshift-multus "select * from pods where name ~= 'cni'"
226 | KIND: Pod COUNT: 3
227 | NAMESPACE NAME PHASE CREATION_TIME(RFC3339)
228 | openshift-multus multus-additional-cni-plugins-7kcsd Running 2024-12-02T11:41:45Z
229 | openshift-multus multus-additional-cni-plugins-kc8sz Running 2024-12-02T11:41:45Z
230 | openshift-multus multus-additional-cni-plugins-vrpx9 Running 2024-12-02T11:41:45Z
231 | ...
232 | ```
233 |
234 | ---
235 |
236 | **Select fields**
237 |
238 | * **Get pods in namespace "openshift-multus" with name containing "cni" and select specific fields:**
239 |
240 | ```bash
241 | kubectl-sql "select name, status.phase, status.podIP \
242 | from openshift-multus/pods \
243 | where name ~= 'cni'"
244 | KIND: Pod COUNT: 3
245 | name status.phase status.podIP
246 | multus-additional-cni-plugins-7kcsd Running 10.130.10.85
247 | multus-additional-cni-plugins-kc8sz Running 10.131.6.65
248 | multus-additional-cni-plugins-vrpx9 Running 10.129.8.252
249 | ...
250 | ```
251 |
252 | ---
253 |
254 | **Alias selected fields**
255 |
256 | * **Get pods matching criteria with aliased fields:**
257 |
258 | ```bash
259 | kubectl-sql "select name, status.phase as phase, status.podIP as ip \
260 | from openshift-multus/pods \
261 | where name ~= 'cni' and ip ~= '5$' and phase = 'Running'"
262 | KIND: Pod COUNT: 2
263 | name phase ip
264 | multus-additional-cni-plugins-7kcsd Running 10.130.10.85
265 | multus-additional-cni-plugins-kc8sz Running 10.131.6.65
266 | ...
267 | ```
268 |
269 | ---
270 |
271 | **Using Regexp**
272 |
273 | * **Get pods with name starting with "virt-" and IP ending with ".84":**
274 |
275 | ```bash
276 | kubectl-sql -n default "select * from pods where name ~= '^virt-' and status.podIP ~= '[.]84$'"
277 | NAMESPACE NAME PHASE hostIP CREATION_TIME(RFC3339)
278 | default virt-launcher-test-bdw2p-lcrwx Running 192.168.126.56 2020-02-12T14:14:01+02:00
279 | ...
280 | ```
281 |
282 | ---
283 |
284 | **SI Units**
285 |
286 | * **Get PVCs less than 20Gi and output as JSON:**
287 |
288 | ```bash
289 | kubectl-sql -o json "select * from */pvc where spec.resources.requests.storage < 20Gi"
290 |
291 | ... json
292 | {
293 | "storage": "10Gi"
294 | }
295 | ...
296 | ```
297 |
298 | ---
299 |
300 | **Comparing fields**
301 |
302 | * **Get replica sets with 3 replicas but less ready replicas:**
303 |
304 | ```bash
305 | kubectl-sql --all-namespaces "select * from rs where spec.replicas = 3 and status.readyReplicas < spec.replicas"
306 |
307 | ...
308 | ```
309 |
310 | ---
311 |
312 | **Escaping Identifiers**
313 |
314 | * **Use square brackets for identifiers with special characters:**
315 |
316 | ```bash
317 | ./kubectl-sql --all-namespaces "select * from pods where name ~= 'cni' and metadata.labels[openshift.io/component] = 'network'"
318 | ...
319 | ```
320 |
321 | ---
322 |
323 | **Print help**
324 |
325 | * **Display kubectl-sql help:**
326 |
327 | ```bash
328 | kubectl-sql --help
329 | ...
330 | ```
331 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/pkg/cmd/sql-sql.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "regexp"
7 | "strconv"
8 | "strings"
9 |
10 | "k8s.io/client-go/rest"
11 |
12 | "github.com/yaacov/kubectl-sql/pkg/client"
13 | "github.com/yaacov/kubectl-sql/pkg/filter"
14 | "github.com/yaacov/kubectl-sql/pkg/printers"
15 | )
16 |
17 | // isValidFieldIdentifier checks if a field name matches the allowed pattern
18 | func isValidFieldIdentifier(field string) bool {
19 | // Check for labels.* pattern
20 | if strings.HasPrefix(field, "labels.") {
21 | labelKey := field[7:] // Remove "labels." prefix
22 | // K8s label keys: alphanumeric, hyphens, underscores, dots
23 | // Must start and end with alphanumeric character
24 | labelPattern := `^[a-zA-Z0-9]([a-zA-Z0-9\-_.]*[a-zA-Z0-9])?$`
25 | match, _ := regexp.MatchString(labelPattern, labelKey)
26 | return match
27 | }
28 |
29 | // Check for annotations.* pattern
30 | if strings.HasPrefix(field, "annotations.") {
31 | annotationKey := field[12:] // Remove "annotations." prefix
32 | // K8s annotation keys: similar to labels but more flexible
33 | // Can contain alphanumeric, hyphens, underscores, dots, and slashes
34 | annotationPattern := `^[a-zA-Z0-9]([a-zA-Z0-9\-_./]*[a-zA-Z0-9])?$`
35 | match, _ := regexp.MatchString(annotationPattern, annotationKey)
36 | return match
37 | }
38 |
39 | // Matches patterns like:
40 | // - simple: name, first_name, my.field
41 | // - array access: items[0], my.array[123]
42 | pattern := `^[a-zA-Z_]([a-zA-Z0-9_.]*(?:\[\d+\])?)*$`
43 | match, _ := regexp.MatchString(pattern, field)
44 | return match
45 | }
46 |
47 | // isValidK8sResourceName checks if a resource name follows Kubernetes naming conventions
48 | func isValidK8sResourceName(resource string) bool {
49 | // Matches lowercase words separated by dots or slashes
50 | // Examples: pods, deployments, apps/v1/deployments
51 | pattern := `^[a-z]+([a-z0-9-]*[a-z0-9])?(/[a-z0-9]+)*$`
52 | match, _ := regexp.MatchString(pattern, resource)
53 | return match
54 | }
55 |
56 | // isValidNamespace checks if a namespace name is valid according to Kubernetes naming conventions
57 | // or if it's the special "*" value for all namespaces
58 | func isValidNamespace(namespace string) bool {
59 | // Special case for "all namespaces"
60 | if namespace == "*" {
61 | return true
62 | }
63 |
64 | pattern := `^[a-z0-9]([a-z0-9\-]*[a-z0-9])?$`
65 | match, _ := regexp.MatchString(pattern, namespace)
66 | return match
67 | }
68 |
69 | // QueryType represents the type of SQL query
70 | type QueryType int
71 |
72 | const (
73 | SimpleQuery QueryType = iota
74 | )
75 |
76 | // parseFields extracts and validates SELECT fields
77 | func (o *SQLOptions) parseFields(selectFields string) error {
78 | if selectFields == "*" {
79 | return nil
80 | }
81 |
82 | if len(strings.TrimSpace(selectFields)) == 0 {
83 | return fmt.Errorf("SELECT clause cannot be empty")
84 | }
85 |
86 | fields := strings.Split(selectFields, ",")
87 | tableFields := make([]printers.TableField, 0, len(fields))
88 |
89 | for _, field := range fields {
90 | field = strings.TrimSpace(field)
91 |
92 | // Check for AS syntax
93 | parts := strings.Split(strings.ToUpper(field), " AS ")
94 | var name, title string
95 |
96 | if len(parts) == 2 {
97 | // We have an AS clause
98 | name = strings.TrimSpace(field[:strings.Index(strings.ToUpper(field), " AS ")])
99 | title = strings.TrimSpace(field[strings.Index(strings.ToUpper(field), " AS ")+4:])
100 |
101 | if !isValidFieldIdentifier(name) {
102 | return fmt.Errorf("invalid field identifier before AS: %s", name)
103 | }
104 | if !isValidFieldIdentifier(title) {
105 | return fmt.Errorf("invalid field identifier after AS: %s", title)
106 | }
107 | } else {
108 | // No AS clause, use field as both name and title
109 | if !isValidFieldIdentifier(field) {
110 | return fmt.Errorf("invalid field identifier: %s", field)
111 | }
112 | name = field
113 | title = field
114 | }
115 |
116 | // Append to table fields
117 | tableFields = append(tableFields, printers.TableField{
118 | Name: name,
119 | Title: title,
120 | })
121 |
122 | // Append to default aliases
123 | o.defaultAliases[title] = name
124 | }
125 |
126 | o.defaultTableFields[printers.SelectedFields] = tableFields
127 | return nil
128 | }
129 |
130 | // parseResources validates and sets the requested resources
131 | func (o *SQLOptions) parseResources(resources []string, queryType QueryType) error {
132 | for i, r := range resources {
133 | r = strings.TrimSpace(r)
134 |
135 | // Split resource on "/" to check for namespace
136 | parts := strings.Split(r, "/")
137 | var resourceName string
138 |
139 | switch len(parts) {
140 | case 1:
141 | resourceName = parts[0]
142 | case 2:
143 | // Check for namespace validity
144 | namespace := parts[0]
145 | if !isValidNamespace(namespace) {
146 | return fmt.Errorf("invalid namespace: %s", namespace)
147 | }
148 |
149 | // Set namespace options
150 | o.namespace = namespace
151 | resourceName = parts[1]
152 | default:
153 | return fmt.Errorf("invalid resource format: %s, expected [namespace/]resource or */resource for all namespaces", r)
154 | }
155 |
156 | if !isValidK8sResourceName(resourceName) {
157 | return fmt.Errorf("invalid resource name: %s", resourceName)
158 | }
159 |
160 | resources[i] = resourceName
161 | }
162 |
163 | if len(resources) != 1 {
164 | return fmt.Errorf("exactly one resource must be specified")
165 | }
166 |
167 | o.requestedResources = resources
168 | return nil
169 | }
170 |
171 | // identifyQueryType determines the type of SQL query and returns relevant indices
172 | func (o *SQLOptions) identifyQueryType(query string) (QueryType, map[string]int, error) {
173 | upperQuery := strings.ToUpper(query)
174 | if !strings.HasPrefix(upperQuery, "SELECT") {
175 | return SimpleQuery, nil, fmt.Errorf("query must start with SELECT")
176 | }
177 |
178 | indices := map[string]int{
179 | "SELECT": 0,
180 | "FROM": strings.Index(upperQuery, " FROM "),
181 | "JOIN": strings.Index(upperQuery, " JOIN "),
182 | "ON": strings.Index(upperQuery, " ON "),
183 | "WHERE": strings.Index(upperQuery, " WHERE "),
184 | "ORDER BY": strings.Index(upperQuery, " ORDER BY "),
185 | "LIMIT": strings.Index(upperQuery, " LIMIT "),
186 | }
187 |
188 | if indices["FROM"] == -1 {
189 | return 0, nil, fmt.Errorf("missing FROM clause in query")
190 | }
191 |
192 | return SimpleQuery, indices, nil
193 | }
194 |
195 | // parseOrderBy extracts and validates the ORDER BY clause
196 | func (o *SQLOptions) parseOrderBy(query string, indices map[string]int) error {
197 | if indices["ORDER BY"] == -1 {
198 | return nil
199 | }
200 |
201 | orderByStart := indices["ORDER BY"] + 9
202 | var orderByEnd int
203 | if indices["LIMIT"] != -1 {
204 | orderByEnd = indices["LIMIT"]
205 | } else {
206 | orderByEnd = len(query)
207 | }
208 |
209 | orderByStr := strings.TrimSpace(query[orderByStart:orderByEnd])
210 | if orderByStr == "" {
211 | return fmt.Errorf("ORDER BY clause cannot be empty")
212 | }
213 |
214 | fields := strings.Split(orderByStr, ",")
215 | orderByFields := make([]printers.OrderByField, 0, len(fields))
216 |
217 | for _, field := range fields {
218 | field = strings.TrimSpace(field)
219 | if field == "" {
220 | continue
221 | }
222 |
223 | parts := strings.Fields(field)
224 | if len(parts) == 0 {
225 | continue
226 | }
227 |
228 | fieldName := parts[0]
229 | // Check for possible alias
230 | if alias, err := o.checkColumnName(fieldName); err == nil {
231 | fieldName = alias
232 | }
233 |
234 | orderBy := printers.OrderByField{
235 | Name: fieldName,
236 | Descending: false,
237 | }
238 |
239 | // Check for DESC/ASC modifier
240 | if len(parts) > 1 && strings.ToUpper(parts[1]) == "DESC" {
241 | orderBy.Descending = true
242 | }
243 |
244 | orderByFields = append(orderByFields, orderBy)
245 | }
246 |
247 | o.orderByFields = orderByFields
248 | return nil
249 | }
250 |
251 | // parseLimit extracts and validates the LIMIT clause
252 | func (o *SQLOptions) parseLimit(query string, indices map[string]int) error {
253 | if indices["LIMIT"] == -1 {
254 | return nil
255 | }
256 |
257 | limitStart := indices["LIMIT"] + 6
258 | limitStr := strings.TrimSpace(query[limitStart:])
259 |
260 | // Check if there are other clauses after LIMIT
261 | if space := strings.Index(limitStr, " "); space != -1 {
262 | limitStr = limitStr[:space]
263 | }
264 |
265 | limit, err := strconv.Atoi(limitStr)
266 | if err != nil {
267 | return fmt.Errorf("invalid LIMIT value: %s", limitStr)
268 | }
269 |
270 | if limit < 0 {
271 | return fmt.Errorf("LIMIT cannot be negative: %d", limit)
272 | }
273 |
274 | o.limit = limit
275 | return nil
276 | }
277 |
278 | // parseQueryParts extracts and validates different parts of the query
279 | func (o *SQLOptions) parseQueryParts(query string, indices map[string]int, queryType QueryType) error {
280 | // Parse FROM resource (only one resource allowed)
281 | var fromEnd int
282 | if indices["WHERE"] != -1 {
283 | fromEnd = indices["WHERE"]
284 | } else if indices["ORDER BY"] != -1 {
285 | fromEnd = indices["ORDER BY"]
286 | } else if indices["LIMIT"] != -1 {
287 | fromEnd = indices["LIMIT"]
288 | } else {
289 | fromEnd = len(query)
290 | }
291 |
292 | fromPart := strings.TrimSpace(query[indices["FROM"]+5 : fromEnd])
293 | resources := strings.Split(fromPart, ",")
294 | if len(resources) != 1 {
295 | return fmt.Errorf("only one resource allowed in FROM clause")
296 | }
297 |
298 | allResources := []string{resources[0]}
299 |
300 | if err := o.parseResources(allResources, queryType); err != nil {
301 | return err
302 | }
303 |
304 | // Parse SELECT fields
305 | selectFields := strings.TrimSpace(query[6:indices["FROM"]])
306 | if err := o.parseFields(selectFields); err != nil {
307 | return err
308 | }
309 |
310 | // Parse WHERE clause if present
311 | if indices["WHERE"] != -1 {
312 | whereStart := indices["WHERE"] + 6
313 | var whereEnd int
314 | if indices["ORDER BY"] != -1 {
315 | whereEnd = indices["ORDER BY"]
316 | } else if indices["LIMIT"] != -1 {
317 | whereEnd = indices["LIMIT"]
318 | } else {
319 | whereEnd = len(query)
320 | }
321 | wherePart := strings.TrimSpace(query[whereStart:whereEnd])
322 | if wherePart == "" {
323 | return fmt.Errorf("WHERE clause cannot be empty")
324 | }
325 | o.requestedQuery = wherePart
326 | }
327 |
328 | // Parse ORDER BY clause if present
329 | if err := o.parseOrderBy(query, indices); err != nil {
330 | return err
331 | }
332 |
333 | // Parse LIMIT clause if present
334 | if err := o.parseLimit(query, indices); err != nil {
335 | return err
336 | }
337 |
338 | return nil
339 | }
340 |
341 | // CompleteSQL parses SQL query into components
342 | func (o *SQLOptions) CompleteSQL(query string) error {
343 | queryType, indices, err := o.identifyQueryType(query)
344 | if err != nil {
345 | return err
346 | }
347 |
348 | if err := o.parseQueryParts(query, indices, queryType); err != nil {
349 | return err
350 | }
351 |
352 | return nil
353 | }
354 |
355 | // Get the resource list.
356 | func (o *SQLOptions) Get(config *rest.Config) error {
357 | c := client.Config{
358 | Config: config,
359 | Namespace: o.namespace,
360 | }
361 |
362 | if len(o.requestedQuery) > 0 {
363 | return o.printFilteredResources(c)
364 | }
365 |
366 | return o.printResources(c)
367 | }
368 |
369 | // printResources prints resources lists.
370 | func (o *SQLOptions) printResources(c client.Config) error {
371 | ctx := context.Background()
372 | for _, r := range o.requestedResources {
373 | list, err := c.List(ctx, r)
374 | if err != nil {
375 | return err
376 | }
377 |
378 | err = o.Printer(list)
379 | if err != nil {
380 | return err
381 | }
382 | }
383 |
384 | return nil
385 | }
386 |
387 | // printFilteredResources prints filtered resource list.
388 | func (o *SQLOptions) printFilteredResources(c client.Config) error {
389 | ctx := context.Background()
390 | f := filter.Config{
391 | CheckColumnName: o.checkColumnName,
392 | Query: o.requestedQuery,
393 | }
394 |
395 | // Print resources lists.
396 | for _, r := range o.requestedResources {
397 | list, err := c.List(ctx, r)
398 | if err != nil {
399 | return err
400 | }
401 |
402 | // Filter items by query.
403 | filteredList, err := f.Filter(list)
404 | if err != nil {
405 | return err
406 | }
407 |
408 | err = o.Printer(filteredList)
409 | if err != nil {
410 | return err
411 | }
412 | }
413 |
414 | return nil
415 | }
416 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
2 | github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
3 | github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
4 | github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
5 | github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
6 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
7 | github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
8 | github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
9 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
10 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
11 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
12 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
13 | github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
14 | github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
15 | github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
16 | github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
17 | github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
18 | github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
19 | github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
20 | github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
21 | github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
22 | github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
23 | github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
24 | github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
25 | github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
26 | github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
27 | github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
28 | github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
29 | github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
30 | github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
31 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
32 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
33 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
34 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
35 | github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
36 | github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
37 | github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
38 | github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
39 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
40 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
41 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
42 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
43 | github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
44 | github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
45 | github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
46 | github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
47 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
48 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
49 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
50 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
51 | github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
52 | github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
53 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
54 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
55 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
56 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
57 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
58 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
59 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
60 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
61 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
62 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
63 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
64 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
65 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
66 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
67 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
68 | github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
69 | github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
70 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
71 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
72 | github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
73 | github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
74 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
75 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
76 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
77 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
78 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
79 | github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
80 | github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
81 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
82 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
83 | github.com/onsi/ginkgo/v2 v2.22.1 h1:QW7tbJAUDyVDVOM5dFa7qaybo+CRfR7bemlQUN6Z8aM=
84 | github.com/onsi/ginkgo/v2 v2.22.1/go.mod h1:S6aTpoRsSq2cZOd+pssHAlKW/Q/jZt6cPrPlnj4a1xM=
85 | github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
86 | github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
87 | github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
88 | github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
89 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
90 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
91 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
92 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
93 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
94 | github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
95 | github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
96 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
97 | github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
98 | github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
99 | github.com/spf13/cobra v1.9.0 h1:Py5fIuq/lJsRYxcxfOtsJqpmwJWCMOUy2tMJYV8TNHE=
100 | github.com/spf13/cobra v1.9.0/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
101 | github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
102 | github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
103 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
104 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
105 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
106 | github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
107 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
108 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
109 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
110 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
111 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
112 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
113 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
114 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
115 | github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
116 | github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
117 | github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
118 | github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
119 | github.com/yaacov/tree-search-language/v6 v6.0.8 h1:BnrvqOOh/VS84EH0MSKaZHZEBTYOf6tAMUCdzOdOxuU=
120 | github.com/yaacov/tree-search-language/v6 v6.0.8/go.mod h1:ZXSpcgyyUZswjeC/OOYBnrvUWhhENumaV4+Xeg59NFk=
121 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
122 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
123 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
124 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
125 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
126 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
127 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
128 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
129 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
130 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
131 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
132 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
133 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
134 | golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
135 | golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
136 | golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
137 | golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
138 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
139 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
140 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
141 | golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
142 | golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
143 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
144 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
145 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
146 | golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
147 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
148 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
149 | golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
150 | golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
151 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
152 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
153 | golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
154 | golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
155 | golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
156 | golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
157 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
158 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
159 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
160 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
161 | golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
162 | golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
163 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
164 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
165 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
166 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
167 | google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
168 | google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
169 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
170 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
171 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
172 | gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
173 | gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
174 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
175 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
176 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
177 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
178 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
179 | k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw=
180 | k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y=
181 | k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ=
182 | k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
183 | k8s.io/cli-runtime v0.32.2 h1:aKQR4foh9qeyckKRkNXUccP9moxzffyndZAvr+IXMks=
184 | k8s.io/cli-runtime v0.32.2/go.mod h1:a/JpeMztz3xDa7GCyyShcwe55p8pbcCVQxvqZnIwXN8=
185 | k8s.io/client-go v0.32.2 h1:4dYCD4Nz+9RApM2b/3BtVvBHw54QjMFUl1OLcJG5yOA=
186 | k8s.io/client-go v0.32.2/go.mod h1:fpZ4oJXclZ3r2nDOv+Ux3XcJutfrwjKTCHz2H3sww94=
187 | k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
188 | k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
189 | k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
190 | k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
191 | k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
192 | k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
193 | sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
194 | sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
195 | sigs.k8s.io/kustomize/api v0.18.0 h1:hTzp67k+3NEVInwz5BHyzc9rGxIauoXferXyjv5lWPo=
196 | sigs.k8s.io/kustomize/api v0.18.0/go.mod h1:f8isXnX+8b+SGLHQ6yO4JG1rdkZlvhaCf/uZbLVMb0U=
197 | sigs.k8s.io/kustomize/kyaml v0.18.1 h1:WvBo56Wzw3fjS+7vBjN6TeivvpbW9GmRaWZ9CIVmt4E=
198 | sigs.k8s.io/kustomize/kyaml v0.18.1/go.mod h1:C3L2BFVU1jgcddNBE1TxuVLgS46TjObMwW5FT9FcjYo=
199 | sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
200 | sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
201 | sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
202 | sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
203 |
--------------------------------------------------------------------------------
/.github/img/kubesql.svg:
--------------------------------------------------------------------------------
1 |
2 |
571 |
--------------------------------------------------------------------------------
3 |
3 |