File ksctl-2.8.0.obscpio of Package ksctl
07070100000000000081A40000000000000000000000016818E78D000005FE000000000000000000000000000000000000001D00000000ksctl-2.8.0/.goreleaser.yaml# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
# The lines below are called `modelines`. See `:help modeline`
# Feel free to remove those if you don't want/need to use them.
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
version: 2
project_name: ksctl-cli
before:
hooks:
- go mod tidy
builds:
- env:
- CGO_ENABLED=0
binary: ksctl
goos:
- linux
- darwin
goarch:
- amd64
- arm64
ldflags:
- -s -w -X "github.com/ksctl/cli/v2/pkg/config.BuildDate={{.Env.DATE}}"
- -s -w -X "github.com/ksctl/cli/v2/pkg/config.Version={{.Env.VERSION}}"
- -s -w -X "github.com/ksctl/cli/v2/pkg/config.KsctlCoreVer={{.Env.OCIVERSION}}"
- -s -w -X "github.com/ksctl/cli/v2/pkg/telemetry.clientIdentity={{.Env.CLI_IDENTITY}}"
signs:
- cmd: cosign
certificate: '${artifact}.cert'
args:
- sign-blob
- '--output-certificate=${certificate}'
- '--output-signature=${signature}'
- '${artifact}'
- --yes
artifacts: all
output: true
changelog:
sort: asc
filters:
exclude:
- "^scripts:"
- "^gen:"
release:
draft: true
make_latest: true
header: >-
# 🚀 Changelog
This release contains a number of enhancements and bug fixes. For a full list of changes
footer: >-
---
Released by [GoReleaser](https://github.com/goreleaser/goreleaser).
07070100000001000081A40000000000000000000000016818E78D0000038A000000000000000000000000000000000000002400000000ksctl-2.8.0/.pre-commit-config.yamlrepos:
- repo: local
hooks:
- id: add-license
name: Add License Headers
entry: addlicense
language: system
types: [go]
args: ["-c", "Ksctl Authors"]
- id: go-fmt
name: go fmt
entry: gofmt -l -w .
language: system
types: [go]
- id: go-vet
name: go vet
entry: go vet ./...
language: system
types: [go]
pass_filenames: false
- id: go-mod-tidy
name: go mod tidy
entry: go mod tidy
language: system
pass_filenames: false
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- id: mixed-line-ending
- id: check-merge-conflict
- id: check-executables-have-shebangs
07070100000002000081A40000000000000000000000016818E78D00001459000000000000000000000000000000000000001F00000000ksctl-2.8.0/CODE_OF_CONDUCT.md# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
Discord.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
07070100000003000081A40000000000000000000000016818E78D00000057000000000000000000000000000000000000001C00000000ksctl-2.8.0/CONTRIBUTING.mdThe contents are moved to [docs](https://docs.ksctl.com/docs/contribution-guidelines/)
07070100000004000081A40000000000000000000000016818E78D00002C5D000000000000000000000000000000000000001400000000ksctl-2.8.0/LICENSE Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
07070100000005000081A40000000000000000000000016818E78D00000099000000000000000000000000000000000000001B00000000ksctl-2.8.0/MAINTAINERS.mdPlease refer to [MAINTAINERS.md](https://github.com/ksctl/community/blob/main/MAINTAINERS.md) for more information on how to contribute to this project.
07070100000006000081A40000000000000000000000016818E78D00000AF3000000000000000000000000000000000000001500000000ksctl-2.8.0/MakefileGOOS_LINUX = linux
GOOS_MACOS = darwin
GOARCH_LINUX ?= amd64
GOARCH_MACOS = arm64
GOARCH_MACOS_INTEL = amd64
CURR_TIME = $(shell date +%s)
.PHONY: help
help: ## Display this help.
@awk 'BEGIN {FS = ":.*##"; printf "\n\033[36m _ _ _ \n | | | | | |\n | | _____ ___| |_| |\n | |/ / __|/ __| __| |\n | <\\__ \\ (__| |_| |\n |_|\\_\\___/\\___|\\__|_| \033[0m\n\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
##@ Generate
.PHONY: gen-docs
gen-docs: ## Generates docs
go run gen/docs.go
##@ Install (Dev)
.PHONY: install_linux
install_linux: ## Install ksctl
@echo "Started to Install ksctl"
cd scripts && \
env GOOS=${GOOS_LINUX} GOARCH=${GOARCH_LINUX} ./builder.sh
.PHONY: install_macos
install_macos: ## Install ksctl on macos m1,m2,..
@echo "Started to Install ksctl"
cd scripts && \
env GOOS=${GOOS_MACOS} GOARCH=${GOARCH_MACOS} ./builder.sh
.PHONY: install_macos_intel
install_macos_intel: ## Install ksctl on macos intel
@echo "Started to Install ksctl"
cd scripts && \
env GOOS=${GOOS_MACOS} GOARCH=${GOARCH_MACOS_INTEL} ./builder.sh
.PHONY: uninstall
uninstall: ## Uninstall ksctl
@echo "Started to Uninstall ksctl"
cd scripts && \
./uninstall.sh
##@ Linters
.PHONY: golangci-lint
golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
$(GOLANGCI_LINT): $(LOCALBIN)
$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,${GOLANGCI_LINT_VERSION})
.PHONY: lint
lint: golangci-lint ## Run golangci-lint linter & yamllint
$(GOLANGCI_LINT) run && echo -e "\n=========\n\033[91m✔ PASSED\033[0m\n=========\n" || echo -e "\n=========\n\033[91m✖ FAILED\033[0m\n=========\n"
##@ Dependencies
## Location to install dependencies to
LOCALBIN ?= /tmp/bin
$(LOCALBIN):
mkdir -p $(LOCALBIN)
## Tool Binaries
GOLANGCI_LINT = $(LOCALBIN)/golangci-lint-$(GOLANGCI_LINT_VERSION)
## Tool Versions
GOLANGCI_LINT_VERSION ?= v1.54.2
.PHONY: golangci-lint
golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
$(GOLANGCI_LINT): $(LOCALBIN)
$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,${GOLANGCI_LINT_VERSION})
# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
# $1 - target path with name of binary (ideally with version)
# $2 - package url which can be installed
# $3 - specific version of package
define go-install-tool
@[ -f $(1) ] || { \
set -e; \
package=$(2)@$(3) ;\
echo "Downloading $${package}" ;\
GOBIN=$(LOCALBIN) go install $${package} ;\
mv "$$(echo "$(1)" | sed "s/-$(3)$$//")" $(1) ;\
}
endef
07070100000007000081A40000000000000000000000016818E78D000005E0000000000000000000000000000000000000001600000000ksctl-2.8.0/README.md
# Ksctl CLI
It utilizes [Ksctl](https://github.com/ksctl/ksctl) and builds on top to provide Command Line user experience
[](https://goreportcard.com/report/github.com/ksctl/cli) [](https://pkg.go.dev/github.com/ksctl/cli) [](https://www.bestpractices.dev/projects/7469)
  
## All Repositories under (Ksctl Org)
Repo | Description
-|-
[Ksctl](https://github.com/ksctl/ksctl) | It provides the core components aka the internals of ksctl features
[Ksctl CLI](https://github.com/ksctl/cli) | It uses the ksctl repo to make a CLI tool
[Ksctl Docs](https://github.com/ksctl/docs) | It's for documentation purpose and to host the ksctl website
## Getting Started guide
[Getting Started guide](https://docs.ksctl.com/docs/getting-started/)
## Usage
Please refer to the [CLI Reference guide](https://docs.ksctl.com/docs/reference/cli/)
## Contribution Guidelines
Please refer to our [contribution guide](https://docs.ksctl.com/docs/contribution-guidelines/) if you wish to contribute to the project :smile:
07070100000008000081A40000000000000000000000016818E78D00000159000000000000000000000000000000000000001800000000ksctl-2.8.0/SECURITY.md# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 1.0.1 | :white_check_mark: |
| < 1.0.0 | :x: |
## Reporting a Vulnerability
Check [contribution guide](./CONTRIBUTING.md) and also [website contribution page](https://docs.ksctl.com/docs/contribution-guidelines/)
07070100000009000041ED0000000000000000000000026818E78D00000000000000000000000000000000000000000000001000000000ksctl-2.8.0/cmd0707010000000A000081A40000000000000000000000016818E78D00001899000000000000000000000000000000000000001A00000000ksctl-2.8.0/cmd/addons.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"fmt"
"os"
"strconv"
"strings"
"github.com/ksctl/cli/v2/pkg/cli"
"github.com/ksctl/cli/v2/pkg/telemetry"
"github.com/ksctl/ksctl/v2/pkg/handler/cluster/controller"
addonsHandler "github.com/ksctl/ksctl/v2/pkg/handler/addons"
"github.com/spf13/cobra"
)
func (k *KsctlCommand) Addons() *cobra.Command {
cmd := &cobra.Command{
Use: "addons",
Example: `
ksctl addons --help
`,
Short: "Use to work with addons",
Long: "It is used to work with addons",
}
return cmd
}
func (k *KsctlCommand) EnableAddon() *cobra.Command {
cmd := &cobra.Command{
Use: "enable",
Example: `
ksctl addons enable --help
`,
Short: "Use to enable an addon",
Long: "It is used to enable an addon",
Run: func(cmd *cobra.Command, args []string) {
m, ok := k.addonClientSetup()
if !ok {
os.Exit(1)
}
if k.loadCloudProviderCreds(m.Provider) != nil {
os.Exit(1)
}
c, err := addonsHandler.NewController(
k.Ctx,
k.l,
&controller.Client{
Metadata: *m,
},
)
if err != nil {
k.l.Error("Error in creating the controller", "Error", err)
os.Exit(1)
}
addons, err := c.ListAllAddons()
if err != nil {
k.l.Error("Error in listing the addons", "Error", err)
os.Exit(1)
}
addonSku, err := k.menuDriven.DropDownList(
"Select the addon to enable",
addons,
)
if err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
os.Exit(1)
}
addonVers, err := c.ListAvailableVersions(addonSku)
if err != nil {
k.l.Error("Error in listing the versions", "Error", err)
os.Exit(1)
}
addonVer, err := k.menuDriven.DropDownList(
"Select the version to enable",
addonVers,
cli.WithDefaultValue(addonVers[0]),
)
if err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
os.Exit(1)
}
if cc, err := c.GetAddon(addonSku); err != nil {
k.l.Error("Error in getting the addon", "Error", err)
os.Exit(1)
} else {
if err := k.telemetry.Send(k.Ctx, k.l, telemetry.EventClusterAddonEnable, telemetry.TelemetryMeta{
Addons: []telemetry.TelemetryAddon{
{
Sku: addonSku,
Version: addonVer,
},
},
}); err != nil {
k.l.Debug(k.Ctx, "Failed to send the telemetry", "Reason", err)
}
if _err := cc.Install(addonVer); _err != nil {
k.l.Error("Error in enabling the addon", "Error", _err)
os.Exit(1)
}
}
k.l.Success(k.Ctx, "Addon enabled successfully", "sku", addonSku, "version", addonVer)
},
}
return cmd
}
func (k *KsctlCommand) DisableAddon() *cobra.Command {
cmd := &cobra.Command{
Use: "disable",
Example: `
ksctl addons disable --help
`,
Short: "Use to disable an addon",
Long: "It is used to disable an addon",
Run: func(cmd *cobra.Command, args []string) {
m, ok := k.addonClientSetup()
if !ok {
os.Exit(1)
}
if k.loadCloudProviderCreds(m.Provider) != nil {
os.Exit(1)
}
c, err := addonsHandler.NewController(
k.Ctx,
k.l,
&controller.Client{
Metadata: *m,
},
)
if err != nil {
k.l.Error("Error in creating the controller", "Error", err)
os.Exit(1)
}
addons, err := c.ListInstalledAddons()
if err != nil {
k.l.Error("Error in listing the installed addons", "Error", err)
os.Exit(1)
}
vals := make(map[string]string, len(addons))
for _, addon := range addons {
ver := "NaN"
if addon.Version != "" {
ver = addon.Version
}
vals[fmt.Sprintf("%s@%s", addon.Name, ver)] = addon.Name + "@" + ver
}
_selectedAddon, err := k.menuDriven.DropDown(
"Select the addon to disable",
vals,
)
if err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
os.Exit(1)
}
selectedAddon := strings.Split(_selectedAddon, "@")[0]
if cc, err := c.GetAddon(selectedAddon); err != nil {
k.l.Error("Error in getting the addon", "Error", err)
os.Exit(1)
} else {
if err := k.telemetry.Send(k.Ctx, k.l, telemetry.EventClusterAddonDisable, telemetry.TelemetryMeta{
Addons: []telemetry.TelemetryAddon{
{
Sku: selectedAddon,
Version: strings.Split(_selectedAddon, "@")[1],
},
},
}); err != nil {
k.l.Debug(k.Ctx, "Failed to send the telemetry", "Reason", err)
}
if _err := cc.Uninstall(); _err != nil {
k.l.Error("Error in disabling the addon", "Error", _err)
os.Exit(1)
}
}
k.l.Success(k.Ctx, "Addon disabled successfully", "sku", selectedAddon)
},
}
return cmd
}
func (k *KsctlCommand) addonClientSetup() (*controller.Metadata, bool) {
clusters, err := k.fetchAllClusters()
if err != nil {
k.l.Error("Error in fetching the clusters", "Error", err)
return nil, false
}
if len(clusters) == 0 {
k.l.Error("No clusters found to delete")
return nil, false
}
selectDisplay := make(map[string]string, len(clusters))
valueMaping := make(map[string]controller.Metadata, len(clusters))
for idx, cluster := range clusters {
selectDisplay[makeHumanReadableList(cluster)] = strconv.Itoa(idx)
valueMaping[strconv.Itoa(idx)] = controller.Metadata{
ClusterName: cluster.Name,
ClusterType: cluster.ClusterType,
Provider: cluster.CloudProvider,
Region: cluster.Region,
StateLocation: k.KsctlConfig.PreferedStateStore,
K8sDistro: cluster.K8sDistro,
K8sVersion: cluster.K8sVersion,
}
}
selectedCluster, err := k.menuDriven.DropDown(
"Select the cluster for addon operation",
selectDisplay,
)
if err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
return nil, false
}
m := valueMaping[selectedCluster]
return &m, true
}
0707010000000B000081A40000000000000000000000016818E78D0000086A000000000000000000000000000000000000001700000000ksctl-2.8.0/cmd/cli.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"context"
"os"
"github.com/ksctl/cli/v2/pkg/cli"
"github.com/ksctl/cli/v2/pkg/config"
cLogger "github.com/ksctl/cli/v2/pkg/logger"
"github.com/ksctl/cli/v2/pkg/telemetry"
"github.com/ksctl/ksctl/v2/pkg/consts"
"github.com/ksctl/ksctl/v2/pkg/logger"
"github.com/ksctl/ksctl/v2/pkg/provider"
"github.com/ksctl/ksctl/v2/pkg/storage"
"github.com/spf13/cobra"
)
type KsctlCommand struct {
Ctx context.Context
CliLog logger.Logger
l logger.Logger
ksctlStorage storage.Storage
root *cobra.Command
verbose int
debugMode bool
menuDriven cli.MenuDriven
KsctlConfig *config.Config
telemetry *telemetry.Telemetry
inMemInstanceTypesInReg provider.InstancesRegionOutput
}
func New() (*KsctlCommand, error) {
k := new(KsctlCommand)
k.KsctlConfig = new(config.Config)
k.Ctx = context.WithValue(
context.WithValue(
context.Background(),
consts.KsctlModuleNameKey,
"cli",
),
consts.KsctlContextUserID,
"cli",
)
k.root = k.NewRootCmd()
k.CliLog = cLogger.NewLogger(0, os.Stdout)
return k, nil
}
func (k *KsctlCommand) ForDocs() (*cobra.Command, error) {
if err := k.CommandMapping(); err != nil {
return nil, err
}
return k.root, nil
}
func (k *KsctlCommand) Execute() error {
if err := config.LoadConfig(k.KsctlConfig); err != nil {
return err
}
if err := k.CommandMapping(); err != nil {
return err
}
return k.root.Execute()
}
0707010000000C000081A40000000000000000000000016818E78D000004A8000000000000000000000000000000000000001E00000000ksctl-2.8.0/cmd/completion.gopackage cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
func (k *KsctlCommand) ShellCompletion() *cobra.Command {
cmd := &cobra.Command{
Use: "completion [bash|zsh|fish]",
Short: "Generate shell completion scripts",
Long: `To load completions:
Bash:
$ source <(ksctl completion bash)
# To load completions for each session, execute once:
# Linux:
$ ksctl completion bash > /etc/bash_completion.d/ksctl
# macOS:
$ ksctl completion bash > /usr/local/etc/bash_completion.d/ksctl
Zsh:
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
$ ksctl completion zsh > "${fpath[1]}/_ksctl"
Fish:
$ ksctl completion fish | source
# To load completions for each session, execute once:
$ ksctl completion fish > ~/.config/fish/completions/ksctl.fish
`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
switch args[0] {
case "bash":
return cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
return cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
return cmd.Root().GenFishCompletion(os.Stdout, true)
default:
return fmt.Errorf("unsupported shell: %s", args[0])
}
},
}
return cmd
}
0707010000000D000081A40000000000000000000000016818E78D00001F02000000000000000000000000000000000000001D00000000ksctl-2.8.0/cmd/configure.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"context"
"encoding/json"
"os"
"strconv"
"github.com/fatih/color"
"github.com/ksctl/cli/v2/pkg/cli"
"github.com/ksctl/cli/v2/pkg/config"
"github.com/ksctl/ksctl/v2/pkg/consts"
"github.com/ksctl/ksctl/v2/pkg/statefile"
"github.com/ksctl/ksctl/v2/pkg/utilities"
"github.com/spf13/cobra"
)
func (k *KsctlCommand) Configure() *cobra.Command {
cmd := &cobra.Command{
Use: "configure",
Short: "Configure ksctl cli",
Long: "It will display the current ksctl cli configuration",
Run: func(cmd *cobra.Command, args []string) {
headers := []string{"Property", "Value"}
enabled := color.HiCyanString("✔")
disabled := color.HiRedString("✘")
telemetry := enabled
if k.KsctlConfig.Telemetry != nil && !*k.KsctlConfig.Telemetry {
telemetry = disabled
}
rows := [][]string{
{"Storage Backend", string(k.KsctlConfig.PreferedStateStore)},
{"Telemetry", telemetry},
}
if k.KsctlConfig.PreferedStateStore == consts.StoreExtMongo {
if err := k.loadMongoCredentials(); err != nil {
rows = append(rows, []string{"MongoDB 💾", disabled})
} else {
rows = append(rows, []string{"MongoDB 💾", enabled})
}
}
if _, err := k.loadAwsCredentials(); err == nil {
rows = append(rows, []string{"AWS ☁️", enabled})
} else {
rows = append(rows, []string{"AWS ☁️", disabled})
}
if _, err := k.loadAzureCredentials(); err == nil {
rows = append(rows, []string{"Azure ☁️", enabled})
} else {
rows = append(rows, []string{"Azure ☁️", disabled})
}
k.l.Table(k.Ctx, headers, rows)
},
}
return cmd
}
func (k *KsctlCommand) ConfigureStorage() *cobra.Command {
cmd := &cobra.Command{
Use: "storage",
Short: "Configure storage",
Long: "It will help you to configure the storage",
Run: func(cmd *cobra.Command, args []string) {
if ok := k.handleStorageConfig(); !ok {
os.Exit(1)
}
},
}
return cmd
}
func (k *KsctlCommand) ConfigureCloud() *cobra.Command {
cmd := &cobra.Command{
Use: "cloud",
Short: "Configure cloud",
Long: "It will help you to configure the cloud",
Run: func(cmd *cobra.Command, args []string) {
if ok := k.handleCloudConfig(); !ok {
os.Exit(1)
}
},
}
return cmd
}
func (k *KsctlCommand) ConfigureTelemetry() *cobra.Command {
cmd := &cobra.Command{
Use: "telemetry",
Short: "Configure telemetry",
Long: "It will help you to configure the telemetry",
Run: func(cmd *cobra.Command, args []string) {
if v, err := k.menuDriven.Confirmation("Do you want to enable the telemetry?", cli.WithDefaultValue("yes")); err != nil {
k.l.Error("Failed to get the telemetry status", "Reason", err)
os.Exit(1)
} else {
k.KsctlConfig.Telemetry = utilities.Ptr(v)
if err := config.SaveConfig(k.KsctlConfig); err != nil {
k.l.Error("Failed to save the configuration", "Reason", err)
os.Exit(1)
}
}
},
}
return cmd
}
func (k *KsctlCommand) handleStorageConfig() bool {
if v, err := k.menuDriven.DropDown(
"What should be your default storageDriver?",
map[string]string{
"MongoDb": string(consts.StoreExtMongo),
"Local": string(consts.StoreLocal),
},
cli.WithDefaultValue("Local"),
); err != nil {
k.l.Error("Failed to get the storageDriver", "Reason", err)
return false
} else {
k.KsctlConfig.PreferedStateStore = consts.KsctlStore(v)
errL := config.SaveConfig(k.KsctlConfig)
if errL != nil {
k.l.Error("Failed to save the configuration", "Reason", errL)
return false
}
if consts.KsctlStore(v) == consts.StoreExtMongo {
k.l.Note(k.Ctx, "You need to provide the credentials for the MongoDB")
if err := k.storeMongoCredentials(); err != nil {
k.l.Error("Failed to store the MongoDB credentials", "Reason", err)
return false
}
}
}
return true
}
func (k *KsctlCommand) handleCloudConfig() bool {
if v, err := k.menuDriven.DropDown(
"Credentials",
map[string]string{
"Amazon Web Services": string(consts.CloudAws),
"Azure": string(consts.CloudAzure),
},
); err != nil {
k.l.Error("Failed to get the credentials", "Reason", err)
return false
} else {
switch consts.KsctlCloud(v) {
case consts.CloudAws:
if err := k.storeAwsCredentials(); err != nil {
k.l.Error("Failed to store the AWS credentials", "Reason", err)
return false
}
case consts.CloudAzure:
if err := k.storeAzureCredentials(); err != nil {
k.l.Error("Failed to store the Azure credentials", "Reason", err)
return false
}
}
}
return true
}
func (k *KsctlCommand) storeAwsCredentials() (err error) {
c := new(statefile.CredentialsAws)
c.AccessKeyId, err = k.menuDriven.TextInputPassword("Enter your AWS Access Key ID")
if err != nil {
return err
}
c.SecretAccessKey, err = k.menuDriven.TextInputPassword("Enter your AWS Secret Access Key")
if err != nil {
return err
}
return config.SaveCloudCreds(c, consts.CloudAws)
}
func (k *KsctlCommand) loadAwsCredentials() ([]byte, error) {
c := new(statefile.CredentialsAws)
if err := config.LoadCloudCreds(c, consts.CloudAws); err != nil {
return nil, err
}
v, err := json.Marshal(c)
if err != nil {
return nil, err
}
return v, nil
}
func (k *KsctlCommand) storeAzureCredentials() (err error) {
c := new(statefile.CredentialsAzure)
c.SubscriptionID, err = k.menuDriven.TextInputPassword("Enter your Azure Subscription ID")
if err != nil {
return err
}
c.TenantID, err = k.menuDriven.TextInputPassword("Enter your Azure Tenant ID")
if err != nil {
return err
}
c.ClientID, err = k.menuDriven.TextInputPassword("Enter your Azure Client ID")
if err != nil {
return err
}
c.ClientSecret, err = k.menuDriven.TextInputPassword("Enter your Azure Client Secret")
if err != nil {
return err
}
return config.SaveCloudCreds(c, consts.CloudAzure)
}
func (k *KsctlCommand) loadAzureCredentials() ([]byte, error) {
c := new(statefile.CredentialsAzure)
if err := config.LoadCloudCreds(c, consts.CloudAzure); err != nil {
return nil, err
}
v, err := json.Marshal(c)
if err != nil {
return nil, err
}
return v, nil
}
func (k *KsctlCommand) storeMongoCredentials() (err error) {
c := new(statefile.CredentialsMongodb)
srv, err := k.menuDriven.Confirmation("Enter whether MongoDB has SRV record or not", cli.WithDefaultValue("no"))
if err != nil {
return err
}
c.SRV = srv
c.Domain, err = k.menuDriven.TextInput("Enter your MongoDB URI")
if err != nil {
return err
}
c.Username, err = k.menuDriven.TextInputPassword("Enter your MongoDB Username")
if err != nil {
return err
}
c.Password, err = k.menuDriven.TextInputPassword("Enter your MongoDB Password")
if err != nil {
return err
}
port := ""
if port, err = k.menuDriven.TextInput("Enter your MongoDB Port"); err != nil {
return err
}
if len(port) != 0 {
v, err := strconv.Atoi(port)
if err != nil {
return err
}
c.Port = utilities.Ptr(v)
}
return config.SaveStorageCreds(c, consts.StoreExtMongo)
}
func (k *KsctlCommand) loadMongoCredentials() error {
c := new(statefile.CredentialsMongodb)
if err := config.LoadStorageCreds(c, consts.StoreExtMongo); err != nil {
return err
}
v, err := json.Marshal(c)
if err != nil {
return err
}
k.Ctx = context.WithValue(k.Ctx, consts.KsctlMongodbCredentials, v)
return nil
}
0707010000000E000081A40000000000000000000000016818E78D00001C81000000000000000000000000000000000000001B00000000ksctl-2.8.0/cmd/connect.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"fmt"
"io"
"os"
"os/exec"
"os/signal"
"path/filepath"
"strconv"
"strings"
"syscall"
"github.com/creack/pty"
"github.com/fatih/color"
"github.com/ksctl/cli/v2/pkg/cli"
"github.com/ksctl/cli/v2/pkg/telemetry"
"github.com/ksctl/ksctl/v2/pkg/handler/cluster/common"
"github.com/ksctl/ksctl/v2/pkg/handler/cluster/controller"
"github.com/ksctl/ksctl/v2/pkg/logger"
"github.com/spf13/cobra"
"golang.org/x/term"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)
func (k *KsctlCommand) Connect() *cobra.Command {
cmd := &cobra.Command{
Use: "connect",
Example: `
ksctl connect --help
`,
Short: "Connect to existing cluster",
Long: "It is used to connect to existing cluster",
Run: func(cmd *cobra.Command, args []string) {
clusters, err := k.fetchAllClusters()
if err != nil {
k.l.Error("Error in fetching the clusters", "Error", err)
os.Exit(1)
}
if len(clusters) == 0 {
k.l.Error("No clusters found to connect")
os.Exit(1)
}
selectDisplay := make(map[string]string, len(clusters))
valueMaping := make(map[string]controller.Metadata, len(clusters))
for idx, cluster := range clusters {
selectDisplay[makeHumanReadableList(cluster)] = strconv.Itoa(idx)
valueMaping[strconv.Itoa(idx)] = controller.Metadata{
ClusterName: cluster.Name,
ClusterType: cluster.ClusterType,
Provider: cluster.CloudProvider,
Region: cluster.Region,
StateLocation: k.KsctlConfig.PreferedStateStore,
K8sDistro: cluster.K8sDistro,
K8sVersion: cluster.K8sVersion,
}
}
selectedCluster, err := k.menuDriven.DropDown(
"Select the cluster to delete",
selectDisplay,
)
if err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
os.Exit(1)
}
m := valueMaping[selectedCluster]
if err := k.telemetry.Send(k.Ctx, k.l, telemetry.EventClusterConnect, telemetry.TelemetryMeta{
CloudProvider: m.Provider,
StorageDriver: m.StateLocation,
Region: m.Region,
ClusterType: m.ClusterType,
BootstrapProvider: m.K8sDistro,
K8sVersion: m.K8sVersion,
Addons: telemetry.TranslateMetadata(m.Addons),
}); err != nil {
k.l.Debug(k.Ctx, "Failed to send the telemetry", "Reason", err)
}
if k.loadCloudProviderCreds(m.Provider) != nil {
os.Exit(1)
}
c, err := common.NewController(
k.Ctx,
k.l,
&controller.Client{
Metadata: m,
},
)
if err != nil {
k.l.Error("Failed to create the controller", "Reason", err)
os.Exit(1)
}
kubeconfig, err := c.Switch()
if err != nil {
k.l.Error("Failed to connect to the cluster", "Reason", err)
os.Exit(1)
}
k.l.Note(k.Ctx, "Downloaded the kubeconfig")
k.writeKubeconfig([]byte(*kubeconfig))
accessMode, err := k.menuDriven.DropDown(
"Select the access mode",
map[string]string{
"k9s": "k9s",
"bash": "shell",
"none": "none",
},
cli.WithDefaultValue("none"),
)
if err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
os.Exit(1)
}
if accessMode == "k9s" {
K9sAccess(k.l)
} else if accessMode == "shell" {
shellAccess(k.l)
} else {
k.l.Box(k.Ctx, "Kubeconfig", "You can access the cluster using $ kubectl commands or any other k8s client as its saved to ~/.kube/config")
}
},
}
return cmd
}
func shellAccess(log logger.Logger) {
home, err := os.UserHomeDir()
if err != nil {
log.Error("Failed to get home dir", "Reason", err)
os.Exit(1)
}
home = filepath.Join(home, ".kube", "config")
cmd := exec.Command("/bin/bash")
cmd.Env = append(os.Environ(), "KUBECONFIG="+home)
ptmx, err := pty.Start(cmd)
if err != nil {
fmt.Println("Error creating pseudo-terminal:", err)
return
}
defer func() { _ = ptmx.Close() }()
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGWINCH)
go func() {
for range ch {
if err := pty.InheritSize(os.Stdin, ptmx); err != nil {
fmt.Println("Error resizing pty:", err)
}
}
}()
ch <- syscall.SIGWINCH
oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
fmt.Println("Error setting raw mode:", err)
return
}
defer func() { _ = term.Restore(int(os.Stdin.Fd()), oldState) }()
fmt.Fprintln(ptmx, "echo Hi from Ksctl team! You are now in the shell session having cluster context.")
fmt.Fprintln(ptmx, "kubectl get nodes -owide && kubectl cluster-info")
go func() { _, _ = io.Copy(ptmx, os.Stdin) }()
_, _ = io.Copy(os.Stdout, ptmx)
}
func K9sAccess(log logger.Logger) {
// home = filepath.Join(home, ".ksctl", "kubeconfig")
// _cmd := exec.Command("k9s", "--kubeconfig", home)
_cmd := exec.Command("k9s")
_bout := new(strings.Builder)
_berr := new(strings.Builder)
_cmd.Stdout = _bout
_cmd.Stderr = _berr
if err := _cmd.Run(); err != nil {
log.Error("Failed to run k9s", "Reason", err)
}
_stdout, _stderr := _bout.String(), _berr.String()
fmt.Println(color.HiBlueString(_stdout))
fmt.Println(color.HiRedString(_stderr))
}
func mergeKubeConfigs(configs ...*clientcmdapi.Config) *clientcmdapi.Config {
merged := clientcmdapi.NewConfig()
for _, cfg := range configs {
for name, cluster := range cfg.Clusters {
merged.Clusters[name] = cluster
}
for name, authInfo := range cfg.AuthInfos {
merged.AuthInfos[name] = authInfo
}
for name, context := range cfg.Contexts {
merged.Contexts[name] = context
}
if cfg.CurrentContext != "" {
merged.CurrentContext = cfg.CurrentContext
}
}
return merged
}
func (k *KsctlCommand) writeKubeconfig(newKubeconfig []byte) {
home, err := os.UserHomeDir()
if err != nil {
k.l.Error("Failed to get the home directory", "Reason", err)
os.Exit(1)
}
orgConfig, err := os.ReadFile(filepath.Join(home, ".kube", "config"))
if err != nil {
k.l.Error("Failed to read the kubeconfig", "Reason", err)
os.Exit(1)
}
config1, err := clientcmd.Load(orgConfig)
if err != nil {
k.l.Error("Failed to load the kubeconfig in ~/.kube/config", "Reason", err)
os.Exit(1)
}
config2, err := clientcmd.Load(newKubeconfig)
if err != nil {
k.l.Error("Failed to load the new kubeconfig", "Reason", err)
os.Exit(1)
}
mergedConfig := mergeKubeConfigs(config1, config2)
mergedConfig.CurrentContext = config2.CurrentContext
mergedYAML, err := clientcmd.Write(*mergedConfig)
if err != nil {
k.l.Error("Failed to write the merged kubeconfig", "Reason", err)
os.Exit(1)
}
if err := os.WriteFile(filepath.Join(home, ".kube", "config"), mergedYAML, 0640); err != nil {
k.l.Error("Failed to write the kubeconfig", "Reason", err)
os.Exit(1)
}
}
0707010000000F000081A40000000000000000000000016818E78D00003098000000000000000000000000000000000000001A00000000ksctl-2.8.0/cmd/create.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"os"
"time"
"github.com/ksctl/ksctl/v2/pkg/provider/optimizer"
"github.com/fatih/color"
"github.com/ksctl/ksctl/v2/pkg/consts"
"github.com/ksctl/ksctl/v2/pkg/provider"
"github.com/ksctl/cli/v2/pkg/cli"
"github.com/ksctl/cli/v2/pkg/telemetry"
"github.com/ksctl/ksctl/v2/pkg/handler/cluster/controller"
controllerManaged "github.com/ksctl/ksctl/v2/pkg/handler/cluster/managed"
controllerMeta "github.com/ksctl/ksctl/v2/pkg/handler/cluster/metadata"
controllerSelfManaged "github.com/ksctl/ksctl/v2/pkg/handler/cluster/selfmanaged"
"github.com/spf13/cobra"
)
func (k *KsctlCommand) Create() *cobra.Command {
cmd := &cobra.Command{
Use: "create",
Example: `
ksctl create --help
`,
Short: "Use to create a cluster",
Long: "It is used to create cluster with the given name from user",
Run: func(cmd *cobra.Command, args []string) {
meta := controller.Metadata{}
k.baseMetadataFields(&meta)
if meta.ClusterType == consts.ClusterTypeMang {
k.metadataForManagedCluster(&meta)
} else {
k.metadataForSelfManagedCluster(&meta)
}
k.l.Success(k.Ctx, "Created the cluster", "Name", meta.ClusterName)
},
}
return cmd
}
type CliRecommendation struct {
isOptimizeInstanceRegionReady *optimizer.RecommendationAcrossRegions
errInRecommendation error
}
func (k *KsctlCommand) CostOptimizeAcrossRegion(inp chan CliRecommendation, meta *controller.Metadata) {
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()
for {
select {
case o := <-inp:
optimizeResp, errResp := o.isOptimizeInstanceRegionReady, o.errInRecommendation
if errResp != nil {
k.l.Warn(k.Ctx, "Failed to get the recommendation", "Reason", errResp)
return
}
if len(optimizeResp.RegionRecommendations) == 0 {
k.l.Success(k.Ctx, "✨ No recommendation available for the selected region")
return
}
selectedReg, err := k.menuDriven.CardSelection(
cli.ConverterForRecommendationIOutputForCards(optimizeResp, meta.ClusterType),
)
if err != nil {
k.l.Error("Failed to get the recommendation options from user", "Reason", err)
return
}
if selectedReg != "" {
k.l.Print(k.Ctx, "changed the region", "from", color.HiRedString(meta.Region), "to", color.HiGreenString(selectedReg))
meta.Region = selectedReg
}
return
case <-ticker.C:
k.l.Print(k.Ctx, "Still optimizing instance types...")
}
}
}
func (k *KsctlCommand) metadataForSelfManagedCluster(meta *controller.Metadata) {
metaClient, err := controllerMeta.NewController(
k.Ctx,
k.l,
&controller.Client{
Metadata: *meta,
},
)
if err != nil {
k.l.Error("Failed to create the controller", "Reason", err)
os.Exit(1)
}
allAvailRegions := k.handleRegionSelection(metaClient, meta)
cp := k.handleInstanceTypeSelection(metaClient, meta, provider.ComputeIntensive, "Select instance_type for Control Plane")
etcd := k.handleInstanceTypeSelection(metaClient, meta, provider.MemoryIntensive, "Select instance_type for Etcd Nodes")
lb := k.handleInstanceTypeSelection(metaClient, meta, provider.GeneralPurpose, "Select instance_type for Load Balancer")
category := provider.Unknown
if meta.Provider != consts.CloudLocal {
category = k.handleInstanceCategorySelection()
}
wp := k.handleInstanceTypeSelection(metaClient, meta, category, "Select instance_type for Worker Nodes")
meta.ControlPlaneNodeType = cp.Sku
meta.WorkerPlaneNodeType = wp.Sku
meta.DataStoreNodeType = etcd.Sku
meta.LoadBalancerNodeType = lb.Sku
if v, ok := k.getCounterValue("Enter the number of Control Plane Nodes", func(v int) bool {
return v >= 3
}, 3); !ok {
k.l.Error("Failed to get the number of control plane nodes")
os.Exit(1)
} else {
meta.NoCP = v
}
if v, ok := k.getCounterValue("Enter the number of Worker Nodes", func(v int) bool {
return v > 0
}, 1); !ok {
k.l.Error("Failed to get the number of worker nodes")
os.Exit(1)
} else {
meta.NoWP = v
}
if v, ok := k.getCounterValue("Enter the number of Etcd Nodes", func(v int) bool {
return v >= 3
}, 3); !ok {
k.l.Error("Failed to get the number of etcd nodes")
os.Exit(1)
} else {
meta.NoDS = v
}
var (
isOptimizeInstanceRegionReady chan CliRecommendation
)
isOptimizeInstanceRegionReady = make(chan CliRecommendation)
go func() {
res, err := metaClient.CostOptimizeAcrossRegions(
allAvailRegions, meta.Region,
controllerMeta.CostOptimizerInput{
ControlPlane: cp,
WorkerPlane: wp,
DataStorePlane: etcd,
LoadBalancer: lb,
CountOfControlPlaneNodes: meta.NoCP,
CountOfWorkerNodes: meta.NoWP,
CountOfEtcdNodes: meta.NoDS,
},
)
isOptimizeInstanceRegionReady <- CliRecommendation{
isOptimizeInstanceRegionReady: res,
errInRecommendation: err,
}
}()
bootstrapVers, err := metaClient.ListAllBootstrapVersions()
if err != nil {
k.l.Error("Failed to get the list of bootstrap versions", "Reason", err)
os.Exit(1)
}
if v, err := k.menuDriven.DropDownList("Select the bootstrap version", bootstrapVers, cli.WithDefaultValue(bootstrapVers[0])); err != nil {
k.l.Error("Failed to get the bootstrap version", "Reason", err)
os.Exit(1)
} else {
k.l.Debug(k.Ctx, "Selected bootstrap version", "Version", v)
meta.K8sVersion = v
}
etcdVers, err := metaClient.ListAllEtcdVersions()
if err != nil {
k.l.Error("Failed to get the list of etcd versions", "Reason", err)
os.Exit(1)
}
if v, err := k.menuDriven.DropDownList("Select the etcd version", etcdVers, cli.WithDefaultValue(etcdVers[0])); err != nil {
k.l.Error("Failed to get the etcd version", "Reason", err)
os.Exit(1)
} else {
k.l.Debug(k.Ctx, "Selected etcd version", "Version", v)
meta.EtcdVersion = v
}
k.l.Print(k.Ctx, "Current Selection will cost you")
_, err = metaClient.PriceCalculator(
controllerMeta.PriceCalculatorInput{
Currency: cp.Price.Currency,
NoOfWorkerNodes: meta.NoWP,
NoOfControlPlaneNodes: meta.NoCP,
NoOfEtcdNodes: meta.NoDS,
ControlPlaneMachine: cp,
WorkerMachine: wp,
EtcdMachine: etcd,
LoadBalancerMachine: lb,
})
if err != nil {
k.l.Error("Failed to calculate the price", "Reason", err)
os.Exit(1)
}
k.CostOptimizeAcrossRegion(isOptimizeInstanceRegionReady, meta)
managedCNI, defaultCNI, ksctlCNI, defaultKsctl, err := metaClient.ListBootstrapCNIs()
if err != nil {
k.l.Error("Failed to get the list of self managed CNIs", "Reason", err)
os.Exit(1)
}
v, err := k.handleCNI(metaClient, managedCNI, defaultCNI, ksctlCNI, defaultKsctl)
if err != nil {
k.l.Error("Failed to get the CNI", "Reason", err)
os.Exit(1)
}
meta.Addons = v
k.metadataSummary(*meta)
if err := k.telemetry.Send(k.Ctx, k.l, telemetry.EventClusterCreate, telemetry.TelemetryMeta{
CloudProvider: meta.Provider,
StorageDriver: meta.StateLocation,
Region: meta.Region,
ClusterType: meta.ClusterType,
BootstrapProvider: meta.K8sDistro,
K8sVersion: meta.K8sVersion,
Addons: telemetry.TranslateMetadata(meta.Addons),
}); err != nil {
k.l.Debug(k.Ctx, "Failed to send the telemetry", "Reason", err)
}
if ok, _ := k.menuDriven.Confirmation("Do you want to proceed with the cluster creation", cli.WithDefaultValue("no")); !ok {
os.Exit(1)
}
c, err := controllerSelfManaged.NewController(
k.Ctx,
k.l,
&controller.Client{
Metadata: *meta,
},
)
if err != nil {
k.l.Error("Failed to create the controller", "Reason", err)
os.Exit(1)
}
if err := c.Create(); err != nil {
k.l.Error("Failed to create the cluster", "Reason", err)
os.Exit(1)
}
return
}
func (k *KsctlCommand) metadataForManagedCluster(meta *controller.Metadata) {
metaClient, err := controllerMeta.NewController(
k.Ctx,
k.l,
&controller.Client{
Metadata: *meta,
},
)
if err != nil {
k.l.Error("Failed to create the controller", "Reason", err)
os.Exit(1)
}
if v, ok := k.getCounterValue("Enter the number of Managed Nodes", func(v int) bool {
return v > 0
}, 1); !ok {
k.l.Error("Failed to get the number of managed nodes")
os.Exit(1)
} else {
meta.NoMP = v
}
var (
isOptimizeInstanceRegionReady chan CliRecommendation
)
if meta.Provider != consts.CloudLocal {
allAvailRegions := k.handleRegionSelection(metaClient, meta)
category := provider.Unknown
if meta.Provider != consts.CloudLocal {
category = k.handleInstanceCategorySelection()
}
vm := k.handleInstanceTypeSelection(metaClient, meta, category, "Select instance_type for Managed Nodes")
meta.ManagedNodeType = vm.Sku
k.menuDriven.GetProgressAnimation().Start("Fetching the managed cluster offerings")
listOfOfferings, err := metaClient.ListAllManagedClusterManagementOfferings(meta.Region, nil)
if err != nil {
k.menuDriven.GetProgressAnimation().Stop()
k.l.Error("Failed to sync the metadata", "Reason", err)
os.Exit(1)
}
k.menuDriven.GetProgressAnimation().Stop()
offeringSelected := ""
if v, ok := k.getSelectedManagedClusterOffering("Select the managed cluster offering", listOfOfferings); !ok {
k.l.Error("Failed to get the managed cluster offering")
os.Exit(1)
} else {
offeringSelected = v
}
isOptimizeInstanceRegionReady = make(chan CliRecommendation)
go func() {
res, err := metaClient.CostOptimizeAcrossRegions(
allAvailRegions, meta.Region,
controllerMeta.CostOptimizerInput{
ManagedOffering: listOfOfferings[offeringSelected],
ManagedPlane: vm,
CountOfManagedNodes: meta.NoMP,
},
)
isOptimizeInstanceRegionReady <- CliRecommendation{
isOptimizeInstanceRegionReady: res,
errInRecommendation: err,
}
}()
k.l.Print(k.Ctx, "Current Selection will cost you")
_, err = metaClient.PriceCalculator(
controllerMeta.PriceCalculatorInput{
ManagedControlPlaneMachine: listOfOfferings[offeringSelected],
NoOfWorkerNodes: meta.NoMP,
WorkerMachine: vm,
})
if err != nil {
k.l.Error("Failed to calculate the price", "Reason", err)
os.Exit(1)
}
k.CostOptimizeAcrossRegion(isOptimizeInstanceRegionReady, meta)
}
managedCNI, defaultCNI, ksctlCNI, defaultKsctl, err := metaClient.ListManagedCNIs()
if err != nil {
k.l.Error("Failed to get the list of managed CNIs", "Reason", err)
os.Exit(1)
}
if v, err := k.handleCNI(metaClient, managedCNI, defaultCNI, ksctlCNI, defaultKsctl); err != nil {
k.l.Error("Failed to get the CNI", "Reason", err)
os.Exit(1)
} else {
meta.Addons = v
}
k.handleManagedK8sVersion(metaClient, meta)
k.metadataSummary(*meta)
if err := k.telemetry.Send(k.Ctx, k.l, telemetry.EventClusterCreate, telemetry.TelemetryMeta{
CloudProvider: meta.Provider,
StorageDriver: meta.StateLocation,
Region: meta.Region,
ClusterType: meta.ClusterType,
BootstrapProvider: func() consts.KsctlKubernetes {
switch meta.Provider {
case consts.CloudLocal:
return consts.K8sKind
case consts.CloudAzure:
return consts.K8sAks
case consts.CloudAws:
return consts.K8sEks
default:
return ""
}
}(),
K8sVersion: meta.K8sVersion,
Addons: telemetry.TranslateMetadata(meta.Addons),
}); err != nil {
k.l.Debug(k.Ctx, "Failed to send the telemetry", "Reason", err)
}
if ok, _ := k.menuDriven.Confirmation("Do you want to proceed with the cluster creation", cli.WithDefaultValue("no")); !ok {
os.Exit(1)
}
c, err := controllerManaged.NewController(
k.Ctx,
k.l,
&controller.Client{
Metadata: *meta,
},
)
if err != nil {
k.l.Error("Failed to create the controller", "Reason", err)
os.Exit(1)
}
if err := c.Create(); err != nil {
k.l.Error("Failed to create the cluster", "Reason", err)
os.Exit(1)
}
return
}
07070100000010000081A40000000000000000000000016818E78D000010C3000000000000000000000000000000000000001A00000000ksctl-2.8.0/cmd/delete.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"fmt"
"os"
"strconv"
"strings"
"github.com/ksctl/cli/v2/pkg/cli"
"github.com/ksctl/cli/v2/pkg/telemetry"
"github.com/ksctl/ksctl/v2/pkg/consts"
"github.com/ksctl/ksctl/v2/pkg/handler/cluster/controller"
"github.com/ksctl/ksctl/v2/pkg/handler/cluster/managed"
"github.com/ksctl/ksctl/v2/pkg/handler/cluster/selfmanaged"
"github.com/ksctl/ksctl/v2/pkg/provider"
"github.com/spf13/cobra"
)
func (k *KsctlCommand) Delete() *cobra.Command {
cmd := &cobra.Command{
Use: "delete",
Example: `
ksctl delete --help
`,
Short: "Use to delete a cluster",
Long: "It is used to delete cluster with the given name from user",
Run: func(cmd *cobra.Command, args []string) {
clusters, err := k.fetchAllClusters()
if err != nil {
k.l.Error("Error in fetching the clusters", "Error", err)
os.Exit(1)
}
if len(clusters) == 0 {
k.l.Error("No clusters found to delete")
os.Exit(1)
}
selectDisplay := make(map[string]string, len(clusters))
valueMaping := make(map[string]controller.Metadata, len(clusters))
for idx, cluster := range clusters {
selectDisplay[makeHumanReadableList(cluster)] = strconv.Itoa(idx)
valueMaping[strconv.Itoa(idx)] = controller.Metadata{
ClusterName: cluster.Name,
ClusterType: cluster.ClusterType,
Provider: cluster.CloudProvider,
Region: cluster.Region,
StateLocation: k.KsctlConfig.PreferedStateStore,
K8sDistro: cluster.K8sDistro,
}
}
selectedCluster, err := k.menuDriven.DropDown(
"Select the cluster to delete",
selectDisplay,
)
if err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
os.Exit(1)
}
m := valueMaping[selectedCluster]
if err := k.telemetry.Send(k.Ctx, k.l, telemetry.EventClusterDelete, telemetry.TelemetryMeta{
CloudProvider: m.Provider,
StorageDriver: m.StateLocation,
Region: m.Region,
ClusterType: m.ClusterType,
BootstrapProvider: m.K8sDistro,
K8sVersion: m.K8sVersion,
Addons: telemetry.TranslateMetadata(m.Addons),
}); err != nil {
k.l.Debug(k.Ctx, "Failed to send the telemetry", "Reason", err)
}
if ok, _ := k.menuDriven.Confirmation("Do you want to proceed with the cluster deletion", cli.WithDefaultValue("no")); !ok {
os.Exit(1)
}
if k.loadCloudProviderCreds(m.Provider) != nil {
os.Exit(1)
}
if m.ClusterType == consts.ClusterTypeMang {
c, err := managed.NewController(
k.Ctx,
k.l,
&controller.Client{
Metadata: m,
},
)
if err != nil {
k.l.Error("Failed to create the controller", "Reason", err)
os.Exit(1)
}
if err := c.Delete(); err != nil {
k.l.Error("Failed to delete your managed cluster", "Reason", err)
os.Exit(1)
}
} else {
c, err := selfmanaged.NewController(
k.Ctx,
k.l,
&controller.Client{
Metadata: m,
},
)
if err != nil {
k.l.Error("Failed to create the controller", "Reason", err)
os.Exit(1)
}
if err := c.Delete(); err != nil {
k.l.Error("Failed to delete your selfmanaged cluster", "Reason", err)
os.Exit(1)
}
}
k.l.Success(k.Ctx, "Deleted your cluster", "Name", m.ClusterName)
},
}
return cmd
}
func makeHumanReadableList(m provider.ClusterData) string {
fields := []string{"%s", "[%s]", "=>"}
args := []any{m.Name, m.ClusterType}
if m.CloudProvider == consts.CloudLocal {
fields = append(fields, "local")
} else {
fields = append(fields, "%s ⟨%s⟩")
args = append(args, m.CloudProvider, m.Region)
}
return fmt.Sprintf(strings.Join(fields, " "), args...)
}
07070100000011000081A40000000000000000000000016818E78D00000F21000000000000000000000000000000000000001700000000ksctl-2.8.0/cmd/get.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"context"
"os"
"strconv"
"strings"
"github.com/ksctl/cli/v2/pkg/telemetry"
"github.com/ksctl/ksctl/v2/pkg/consts"
"github.com/ksctl/ksctl/v2/pkg/logger"
"github.com/ksctl/ksctl/v2/pkg/provider"
"github.com/spf13/cobra"
)
func (k *KsctlCommand) Get() *cobra.Command {
cmd := &cobra.Command{
Use: "get",
Example: `
ksctl get --help
`,
Short: "Use to get the cluster",
Long: "It is used to get the cluster created by the user",
Run: func(cmd *cobra.Command, args []string) {
clusters, err := k.fetchAllClusters()
if err != nil {
k.l.Error("Error in fetching the clusters", "Error", err)
os.Exit(1)
}
if len(clusters) == 0 {
k.l.Print(k.Ctx, "No clusters found")
os.Exit(1)
}
selectDisplay := make(map[string]string, len(clusters))
valueMaping := make(map[string]provider.ClusterData, len(clusters))
for idx, cluster := range clusters {
selectDisplay[makeHumanReadableList(cluster)] = strconv.Itoa(idx)
valueMaping[strconv.Itoa(idx)] = cluster
}
selectedCluster, err := k.menuDriven.DropDown(
"Select the cluster to delete",
selectDisplay,
)
if err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
os.Exit(1)
}
cluster := valueMaping[selectedCluster]
if err := k.telemetry.Send(k.Ctx, k.l, telemetry.EventClusterGet, telemetry.TelemetryMeta{
CloudProvider: cluster.CloudProvider,
StorageDriver: k.KsctlConfig.PreferedStateStore,
Region: cluster.Region,
ClusterType: cluster.ClusterType,
BootstrapProvider: cluster.K8sDistro,
K8sVersion: cluster.K8sVersion,
}); err != nil {
k.l.Debug(k.Ctx, "Failed to send the telemetry", "Reason", err)
}
handleTableOutputGet(k.Ctx, k.l, cluster)
},
}
return cmd
}
func handleTableOutputGet(ctx context.Context, l logger.Logger, data provider.ClusterData) {
headers := []string{"Attributes", "Values"}
dataToPrint := [][]string{
{"ClusterName", data.Name},
{"CloudProvider", string(data.CloudProvider)},
{"ClusterType", string(data.ClusterType)},
}
if data.CloudProvider != consts.CloudLocal {
dataToPrint = append(dataToPrint,
[]string{"Region", data.Region},
)
}
if data.ClusterType == consts.ClusterTypeSelfMang {
nodes := func(vm []provider.VMData) string {
slice := make([]string, 0, len(vm))
for _, v := range vm {
slice = append(slice, v.VMSize)
}
return strings.Join(slice, ",")
}
dataToPrint = append(
dataToPrint,
[]string{"BootstrapProvider", string(data.K8sDistro)},
[]string{"BootstrapKubernetesVersion", data.K8sVersion},
[]string{"ControlPlaneNodes", nodes(data.CP)},
[]string{"WorkerPlaneNodes", nodes(data.WP)},
[]string{"EtcdNodes", nodes(data.DS)},
[]string{"LoadBalancer", data.LB.VMSize},
[]string{"EtcdVersion", data.EtcdVersion},
[]string{"HaProxyVersion", data.HAProxyVersion},
)
} else {
dataToPrint = append(
dataToPrint,
[]string{"ManagedNodes", strconv.Itoa(data.NoMgt) + " X " + data.Mgt.VMSize},
[]string{"ManagedK8sVersion", data.K8sVersion},
)
}
dataToPrint = append(dataToPrint,
[]string{"Addons", strings.Join(data.Apps, ",")},
[]string{"CNI", data.Cni},
)
l.Table(ctx, headers, dataToPrint)
}
07070100000012000081A40000000000000000000000016818E78D00002C16000000000000000000000000000000000000001F00000000ksctl-2.8.0/cmd/handle_meta.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"github.com/ksctl/cli/v2/pkg/cli"
"github.com/ksctl/ksctl/v2/pkg/addons"
"github.com/ksctl/ksctl/v2/pkg/bootstrap/handler/cni"
"github.com/ksctl/ksctl/v2/pkg/consts"
"github.com/ksctl/ksctl/v2/pkg/errors"
"github.com/ksctl/ksctl/v2/pkg/handler/cluster/controller"
controllerMeta "github.com/ksctl/ksctl/v2/pkg/handler/cluster/metadata"
"github.com/ksctl/ksctl/v2/pkg/provider"
"github.com/ksctl/ksctl/v2/pkg/utilities"
"gopkg.in/yaml.v3"
)
func (k *KsctlCommand) baseMetadataFields(m *controller.Metadata) {
if v, ok := k.getClusterName(); !ok {
os.Exit(1)
} else {
m.ClusterName = v
}
if v, ok := k.getSelectedClusterType(); !ok {
os.Exit(1)
} else {
m.ClusterType = v
}
if v, ok := k.getSelectedCloudProvider(m.ClusterType); !ok {
os.Exit(1)
} else {
m.Provider = v
}
if v, ok := k.getSelectedStorageDriver(); !ok {
os.Exit(1)
} else {
m.StateLocation = consts.KsctlStore(v)
}
if m.ClusterType == consts.ClusterTypeSelfMang {
if v, ok := k.getBootstrap(); ok {
m.K8sDistro = v
} else {
os.Exit(1)
}
}
}
func (k *KsctlCommand) handleRegionSelection(meta *controllerMeta.Controller, m *controller.Metadata) []provider.RegionOutput {
ss := k.menuDriven.GetProgressAnimation()
ss.Start("Fetching the region list")
listOfRegions, err := meta.ListAllRegions()
if err != nil {
ss.Stop()
k.l.Error("Failed to sync the metadata", "Reason", err)
os.Exit(1)
}
ss.Stop()
k.l.Note(k.Ctx, "Carbon emission data shown represents monthly averages calculated over a one-year period")
k.l.Note(k.Ctx, "Select the region for the cluster")
if v, err := k.menuDriven.CardSelection(
cli.ConverterForRegionOutputForCards(listOfRegions),
); err != nil {
k.l.Error("Failed to get the region", "Reason", err)
os.Exit(1)
} else {
if v == "" {
k.l.Error("Region not selected")
os.Exit(1)
}
k.l.Debug(k.Ctx, "Selected region", "Region", v)
m.Region = v
}
return listOfRegions
}
func (k *KsctlCommand) handleInstanceCategorySelection() provider.MachineCategory {
v := provider.GetAvailableMachineCategories()
_v, ok := k.getSelectedInstanceCategory(v)
if !ok {
k.l.Error("Failed to get the instance category")
os.Exit(1)
}
return _v
}
func (k *KsctlCommand) handleInstanceTypeSelection(
meta *controllerMeta.Controller,
m *controller.Metadata,
category provider.MachineCategory,
prompt string,
) provider.InstanceRegionOutput {
if len(k.inMemInstanceTypesInReg) == 0 {
if len(category) == 0 {
k.l.Error("Machine category is not provided")
os.Exit(1)
}
ss := k.menuDriven.GetProgressAnimation()
ss.Start("Fetching the instance type list")
listOfVMs, err := meta.ListAllInstances(m.Region)
if err != nil {
ss.Stop()
k.l.Error("Failed to sync the metadata", "Reason", err)
os.Exit(1)
}
ss.Stop()
k.inMemInstanceTypesInReg = listOfVMs
}
availableOptions := make(provider.InstancesRegionOutput, 0, len(k.inMemInstanceTypesInReg))
k.l.Note(k.Ctx, prompt)
for _, v := range k.inMemInstanceTypesInReg {
if v.Category == category && v.CpuArch == provider.ArchAmd64 {
availableOptions = append(availableOptions, v)
}
}
v, err := k.menuDriven.CardSelection(
cli.ConverterForInstanceTypesForCards(availableOptions),
)
if err != nil {
k.l.Error("Failed to get the instance type from user", "Reason", err)
os.Exit(1)
}
if v == "" {
k.l.Error("Instance type not selected")
os.Exit(1)
}
_v, ok := availableOptions.Get(v)
if !ok {
k.l.Error("Failed to get the instance type")
os.Exit(1)
}
return *_v
}
func (k *KsctlCommand) getSpecificInstanceForScaledown(
meta *controllerMeta.Controller,
region string,
instanceSku string,
) provider.InstanceRegionOutput {
if len(k.inMemInstanceTypesInReg) == 0 {
ss := k.menuDriven.GetProgressAnimation()
ss.Start("Fetching the instance type list")
listOfVMs, err := meta.ListAllInstances(region)
if err != nil {
ss.Stop()
k.l.Error("Failed to sync the metadata", "Reason", err)
os.Exit(1)
}
ss.Stop()
k.inMemInstanceTypesInReg = listOfVMs
}
v, ok := k.inMemInstanceTypesInReg.Get(instanceSku)
if !ok {
k.l.Error("Failed to get the instance type")
os.Exit(1)
}
return *v
}
func (k *KsctlCommand) handleManagedK8sVersion(meta *controllerMeta.Controller, m *controller.Metadata) {
ss := k.menuDriven.GetProgressAnimation()
ss.Start("Fetching the managed cluster k8s versions")
listOfK8sVersions, err := meta.ListAllManagedClusterK8sVersions(m.Region)
if err != nil {
ss.Stop()
k.l.Error("Failed to sync the metadata", "Reason", err)
os.Exit(1)
}
ss.Stop()
if v, ok := k.getSelectedK8sVersion("Select the k8s version for Managed Cluster", listOfK8sVersions); !ok {
k.l.Error("Failed to get the k8s version")
os.Exit(1)
} else {
m.K8sVersion = v
}
}
func (k *KsctlCommand) metadataSummary(meta controller.Metadata) {
// Use the new interactive cluster summary
cli.NewBlueprintUI(os.Stdout).RenderClusterBlueprint(meta)
}
func (k *KsctlCommand) handleCNI(metaClient *controllerMeta.Controller, managedCNI addons.ClusterAddons, defaultOptionManaged string, ksctlCNI addons.ClusterAddons, defaultOptionKsctl string) (addons.ClusterAddons, error) {
var v addons.ClusterAddons
handleInput := func(
vc addons.ClusterAddons,
prompt string,
defaultOpt string,
errorPrompt string,
) (addons.ClusterAddon, error) {
cc := map[string]string{}
cm := map[string]addons.ClusterAddon{}
for _, c := range vc {
cc[fmt.Sprintf("%s (From: %s)", c.Name, c.Label)] = c.Name
cm[c.Name] = c
}
selected, err := k.menuDriven.DropDown(
prompt,
cc,
cli.WithDefaultValue(defaultOpt),
)
if err != nil {
return addons.ClusterAddon{}, errors.WrapError(
errors.ErrInvalidUserInput,
k.l.NewError(k.Ctx, errorPrompt, "Reason", err),
)
}
return cm[selected], nil
}
_v0, err := handleInput(managedCNI, "Select the CNI addon provided by offering", defaultOptionManaged, "Failed to get the CNI addon provided by managed offering")
if err != nil {
return nil, err
}
v = append(v, _v0)
if _v0.Name != string(consts.CNINone) {
return v, nil
}
_v1, err := handleInput(ksctlCNI, "Select the CNI addon provided by ksctl", defaultOptionKsctl, "Failed to get the CNI addon provided by ksctl")
if err != nil {
return nil, err
}
ss := k.menuDriven.GetProgressAnimation()
ss.Start("Fetching the CNI version list")
config := make(map[string]map[string]any)
if _v1.Name == string(consts.CNIFlannel) {
vers, errF := metaClient.ListAllFlannelVersions()
ss.Stop()
if errF != nil { // Skip further processing if error
k.l.Warn(k.Ctx, "Failed to get the Flannel version list", "Reason", errF)
} else {
if v, err := k.menuDriven.DropDownList("Select the flannel version", vers, cli.WithDefaultValue(vers[0])); err != nil {
k.l.Error("Failed to get the flannel version", "Reason", err)
} else {
k.l.Debug(k.Ctx, "Selected flannel version", "Version", v)
config[string(cni.FlannelComponentID)] = map[string]any{
"version": v,
}
}
}
}
if _v1.Name == string(consts.CNICilium) {
vers, errC := metaClient.ListAllCiliumVersions()
ss.Stop()
if errC != nil { // Skip further processing if error
k.l.Warn(k.Ctx, "Failed to get the Cilium version list", "Reason", errC)
} else {
if v, err := k.menuDriven.DropDownList("Select the cilium version", vers, cli.WithDefaultValue(vers[0])); err != nil {
k.l.Error("Failed to get the cilium version", "Reason", err)
} else {
k.l.Debug(k.Ctx, "Selected cilium version", "Version", v)
config[string(cni.CiliumComponentID)] = map[string]any{
"version": v,
}
}
}
// Get the Cilium Specific options
// where 2 modes are there one if guided and another is advance
ciliumMode, err := k.menuDriven.DropDownList("Select the cilium mode", []string{"guided", "advanced", "ksctl default"}, cli.WithDefaultValue("ksctl default"))
if err != nil {
return nil, errors.WrapError(
errors.ErrInvalidUserInput,
k.l.NewError(k.Ctx, "Failed to get the cilium mode", "Reason", err),
)
}
k.l.Print(k.Ctx, "Selected cilium mode", "Mode", ciliumMode)
if ciliumMode == "guided" {
availableGuidedSetup := cni.CiliumGuidedConfigurations()
input := make(map[string]string, len(availableGuidedSetup))
for _, v := range availableGuidedSetup {
input[fmt.Sprintf("%s: %s", v.Name, v.Description)] = v.Name
}
selectedOption, err := k.menuDriven.MultiSelect("Select the cilium guided setup", input)
if err != nil {
return nil, errors.WrapError(
errors.ErrInvalidUserInput,
k.l.NewError(k.Ctx, "Failed to get the cilium guided setup", "Reason", err),
)
}
k.l.Debug(k.Ctx, "Selected cilium guided setup", "Setup", selectedOption)
config[string(cni.CiliumComponentID)]["guidedConfig"] = selectedOption
} else if ciliumMode == "advanced" {
editor := os.Getenv("EDITOR")
if editor == "" {
editor = "vim"
}
tempFile, err := os.CreateTemp("", "cilium_config_*.yaml")
if err != nil {
k.l.Error("Failed to create temporary file", "Reason", err)
return nil, errors.WrapError(
errors.ErrInvalidUserInput,
k.l.NewError(k.Ctx, "Failed to create temporary file", "Reason", err),
)
}
defer os.Remove(tempFile.Name())
cmd := exec.Command(editor, tempFile.Name())
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
k.l.Error("Failed to open editor", "Reason", err)
return nil, errors.WrapError(
errors.ErrInvalidUserInput,
k.l.NewError(k.Ctx, "Failed to open editor", "Reason", err),
)
}
content, err := os.ReadFile(tempFile.Name())
if err != nil {
k.l.Error("Failed to read temporary file", "Reason", err)
return nil, errors.WrapError(
errors.ErrInvalidUserInput,
k.l.NewError(k.Ctx, "Failed to read temporary file", "Reason", err),
)
}
var customConfig map[string]any
if err := yaml.Unmarshal(content, &customConfig); err != nil {
k.l.Error("Failed to parse YAML content", "Reason", err)
return nil, errors.WrapError(
errors.ErrInvalidUserInput,
k.l.NewError(k.Ctx, "Failed to parse YAML content", "Reason", err),
)
}
config[string(cni.CiliumComponentID)]["ciliumChartOverridings"] = customConfig
}
}
_config, err := json.Marshal(config)
if err != nil {
return nil, errors.WrapError(
errors.ErrInvalidUserInput,
k.l.NewError(k.Ctx, "Failed to marshal the CNI config", "Reason", err),
)
}
_v1.Config = utilities.Ptr(string(_config))
v = append(v, _v1)
return v, nil
}
07070100000013000081A40000000000000000000000016818E78D000004A6000000000000000000000000000000000000001B00000000ksctl-2.8.0/cmd/handler.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import "github.com/ksctl/cli/v2/pkg/cli"
func (k *KsctlCommand) CommandMapping() error {
c := k.Cluster()
cr := k.Configure()
a := k.Addons()
cli.RegisterCommand(
k.root,
c,
k.Version(),
k.SelfUpdate(),
k.ShellCompletion(),
cr,
)
cli.RegisterCommand(
c,
a,
k.Create(),
k.Delete(),
k.List(),
k.Get(),
k.Connect(),
k.ScaleUp(),
k.ScaleDown(),
k.Summary(),
)
cli.RegisterCommand(
cr,
k.ConfigureStorage(),
k.ConfigureCloud(),
k.ConfigureTelemetry(),
)
cli.RegisterCommand(
a,
k.EnableAddon(),
k.DisableAddon(),
)
return nil
}
07070100000014000081A40000000000000000000000016818E78D00000BAF000000000000000000000000000000000000001800000000ksctl-2.8.0/cmd/list.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"context"
"os"
"github.com/ksctl/cli/v2/pkg/telemetry"
"github.com/ksctl/ksctl/v2/pkg/consts"
"github.com/ksctl/ksctl/v2/pkg/errors"
"github.com/ksctl/ksctl/v2/pkg/handler/cluster/controller"
"github.com/ksctl/ksctl/v2/pkg/logger"
"github.com/ksctl/ksctl/v2/pkg/provider"
"github.com/spf13/cobra"
controllerCommon "github.com/ksctl/ksctl/v2/pkg/handler/cluster/common"
)
func (k *KsctlCommand) List() *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Example: `
ksctl list --help
`,
Short: "Use to list all the clusters",
Long: "It is used to list all the clusters created by the user",
Run: func(cmd *cobra.Command, args []string) {
clusters, err := k.fetchAllClusters()
if err != nil {
k.l.Error("Error in fetching the clusters", "Error", err)
os.Exit(1)
}
if err := k.telemetry.Send(k.Ctx, k.l, telemetry.EventClusterList, telemetry.TelemetryMeta{}); err != nil {
k.l.Debug(k.Ctx, "Failed to send the telemetry", "Reason", err)
}
if len(clusters) == 0 {
k.l.Print(k.Ctx, "No clusters found")
return
}
HandleTableOutputListAll(k.Ctx, k.l, clusters)
},
}
return cmd
}
func (k *KsctlCommand) fetchAllClusters() ([]provider.ClusterData, error) {
m := controller.Metadata{}
if v, ok := k.getSelectedStorageDriver(); !ok {
return nil, errors.NewError(errors.ErrInvalidStorageProvider)
} else {
m.StateLocation = v
}
m.Provider = consts.CloudAll
managerClient, err := controllerCommon.NewController(
k.Ctx,
k.l,
&controller.Client{
Metadata: m,
},
)
if err != nil {
k.l.Error("unable to initialize the ksctl manager", "Reason", err)
return nil, err
}
clusters, err := managerClient.ListClusters()
if err != nil {
return nil, err
}
return clusters, nil
}
func HandleTableOutputListAll(ctx context.Context, l logger.Logger, data []provider.ClusterData) {
headers := []string{"Name", "Type", "Cloud", "Region", "BootstrapProvider"}
var dataToPrint [][]string = make([][]string, 0, len(data))
for _, v := range data {
var row []string
row = append(row, v.Name, string(v.ClusterType), string(v.CloudProvider))
if v.CloudProvider == consts.CloudLocal {
row = append(row, "")
} else {
row = append(row, v.Region)
}
row = append(
row,
string(v.K8sDistro),
)
dataToPrint = append(dataToPrint, row)
}
l.Table(ctx, headers, dataToPrint)
}
07070100000015000081A40000000000000000000000016818E78D00000933000000000000000000000000000000000000001800000000ksctl-2.8.0/cmd/root.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"os"
"github.com/ksctl/cli/v2/pkg/cli"
"github.com/ksctl/cli/v2/pkg/telemetry"
cLogger "github.com/ksctl/cli/v2/pkg/logger"
"github.com/spf13/cobra"
)
// for the newController we should be able to pass some option fields for control more things
// for example whther it is a dry-run for testing
func (k *KsctlCommand) NewRootCmd() *cobra.Command {
v := false
cmd := &cobra.Command{
Use: "ksctl",
Short: "CLI tool for managing multiple K8s clusters",
Long: "CLI tool which can manage multiple K8s clusters from local clusters to cloud provider specific clusters.",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
telemetry.IntegrityCheck()
if k.debugMode {
k.CliLog.Box(k.Ctx, "CLI Mode", "CLI is running in debug mode")
k.menuDriven = cli.NewDebugMenuDriven()
} else {
k.menuDriven = cli.NewMenuDriven()
}
_ = k.menuDriven.GetProgressAnimation() // Just boot it up...
if v {
k.CliLog.Box(k.Ctx, "CLI Mode", "Verbose mode is enabled")
k.verbose = -1
}
k.l = cLogger.NewLogger(k.verbose, os.Stdout)
k.telemetry = telemetry.NewTelemetry(k.KsctlConfig.Telemetry)
cmdName := cmd.Name()
if cmdName != "self-update" && cmdName != "version" {
hasUpdates, err := k.CheckForUpdates()
if err == nil && hasUpdates {
k.NotifyAvailableUpdates()
}
}
},
}
cmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
cli.AddDebugMode(cmd, &k.debugMode)
cli.AddVerboseFlag(cmd, &v)
return cmd
}
func (k *KsctlCommand) Cluster() *cobra.Command {
cmd := &cobra.Command{
Use: "cluster",
Example: `
ksctl cluster --help
`,
Short: "Use to work with clusters",
Long: "It is used to work with cluster",
}
return cmd
}
07070100000016000081A40000000000000000000000016818E78D0000298E000000000000000000000000000000000000001900000000ksctl-2.8.0/cmd/scale.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"fmt"
"os"
"strconv"
"strings"
"github.com/ksctl/cli/v2/pkg/cli"
"github.com/ksctl/cli/v2/pkg/telemetry"
"github.com/ksctl/ksctl/v2/pkg/consts"
"github.com/ksctl/ksctl/v2/pkg/errors"
controllerCommon "github.com/ksctl/ksctl/v2/pkg/handler/cluster/common"
"github.com/ksctl/ksctl/v2/pkg/handler/cluster/controller"
controllerMeta "github.com/ksctl/ksctl/v2/pkg/handler/cluster/metadata"
"github.com/ksctl/ksctl/v2/pkg/handler/cluster/selfmanaged"
"github.com/ksctl/ksctl/v2/pkg/provider"
"github.com/spf13/cobra"
)
func (k *KsctlCommand) ScaleUp() *cobra.Command {
cmd := &cobra.Command{
Use: "scaleup",
Example: `
ksctl update scaleup --help
`,
Short: "Use to manually scaleup a selfmanaged cluster",
Long: "It is used to manually scaleup a selfmanaged cluster",
Run: func(cmd *cobra.Command, args []string) {
clusters, err := k.fetchSelfManagedClusters()
if err != nil {
k.l.Error("Error in fetching the clusters", "Error", err)
os.Exit(1)
}
if len(clusters) == 0 {
k.l.Error("There is no SelfManaged cluster")
os.Exit(1)
}
selectDisplay := make(map[string]string, len(clusters))
valueMaping := make(map[string]controller.Metadata, len(clusters))
for idx, cluster := range clusters {
if cluster.ClusterType == consts.ClusterTypeSelfMang {
selectDisplay[makeHumanReadableList(cluster)] = strconv.Itoa(idx)
valueMaping[strconv.Itoa(idx)] = controller.Metadata{
ClusterName: cluster.Name,
ClusterType: cluster.ClusterType,
Provider: cluster.CloudProvider,
Region: cluster.Region,
StateLocation: k.KsctlConfig.PreferedStateStore,
K8sDistro: cluster.K8sDistro,
K8sVersion: cluster.K8sVersion,
NoWP: cluster.NoWP,
}
}
}
selectedCluster, err := k.menuDriven.DropDown(
"Select the cluster to scaleup",
selectDisplay,
)
if err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
os.Exit(1)
}
m := valueMaping[selectedCluster]
if err := k.telemetry.Send(k.Ctx, k.l, telemetry.EventClusterScaleUp, telemetry.TelemetryMeta{
CloudProvider: m.Provider,
StorageDriver: m.StateLocation,
Region: m.Region,
ClusterType: m.ClusterType,
BootstrapProvider: m.K8sDistro,
K8sVersion: m.K8sVersion,
}); err != nil {
k.l.Debug(k.Ctx, "Failed to send the telemetry", "Reason", err)
}
currWP := m.NoWP
v, ok := k.getCounterValue(
"Enter the desired number of worker nodes",
func(i int) bool {
return i > currWP
},
currWP,
)
if !ok {
k.l.Warn(k.Ctx, "Make sure the no of workernodes should be more than the current workernodes")
os.Exit(1)
}
m.NoWP = v
if err := k.loadCloudProviderCreds(m.Provider); err != nil {
k.l.Error("Error in loading the cloud provider creds", "Error", err)
os.Exit(1)
}
metaClient, err := controllerMeta.NewController(
k.Ctx,
k.l,
&controller.Client{
Metadata: m,
},
)
if err != nil {
k.l.Error("Failed to create the controller", "Reason", err)
os.Exit(1)
}
category := provider.Unknown
if m.Provider != consts.CloudLocal {
category = k.handleInstanceCategorySelection()
}
wp := k.handleInstanceTypeSelection(metaClient, &m, category, "Select instance_type for Worker Nodes")
m.WorkerPlaneNodeType = wp.Sku
curr := "$"
if wp.Price.Currency == "USD" {
curr = "$"
} else if wp.Price.Currency == "INR" {
curr = "₹"
} else if wp.Price.Currency == "EUR" {
curr = "€"
}
k.l.Box(k.Ctx, "Updated Cost", fmt.Sprintf("Cost of the cluster will +%s%.2f (%d X %s)", curr, float64(m.NoWP-currWP)*wp.GetCost(), m.NoWP-currWP, wp.Sku))
// {
// cc := m
// cc.NoWP -= currWP
// k.metadataSummary(cc)
// }
if ok, _ := k.menuDriven.Confirmation("Do you want to proceed with the cluster scaleup", cli.WithDefaultValue("no")); !ok {
os.Exit(1)
}
c, err := selfmanaged.NewController(
k.Ctx,
k.l,
&controller.Client{
Metadata: m,
},
)
if err != nil {
k.l.Error("Error in creating the controller", "Error", err)
os.Exit(1)
}
if err := c.AddWorkerNodes(); err != nil {
k.l.Error("Error in scaling up the cluster", "Error", err)
os.Exit(1)
}
k.l.Success(k.Ctx, "Cluster workernode scaled up successfully")
},
}
return cmd
}
func (k *KsctlCommand) ScaleDown() *cobra.Command {
cmd := &cobra.Command{
Use: "scaledown",
Example: `
ksctl update scaledown --help
`,
Short: "Use to manually scaledown a selfmanaged cluster",
Long: "It is used to manually scaledown a selfmanaged cluster",
Run: func(cmd *cobra.Command, args []string) {
clusters, err := k.fetchSelfManagedClusters()
if err != nil {
k.l.Error("Error in fetching the clusters", "Error", err)
os.Exit(1)
}
if len(clusters) == 0 {
k.l.Error("There is no SelfManaged cluster")
os.Exit(1)
}
selectDisplay := make(map[string]string, len(clusters))
valueMaping := make(map[string]controller.Metadata, len(clusters))
for idx, cluster := range clusters {
if cluster.ClusterType == consts.ClusterTypeSelfMang {
selectDisplay[makeHumanReadableList(cluster)] = strconv.Itoa(idx)
valueMaping[strconv.Itoa(idx)] = controller.Metadata{
ClusterName: cluster.Name,
ClusterType: cluster.ClusterType,
Provider: cluster.CloudProvider,
Region: cluster.Region,
StateLocation: k.KsctlConfig.PreferedStateStore,
K8sDistro: cluster.K8sDistro,
K8sVersion: cluster.K8sVersion,
NoWP: cluster.NoWP,
WorkerPlaneNodeType: func() string {
g := []string{}
for _, v := range cluster.WP {
g = append(g, v.VMSize)
}
return strings.Join(g, ",")
}(),
}
}
}
selectedCluster, err := k.menuDriven.DropDown(
"Select the cluster to scaledown",
selectDisplay,
)
if err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
os.Exit(1)
}
m := valueMaping[selectedCluster]
if err := k.telemetry.Send(k.Ctx, k.l, telemetry.EventClusterScaleDown, telemetry.TelemetryMeta{
CloudProvider: m.Provider,
StorageDriver: m.StateLocation,
Region: m.Region,
ClusterType: m.ClusterType,
BootstrapProvider: m.K8sDistro,
K8sVersion: m.K8sVersion,
}); err != nil {
k.l.Debug(k.Ctx, "Failed to send the telemetry", "Reason", err)
}
if err := k.loadCloudProviderCreds(m.Provider); err != nil {
k.l.Error("Error in loading the cloud provider creds", "Error", err)
os.Exit(1)
}
currWP := m.NoWP
if currWP == 0 {
k.l.Error("There is no worker node to scale down")
os.Exit(1)
}
v, ok := k.getCounterValue(
"Enter the desired number of worker nodes",
func(i int) bool {
return i < currWP && i >= 0
},
currWP,
)
if !ok {
k.l.Warn(k.Ctx, "Make sure the no of workernodes should be less than the current workernodes and not less than 0")
os.Exit(1)
}
m.NoWP = v
{
// for just showing the costs changes
cc := m
metaClient, err := controllerMeta.NewController(
k.Ctx,
k.l,
&controller.Client{
Metadata: cc,
},
)
if err != nil {
k.l.Error("Failed to create the controller", "Reason", err)
os.Exit(1)
}
vms := strings.Split(cc.WorkerPlaneNodeType, ",")
g := map[string]struct {
Count int
VM provider.InstanceRegionOutput
}{}
for i := cc.NoWP; i < len(vms); i++ {
vm := vms[i]
wp := k.getSpecificInstanceForScaledown(metaClient, cc.Region, vm)
if _, ok := g[wp.Sku]; ok {
g[wp.Sku] = struct {
Count int
VM provider.InstanceRegionOutput
}{
Count: g[wp.Sku].Count + 1,
VM: wp,
}
} else {
g[wp.Sku] = struct {
Count int
VM provider.InstanceRegionOutput
}{
Count: 1,
VM: wp,
}
}
}
curr := "$"
total := 0.0
vmSize := []string{}
for k, x := range g {
if x.VM.Price.Currency == "USD" {
curr = "$"
} else if x.VM.Price.Currency == "INR" {
curr = "₹"
} else if x.VM.Price.Currency == "EUR" {
curr = "€"
}
total += float64(x.Count) * x.VM.GetCost()
vmSize = append(vmSize, fmt.Sprintf("(%d X %s)", x.Count, k))
}
k.l.Box(k.Ctx, "Updated Cost", fmt.Sprintf("Cost of the cluster will -%s%.2f <%s>", curr, total, strings.Join(vmSize, ",")))
cc.NoWP -= currWP
cc.WorkerPlaneNodeType = strings.Join(vmSize, ",")
// k.metadataSummary(cc)
}
if ok, _ := k.menuDriven.Confirmation("Do you want to proceed with the cluster scaledown", cli.WithDefaultValue("no")); !ok {
os.Exit(1)
}
c, err := selfmanaged.NewController(
k.Ctx,
k.l,
&controller.Client{
Metadata: m,
},
)
if err != nil {
k.l.Error("Error in creating the controller", "Error", err)
os.Exit(1)
}
if err := c.DeleteWorkerNodes(); err != nil {
k.l.Error("Error in scaling down the cluster", "Error", err)
os.Exit(1)
}
k.l.Success(k.Ctx, "Cluster workernode scaled down successfully")
},
}
return cmd
}
func (k *KsctlCommand) fetchSelfManagedClusters() ([]provider.ClusterData, error) {
m := controller.Metadata{}
if v, ok := k.getSelectedStorageDriver(); !ok {
return nil, errors.NewError(errors.ErrInvalidStorageProvider)
} else {
m.StateLocation = v
}
m.Provider = consts.CloudAll
m.ClusterType = consts.ClusterTypeSelfMang
managerClient, err := controllerCommon.NewController(
k.Ctx,
k.l,
&controller.Client{
Metadata: m,
},
)
if err != nil {
k.l.Error("unable to initialize the ksctl manager", "Reason", err)
return nil, err
}
clusters, err := managerClient.ListClusters()
if err != nil {
return nil, err
}
return clusters, nil
}
07070100000017000081A40000000000000000000000016818E78D00000A94000000000000000000000000000000000000001B00000000ksctl-2.8.0/cmd/summary.gopackage cmd
import (
"os"
"strconv"
"github.com/ksctl/cli/v2/pkg/cli"
"github.com/ksctl/cli/v2/pkg/telemetry"
"github.com/ksctl/ksctl/v2/pkg/handler/cluster/common"
"github.com/ksctl/ksctl/v2/pkg/handler/cluster/controller"
"github.com/spf13/cobra"
)
func (k *KsctlCommand) Summary() *cobra.Command {
cmd := &cobra.Command{
Use: "summary",
Example: `
ksctl cluster summary --help
`,
Short: "Use to get summary of the created cluster",
Long: "It is used to get summary cluster",
Run: func(cmd *cobra.Command, args []string) {
clusters, err := k.fetchAllClusters()
if err != nil {
k.l.Error("Error in fetching the clusters", "Error", err)
os.Exit(1)
}
if len(clusters) == 0 {
k.l.Error("No clusters found to connect")
os.Exit(1)
}
selectDisplay := make(map[string]string, len(clusters))
valueMaping := make(map[string]controller.Metadata, len(clusters))
for idx, cluster := range clusters {
selectDisplay[makeHumanReadableList(cluster)] = strconv.Itoa(idx)
valueMaping[strconv.Itoa(idx)] = controller.Metadata{
ClusterName: cluster.Name,
ClusterType: cluster.ClusterType,
Provider: cluster.CloudProvider,
Region: cluster.Region,
StateLocation: k.KsctlConfig.PreferedStateStore,
K8sDistro: cluster.K8sDistro,
K8sVersion: cluster.K8sVersion,
}
}
selectedCluster, err := k.menuDriven.DropDown(
"Select the cluster to for summary",
selectDisplay,
)
if err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
os.Exit(1)
}
m := valueMaping[selectedCluster]
if err := k.telemetry.Send(k.Ctx, k.l, telemetry.EventClusterConnect, telemetry.TelemetryMeta{
CloudProvider: m.Provider,
StorageDriver: m.StateLocation,
Region: m.Region,
ClusterType: m.ClusterType,
BootstrapProvider: m.K8sDistro,
K8sVersion: m.K8sVersion,
Addons: telemetry.TranslateMetadata(m.Addons),
}); err != nil {
k.l.Debug(k.Ctx, "Failed to send the telemetry", "Reason", err)
}
if k.loadCloudProviderCreds(m.Provider) != nil {
os.Exit(1)
}
c, err := common.NewController(
k.Ctx,
k.l,
&controller.Client{
Metadata: m,
},
)
if err != nil {
k.l.Error("Failed to create the controller", "Reason", err)
os.Exit(1)
}
health, err := c.ClusterSummary()
if err != nil {
k.l.Error("Failed to connect to the cluster", "Reason", err)
os.Exit(1)
}
printClusterSummary(health)
},
}
return cmd
}
func printClusterSummary(summary *common.SummaryOutput) {
cli.NewSummaryUI(os.Stdout).RenderClusterSummary(summary)
}
07070100000018000081A40000000000000000000000016818E78D00002372000000000000000000000000000000000000001A00000000ksctl-2.8.0/cmd/update.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"archive/tar"
"compress/gzip"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/ksctl/cli/v2/pkg/cli"
"github.com/ksctl/cli/v2/pkg/config"
"github.com/ksctl/cli/v2/pkg/telemetry"
"github.com/ksctl/ksctl/v2/pkg/poller"
"github.com/spf13/cobra"
"golang.org/x/mod/semver"
)
func (k *KsctlCommand) CheckForUpdates() (bool, error) {
cacheFile := &config.UpdateCache{}
if errC := config.LoadUpdateCache(cacheFile); errC != nil {
k.l.Error("Failed to load update cache", "error", errC)
return false, errC
}
if config.InDevMode() {
return false, nil
}
if !cacheFile.LastChecked.IsZero() && time.Since(cacheFile.LastChecked) < cacheFile.UpdateCheckInterval {
return cacheFile.AvailableVersions, nil
}
versions, err := k.fetchLatestVersion()
if err != nil {
return false, err
}
upgradeableVersions := k.filterToUpgradeableVersions(versions)
cacheFile.LastChecked = time.Now()
cacheFile.AvailableVersions = len(upgradeableVersions) > 0
if err := config.SaveUpdateCache(cacheFile); err != nil {
k.l.Error("Failed to save update cache", "error", err)
return false, err
}
return cacheFile.AvailableVersions, nil
}
func (k *KsctlCommand) NotifyAvailableUpdates() {
k.l.Box(k.Ctx, "Update Available! ✨", "Run 'ksctl self-update' to upgrade to the latest version!")
}
func (k *KsctlCommand) SelfUpdate() *cobra.Command {
cmd := &cobra.Command{
Use: "self-update",
Example: `
ksctl self-update --help
`,
Short: "Use to update the ksctl cli",
Long: "It is used to update the ksctl cli",
Run: func(cmd *cobra.Command, args []string) {
if config.InDevMode() {
k.l.Error("Cannot update dev version", "msg", "Please use a stable version to update")
os.Exit(1)
}
k.l.Warn(k.Ctx, "Currently no migrations are supported", "msg", "Please help us by creating a PR to support migrations. Thank you!")
k.l.Print(k.Ctx, "Fetching available versions")
vers, err := k.fetchLatestVersion()
if err != nil {
k.l.Error("Failed to fetch latest version", "error", err)
os.Exit(1)
}
vers = k.filterToUpgradeableVersions(vers)
if len(vers) == 0 {
k.l.Note(k.Ctx, "You are already on the latest version", "version", config.Version)
os.Exit(0)
}
selectedOption, err := k.menuDriven.DropDownList("Select a version to update", vers, cli.WithDefaultValue(vers[0]))
if err != nil {
return
}
newVer := selectedOption
if err := k.telemetry.Send(k.Ctx, k.l, telemetry.EventClusterUpgrade, telemetry.TelemetryMeta{}); err != nil {
k.l.Debug(k.Ctx, "Failed to send the telemetry", "Reason", err)
}
{
c := &config.UpdateCache{}
err := config.LoadUpdateCache(c)
if err == nil {
c.LastChecked = time.Now()
c.AvailableVersions = false
if err := config.SaveUpdateCache(c); err != nil {
k.l.Error("Failed to save update cache", "error", err)
}
} else {
k.l.Error("Failed to load update cache", "error", err)
}
}
if err := k.update(newVer); err != nil {
k.l.Error("Failed to update ksctl cli", "error", err)
os.Exit(1)
}
k.l.Box(k.Ctx, "Updated Successful 🎉", "ksctl has been updated to version "+newVer)
},
}
return cmd
}
func (k *KsctlCommand) fetchLatestVersion() ([]string, error) {
poller.InitSharedGithubReleasePoller()
return poller.GetSharedPoller().Get("ksctl", "cli")
}
func (k *KsctlCommand) filterToUpgradeableVersions(versions []string) []string {
var upgradeableVersions []string
for _, version := range versions {
if semver.Compare(version, config.Version) > 0 {
upgradeableVersions = append(upgradeableVersions, version)
}
}
return upgradeableVersions
}
func (k *KsctlCommand) downloadFile(url, localFilename string) error {
k.l.Print(k.Ctx, "Downloading file", "url", url, "localFilename", localFilename)
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
out, err := os.Create(localFilename)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, resp.Body)
return err
}
func (k *KsctlCommand) verifyChecksum(filePath, checksumfileLoc string) (bool, error) {
k.l.Print(k.Ctx, "Verifying checksum", "file", filePath, "checksumfile", checksumfileLoc)
rawChecksum, err := os.ReadFile(checksumfileLoc)
if err != nil {
return false, err
}
checksums := strings.Split(string(rawChecksum), "\n")
var expectedChecksum string = "LOL"
for _, line := range checksums {
if strings.Contains(line, filePath) {
expectedChecksum = strings.Fields(line)[0]
break
}
}
if expectedChecksum == "LOL" {
return false, k.l.NewError(k.Ctx, "Checksum not found in checksum file")
}
file, err := os.Open(filePath)
if err != nil {
return false, err
}
defer file.Close()
hash := sha256.New()
if _, err := io.Copy(hash, file); err != nil {
return false, err
}
calculatedChecksum := hex.EncodeToString(hash.Sum(nil))
return calculatedChecksum == expectedChecksum, nil
}
func (k *KsctlCommand) getOsArch() (string, error) {
arch := runtime.GOARCH
if arch != "amd64" && arch != "arm64" {
return "", k.l.NewError(k.Ctx, "Unsupported architecture")
}
return arch, nil
}
func (k *KsctlCommand) getOs() (string, error) {
goos := runtime.GOOS
if goos != "linux" && goos != "darwin" {
return "", k.l.NewError(k.Ctx, "Unsupported OS", "message", "will provide support for windows based OS soon")
}
return goos, nil
}
func (k *KsctlCommand) update(version string) error {
osName, err := k.getOs()
if err != nil {
return err
}
archName, err := k.getOsArch()
if err != nil {
return err
}
k.l.Print(k.Ctx, "Delected System", "OS", osName, "Arch", archName)
downloadURLBase := fmt.Sprintf("https://github.com/ksctl/cli/releases/download/%s", version)
tarFile := fmt.Sprintf("ksctl-cli_%s_%s_%s.tar.gz", version[1:], osName, archName)
checksumFile := fmt.Sprintf("ksctl-cli_%s_checksums.txt", version[1:])
tarUri := fmt.Sprintf("%s/%s", downloadURLBase, tarFile)
checksumUri := fmt.Sprintf("%s/%s", downloadURLBase, checksumFile)
defer func() {
k.l.Print(k.Ctx, "Cleaning up")
if err := os.Remove(checksumFile); err != nil {
k.l.Error("Failed to remove checksum file", "error", err)
}
if err := os.Remove(tarFile); err != nil {
k.l.Error("Failed to remove checksum file", "error", err)
}
}()
if err := k.downloadFile(tarUri, tarFile); err != nil {
return err
}
if err := k.downloadFile(checksumUri, checksumFile); err != nil {
return err
}
match, err := k.verifyChecksum(tarFile, checksumFile)
if err != nil {
return k.l.NewError(k.Ctx, "Failed to verify checksum", "error", err)
}
if !match {
return k.l.NewError(k.Ctx, "Checksum verification failed")
}
k.l.Success(k.Ctx, "Checksum verification successful")
tempDir, err := os.MkdirTemp("", "ksctl-update")
if err != nil {
return k.l.NewError(k.Ctx, "Failed to create temp dir", "error", err)
}
file, err := os.Open(tarFile)
if err != nil {
return k.l.NewError(k.Ctx, "Failed to open tar file", "error", err)
}
defer file.Close()
gzr, err := gzip.NewReader(file)
if err != nil {
return k.l.NewError(k.Ctx, "Failed to read gzip file", "error", err)
}
defer gzr.Close()
tr := tar.NewReader(gzr)
for {
header, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return k.l.NewError(k.Ctx, "Failed to read tar file", "error", err)
}
if header.Name == "ksctl" {
outFile, err := os.Create(filepath.Join(tempDir, "ksctl"))
if err != nil {
return k.l.NewError(k.Ctx, "Failed to create ksctl binary", "error", err)
}
defer outFile.Close()
if _, err := io.Copy(outFile, tr); err != nil {
return k.l.NewError(k.Ctx, "Failed to copy ksctl binary", "error", err)
}
break
}
}
k.l.Print(k.Ctx, "Making ksctl executable...")
if err := os.Chmod(filepath.Join(tempDir, "ksctl"), 0550); err != nil {
return k.l.NewError(k.Ctx, "Failed to make ksctl executable", "error", err)
}
k.l.Print(k.Ctx, "Moving ksctl to /usr/local/bin (requires sudo)...")
cmd := exec.Command("sudo", "mv", "-v", filepath.Join(tempDir, "ksctl"), "/usr/local/bin/ksctl")
err = cmd.Run()
if err != nil {
return k.l.NewError(k.Ctx, "Failed to move ksctl to /usr/local/bin", "error", err)
}
_, err = exec.LookPath("ksctl")
if err != nil {
return k.l.NewError(k.Ctx, "Failed to find ksctl in PATH", "error", err)
}
return nil
}
07070100000019000081A40000000000000000000000016818E78D000023D7000000000000000000000000000000000000001D00000000ksctl-2.8.0/cmd/userinput.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"context"
"fmt"
"strconv"
"strings"
"github.com/ksctl/cli/v2/pkg/cli"
"github.com/ksctl/ksctl/v2/pkg/consts"
"github.com/ksctl/ksctl/v2/pkg/provider"
)
func (k *KsctlCommand) getClusterName() (string, bool) {
v, err := k.menuDriven.TextInput("Enter Cluster Name")
if err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
return "", false
}
if len(v) == 0 {
k.l.Error("Cluster name cannot be empty")
return "", false
}
k.l.Debug(k.Ctx, "Text input", "clusterName", v)
return v, true
}
func (k *KsctlCommand) getBootstrap() (consts.KsctlKubernetes, bool) {
v, err := k.menuDriven.DropDown(
"Select the bootstrap type",
map[string]string{
"Kubeadm": string(consts.K8sKubeadm),
"K3s": string(consts.K8sK3s),
},
cli.WithDefaultValue(string(consts.K8sK3s)),
)
if err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
return "", false
}
k.l.Debug(k.Ctx, "DropDown input", "bootstrapType", v)
return consts.KsctlKubernetes(v), true
}
type userInputValidation func(int) bool
func (k *KsctlCommand) getCounterValue(prompt string, validate userInputValidation, defaultVal int) (int, bool) {
v, err := k.menuDriven.TextInput(prompt, cli.WithDefaultValue(strconv.Itoa(defaultVal)))
if err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
return 0, false
}
_v, err := strconv.Atoi(v)
if err != nil {
k.l.Error("Invalid input", "Reason", err)
return 0, false
}
if !validate(_v) {
k.l.Error("Invalid input")
return 0, false
}
k.l.Debug(k.Ctx, "Text input", "counterValue", v)
return _v, true
}
func (k *KsctlCommand) getSelectedRegion(regions provider.RegionsOutput) (string, bool) {
k.l.Debug(k.Ctx, "Regions", "regions", regions)
if v, err := k.menuDriven.DropDown(
"Select the region",
CliRegions(regions).S(),
); err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
return "", false
} else {
k.l.Debug(k.Ctx, "DropDown input", "region", v)
return v, true
}
}
func (k *KsctlCommand) getSelectedInstanceCategory(categories map[string]provider.MachineCategory) (provider.MachineCategory, bool) {
k.l.Debug(k.Ctx, "Instance categories", "categories", categories)
vr := make(map[string]string, len(categories))
for k, _v := range categories {
useCases := strings.Join(_v.UseCases(), ", ")
key := fmt.Sprintf("%s\n Used for: %s\n", k, useCases)
vr[key] = string(_v)
}
if v, err := k.menuDriven.DropDown(
"Let us know about your workload type",
vr,
); err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
return "", false
} else {
k.l.Debug(k.Ctx, "DropDown input", "instanceCategory", v)
return provider.MachineCategory(v), true
}
}
func (k *KsctlCommand) getSelectedK8sVersion(prompt string, vers []string) (string, bool) {
k.l.Debug(k.Ctx, "List of k8s versions", "versions", vers)
if v, err := k.menuDriven.DropDownList(
prompt,
vers,
cli.WithDefaultValue(vers[0]),
); err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
return "", false
} else {
k.l.Debug(k.Ctx, "DropDown input", "k8sVersion", v)
return v, true
}
}
type CliRegions provider.RegionsOutput
func (r CliRegions) S() map[string]string {
m := make(map[string]string, len(r))
for _, region := range r {
desc := region.Name
if region.Emission != nil {
emissionEmoji := "🔴" // High emissions (default)
if region.Emission.DirectCarbonIntensity < 200 {
emissionEmoji = "🟢" // Low emissions
} else if region.Emission.DirectCarbonIntensity < 400 {
emissionEmoji = "🟡" // Medium emissions
}
carbonInfo := fmt.Sprintf("🏭 Direct %.2f %s, Lifecycle %.2f %s",
region.Emission.DirectCarbonIntensity,
region.Emission.Unit,
region.Emission.LCACarbonIntensity,
region.Emission.Unit)
percentageInfo := ""
if region.Emission.RenewablePercentage > 0 {
percentageInfo += fmt.Sprintf(", ♻️ %.1f%% renewable", region.Emission.RenewablePercentage)
}
if region.Emission.LowCarbonPercentage > 0 {
percentageInfo += fmt.Sprintf(", 👣 %.1f%% low-carbon", region.Emission.LowCarbonPercentage)
}
desc += fmt.Sprintf(" %s (%s: %s%s)", emissionEmoji, region.Emission.CalcMethod, carbonInfo, percentageInfo)
}
m[desc] = region.Sku
}
return m
}
type CliInstances provider.InstancesRegionOutput
func (I CliInstances) S() map[string]string {
m := make(map[string]string, len(I))
for _, vm := range I {
if vm.CpuArch == provider.ArchAmd64 {
displayName := fmt.Sprintf("%s (vCPUs: %d, Memory: %dGB)",
vm.Description,
vm.VCpus,
vm.Memory,
)
displayName += fmt.Sprintf(", Price: %.2f %s/month",
vm.GetCost(),
vm.Price.Currency,
)
if vm.EmboddedEmissions != nil {
displayName += fmt.Sprintf(", Embodied Emission: %.2f %s",
vm.EmboddedEmissions.EmboddedCo2,
vm.EmboddedEmissions.Co2Unit,
)
}
m[displayName] = vm.Sku
}
}
return m
}
func (k *KsctlCommand) getSelectedInstanceType(
prompt string,
vms provider.InstancesRegionOutput,
) (string, bool) {
vr := CliInstances(vms).S()
k.l.Debug(k.Ctx, "Instance types", "vms", vr)
if v, err := k.menuDriven.DropDown(
prompt,
vr,
); err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
return "", false
} else {
k.l.Debug(k.Ctx, "DropDown input", "instanceType", v)
return v, true
}
}
func (k *KsctlCommand) getSelectedManagedClusterOffering(
prompt string,
offerings map[string]provider.ManagedClusterOutput,
) (string, bool) {
vr := make(map[string]string, len(offerings))
for _, o := range offerings {
displayName := fmt.Sprintf("%s, Price: %.2f %s/month",
o.Description,
o.GetCost(),
o.Price.Currency,
)
vr[displayName] = o.Sku
}
k.l.Debug(k.Ctx, "Offerings", "offerings", vr)
if v, err := k.menuDriven.DropDown(
prompt,
vr,
); err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
return "", false
} else {
k.l.Debug(k.Ctx, "DropDown input", "managedClusterOffering", v)
return v, true
}
}
func (k *KsctlCommand) getSelectedClusterType() (consts.KsctlClusterType, bool) {
if v, err := k.menuDriven.DropDown(
"Select the cluster type",
map[string]string{
"Cloud Managed (For ex. EKS, AKS, Kind)": string(consts.ClusterTypeMang),
"Self Managed (For example, K3s, Kubeadm)": string(consts.ClusterTypeSelfMang),
},
cli.WithDefaultValue(string(consts.ClusterTypeMang)),
); err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
return "", false
} else {
k.l.Debug(k.Ctx, "DropDown input", "clusterType", v)
return consts.KsctlClusterType(v), true
}
}
func (k *KsctlCommand) getSelectedCloudProvider(v consts.KsctlClusterType) (consts.KsctlCloud, bool) {
options := map[string]string{
"Amazon Web Services": string(consts.CloudAws),
"Azure": string(consts.CloudAzure),
}
if v == consts.ClusterTypeMang {
options["Kind"] = string(consts.CloudLocal)
}
if v, err := k.menuDriven.DropDown(
"Select the cloud provider",
options,
); err != nil {
k.l.Error("Failed to get userinput", "Reason", err)
return "", false
} else {
k.l.Debug(k.Ctx, "DropDown input", "cloudProvider", v)
if err := k.loadCloudProviderCreds(consts.KsctlCloud(v)); err != nil {
return "", false
}
return consts.KsctlCloud(v), true
}
}
func (k *KsctlCommand) loadCloudProviderCreds(v consts.KsctlCloud) error {
switch v {
case consts.CloudAws:
if v, err := k.loadAwsCredentials(); err != nil {
k.l.Error("Failed to load the AWS credentials", "Reason", err)
return err
} else {
k.Ctx = context.WithValue(k.Ctx, consts.KsctlAwsCredentials, v)
}
case consts.CloudAzure:
if v, err := k.loadAzureCredentials(); err != nil {
k.l.Error("Failed to load the Azure credentials", "Reason", err)
return err
} else {
k.Ctx = context.WithValue(k.Ctx, consts.KsctlAzureCredentials, v)
}
}
return nil
}
func (k *KsctlCommand) getSelectedStorageDriver() (consts.KsctlStore, bool) {
if k.KsctlConfig.PreferedStateStore != consts.StoreExtMongo && k.KsctlConfig.PreferedStateStore != consts.StoreLocal {
k.l.Error("Failed to determine StorageDriver", "message", "Please use $ksctl configure to set the storage driver", "currentSetValue", k.KsctlConfig.PreferedStateStore)
return "", false
}
if k.KsctlConfig.PreferedStateStore == consts.StoreExtMongo {
if errS := k.loadMongoCredentials(); errS != nil {
k.l.Error("Failed to load the MongoDB credentials", "Reason", errS)
return "", false
}
}
return k.KsctlConfig.PreferedStateStore, true
}
0707010000001A000081A40000000000000000000000016818E78D00005444000000000000000000000000000000000000001B00000000ksctl-2.8.0/cmd/version.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
"fmt"
"github.com/fatih/color"
"github.com/ksctl/cli/v2/pkg/config"
"github.com/spf13/cobra"
)
func (k *KsctlCommand) Version() *cobra.Command {
logoKsctl := `
[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⡪[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⡽[0m[48;2;218;226;214m⠵[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⠽[0m[48;2;218;226;214m⢝[0m[48;2;218;226;214m⡮[0m[48;2;218;226;214m⣳[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⡮[0m[48;2;218;226;214m⠷[0m[48;2;218;226;214m⠝[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⡪[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⢷[0m[48;2;218;226;214m⢝[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⡪[0m[48;2;218;226;214m⣯[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⡽[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⢯[0m[48;2;218;226;214m⣳[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⡽[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⣞[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⣳[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⡽[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⢞[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⣳[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⡽[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⢞[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⣳[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⡽[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⢯[0m[48;2;218;226;214m⡳[0m
[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⡺[0m[48;2;218;226;214m⡜[0m[48;2;216;224;212m⢊[0m[48;2;226;233;220m⠡[0m[48;2;128;155;149m⡀[0m[48;2;58;99;97m⢁[0m[48;2;53;95;94m⠠[0m[48;2;56;98;96m⠀[0m[48;2;55;97;95m⢔[0m[48;2;224;231;219m⡯[0m[48;2;218;226;214m⣞[0m[48;2;216;224;212m⠎[0m[48;2;182;198;188m⠠[0m[48;2;51;94;92m⢐[0m[48;2;47;91;90m⣰[0m[48;2;192;205;195m⡳[0m[48;2;217;225;213m⣝[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⣳[0m[48;2;218;226;214m⢝[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⣕[0m[48;2;218;226;214m⢯[0m[48;2;218;226;214m⡺[0m[48;2;218;226;214m⡮[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢝[0m[48;2;218;226;214m⡮[0m[48;2;218;226;214m⡮[0m[48;2;218;226;214m⣳[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⣞[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⢯[0m[48;2;218;226;214m⣳[0m[48;2;218;226;214m⡳[0m[48;2;215;224;212m⠙[0m[48;2;217;225;214m⠚[0m[48;2;222;229;217m⠊[0m[48;2;228;234;221m⢹[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⡵[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⣞[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⢯[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⡽[0m
[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⢗[0m[48;2;218;226;214m⡗[0m[48;2;229;234;222m⠡[0m[48;2;61;101;99m⠐[0m[48;2;65;105;103m⣰[0m[48;2;156;176;168m⢝[0m[48;2;212;221;210m⠂[0m[48;2;61;101;99m⠄[0m[48;2;63;103;101m⡀[0m[48;2;65;104;102m⠂[0m[48;2;171;189;180m⣯[0m[48;2;216;225;213m⡺[0m[48;2;227;233;220m⠁[0m[48;2;59;100;98m⠂[0m[48;2;66;106;104m⣡[0m[48;2;133;159;152m⢞[0m[48;2;216;224;212m⣞[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⡮[0m[48;2;218;226;214m⣳[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⣕[0m[48;2;218;226;214m⢷[0m[48;2;218;226;214m⢝[0m[48;2;218;226;214m⡮[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢵[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢝[0m[48;2;218;226;214m⡵[0m[48;2;218;226;214m⣫[0m[48;2;218;226;214m⢯[0m[48;2;220;228;216m⠎[0m[48;2;171;188;179m⢈[0m[48;2;131;157;150m⠀[0m[48;2;93;127;123m⡀[0m[48;2;73;111;108m⢜[0m[48;2;222;229;217m⣗[0m[48;2;218;226;214m⢵[0m[48;2;220;227;215m⠅[0m[48;2;59;100;98m⠐[0m[48;2;65;104;102m⠈[0m[48;2;66;105;103m⡀[0m[48;2;147;169;162m⣽[0m[48;2;216;224;212m⣪[0m[48;2;218;226;214m⣻[0m[48;2;218;226;214m⡺[0m[48;2;218;226;214m⣪[0m[48;2;218;226;214m⢯[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⡽[0m[48;2;218;226;214m⣝[0m
[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢵[0m[48;2;218;226;214m⢽[0m[48;2;216;224;212m⡝[0m[48;2;144;167;160m⢀[0m[48;2;66;106;104m⠐[0m[48;2;55;96;95m⡸[0m[48;2;228;234;221m⣕[0m[48;2;218;226;214m⡯[0m[48;2;89;124;120m⠐[0m[48;2;64;104;102m⢀[0m[48;2;63;103;101m⠐[0m[48;2;52;94;93m⢸[0m[48;2;227;233;220m⠎[0m[48;2;186;200;190m⠐[0m[48;2;65;104;102m⣈[0m[48;2;69;108;106m⢮[0m[48;2;225;231;219m⣳[0m[48;2;218;226;214m⢝[0m[48;2;218;226;214m⡮[0m[48;2;218;226;214m⣳[0m[48;2;218;226;214m⢝[0m[48;2;218;226;214m⡮[0m[48;2;218;226;214m⠻[0m[48;2;217;225;213m⠪[0m[48;2;216;224;212m⠓[0m[48;2;217;225;213m⢳[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢝[0m[48;2;218;226;214m⡮[0m[48;2;218;226;214m⣳[0m[48;2;218;226;214m⠽[0m[48;2;218;226;214m⠵[0m[48;2;218;226;214m⡫[0m[48;2;218;226;214m⢗[0m[48;2;218;226;214m⡽[0m[48;2;218;226;214m⣪[0m[48;2;217;225;213m⢯[0m[48;2;191;204;194m⠁[0m[48;2;63;103;101m⠄[0m[48;2;63;103;101m⠠[0m[48;2;63;103;101m⢀[0m[48;2;191;204;194m⢯[0m[48;2;217;225;213m⢮[0m[48;2;216;225;213m⠯[0m[48;2;111;141;136m⠀[0m[48;2;65;105;103m⠌[0m[48;2;63;103;101m⢀[0m[48;2;52;94;93m⢰[0m[48;2;228;234;222m⡳[0m[48;2;218;226;214m⣕[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⡪[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⢮[0m
[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;229;235;222m⡧[0m[48;2;52;94;93m⡀[0m[48;2;65;105;103m⠄[0m[48;2;164;184;175m⢝[0m[48;2;216;224;212m⣞[0m[48;2;226;232;220m⠆[0m[48;2;54;96;94m⠐[0m[48;2;63;103;101m⠀[0m[48;2;65;105;103m⠄[0m[48;2;133;158;152m⠑[0m[48;2;58;99;98m⠠[0m[48;2;52;94;93m⠼[0m[48;2;228;234;221m⢮[0m[48;2;218;226;214m⢯[0m[48;2;218;226;214m⡺[0m[48;2;218;226;214m⡵[0m[48;2;218;226;214m⣻[0m[48;2;218;226;214m⡪[0m[48;2;216;224;212m⡟[0m[48;2;170;188;179m⡀[0m[48;2;60;101;99m⠐[0m[48;2;63;103;101m⠀[0m[48;2;67;107;104m⠂[0m[48;2;110;141;136m⢱[0m[48;2;216;225;213m⡳[0m[48;2;218;226;214m⣕[0m[48;2;218;226;214m⢯[0m[48;2;218;226;214m⡳[0m[48;2;217;225;213m⠋[0m[48;2;198;211;200m⡀[0m[48;2;70;109;106m⠂[0m[48;2;52;95;93m⣄[0m[48;2;52;94;93m⡤[0m[48;2;55;97;95m⠀[0m[48;2;175;192;183m⠙[0m[48;2;214;223;211m⣞[0m[48;2;108;138;134m⡤[0m[48;2;64;104;102m⠐[0m[48;2;63;103;101m⠀[0m[48;2;63;103;101m⠂[0m[48;2;63;103;101m⢤[0m[48;2;58;99;97m⣢[0m[48;2;160;180;172m⣺[0m[48;2;227;233;220m⢃[0m[48;2;52;94;93m⠁[0m[48;2;63;103;101m⠐[0m[48;2;65;104;102m⡀[0m[48;2;101;134;129m⢼[0m[48;2;217;225;213m⣺[0m[48;2;218;226;214m⡺[0m[48;2;218;226;214m⣪[0m[48;2;218;226;214m⢗[0m[48;2;218;226;214m⡽[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⢷[0m[48;2;218;226;214m⢝[0m
[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;219;227;215m⡳[0m[48;2;80;117;113m⡦[0m[48;2;106;138;133m⡳[0m[48;2;214;223;211m⣝[0m[48;2;131;157;151m⠀[0m[48;2;66;105;103m⠌[0m[48;2;63;103;101m⠠[0m[48;2;63;103;101m⢨[0m[48;2;59;100;98m⢤[0m[48;2;64;104;102m⡅[0m[48;2;51;94;92m⠐[0m[48;2;57;98;96m⢀[0m[48;2;120;148;142m⠈[0m[48;2;222;229;217m⢫[0m[48;2;218;226;214m⢯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⣺[0m[48;2;227;233;220m⠁[0m[48;2;53;95;94m⣐[0m[48;2;63;103;101m⠈[0m[48;2;63;103;101m⡀[0m[48;2;64;104;102m⢁[0m[48;2;83;119;116m⢸[0m[48;2;219;227;215m⢝[0m[48;2;218;226;214m⡮[0m[48;2;215;224;212m⡏[0m[48;2;149;171;163m⠠[0m[48;2;66;106;103m⠀[0m[48;2;63;103;101m⠄[0m[48;2;52;94;93m⣲[0m[48;2;231;236;224m⢳[0m[48;2;220;228;216m⢝[0m[48;2;69;108;105m⠀[0m[48;2;57;98;97m⣢[0m[48;2;221;229;216m⣳[0m[48;2;219;227;215m⠅[0m[48;2;57;98;97m⠐[0m[48;2;63;103;101m⠈[0m[48;2;66;105;103m⡀[0m[48;2;144;167;160m⣗[0m[48;2;215;224;212m⢵[0m[48;2;216;224;212m⢽[0m[48;2;156;177;169m⠀[0m[48;2;65;105;103m⠄[0m[48;2;63;103;101m⢁[0m[48;2;58;99;97m⢠[0m[48;2;215;224;212m⡻[0m[48;2;218;226;214m⣜[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⣳[0m[48;2;218;226;214m⢝[0m[48;2;218;226;214m⡮[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⢯[0m[48;2;218;226;214m⢯[0m[48;2;218;226;214m⣫[0m
[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣫[0m[48;2;218;226;214m⢯[0m[48;2;229;235;222m⡃[0m[48;2;52;94;93m⠄[0m[48;2;63;103;101m⠂[0m[48;2;64;104;102m⠐[0m[48;2;86;121;118m⣸[0m[48;2;218;226;214m⣕[0m[48;2;217;226;214m⢯[0m[48;2;81;118;114m⠂[0m[48;2;64;104;102m⢀[0m[48;2;64;104;102m⠂[0m[48;2;84;120;116m⢸[0m[48;2;219;227;215m⡳[0m[48;2;218;226;214m⡵[0m[48;2;227;233;221m⡃[0m[48;2;53;95;94m⢠[0m[48;2;214;223;211m⡗[0m[48;2;53;95;94m⡀[0m[48;2;63;103;101m⠄[0m[48;2;63;103;101m⠠[0m[48;2;60;101;99m⠨[0m[48;2;225;231;219m⣗[0m[48;2;216;224;212m⡝[0m[48;2;119;148;142m⠀[0m[48;2;66;105;103m⡐[0m[48;2;63;103;101m⠀[0m[48;2;53;95;93m⢢[0m[48;2;228;234;221m⢯[0m[48;2;218;226;214m⡫[0m[48;2;217;225;213m⣏[0m[48;2;228;234;221m⢯[0m[48;2;226;232;220m⡺[0m[48;2;216;225;213m⡮[0m[48;2;110;141;135m⠀[0m[48;2;65;105;102m⡈[0m[48;2;63;103;101m⠄[0m[48;2;53;95;93m⢰[0m[48;2;228;234;221m⢝[0m[48;2;218;226;214m⡽[0m[48;2;226;233;220m⡕[0m[48;2;56;98;96m⠀[0m[48;2;63;103;101m⡐[0m[48;2;64;103;101m⠀[0m[48;2;68;107;105m⣸[0m[48;2;222;229;217m⢮[0m[48;2;218;226;214m⣳[0m[48;2;218;226;214m⡫[0m[48;2;218;226;214m⣞[0m[48;2;218;226;214m⢵[0m[48;2;218;226;214m⡫[0m[48;2;218;226;214m⣞[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⣕[0m[48;2;218;226;214m⣗[0m
[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;216;225;213m⢵[0m[48;2;177;193;184m⠁[0m[48;2;64;104;102m⠠[0m[48;2;63;103;101m⠈[0m[48;2;61;101;99m⢠[0m[48;2;204;215;204m⡳[0m[48;2;217;225;213m⣳[0m[48;2;227;233;221m⠣[0m[48;2;55;97;95m⠐[0m[48;2;63;103;101m⠀[0m[48;2;66;105;103m⠄[0m[48;2;140;164;157m⡽[0m[48;2;216;224;212m⡪[0m[48;2;224;230;218m⠃[0m[48;2;61;101;100m⠄[0m[48;2;182;197;187m⢷[0m[48;2;218;226;214m⢽[0m[48;2;77;115;112m⠀[0m[48;2;64;104;102m⠐[0m[48;2;63;103;101m⡀[0m[48;2;52;94;93m⠌[0m[48;2;229;235;222m⣞[0m[48;2;213;222;210m⠆[0m[48;2;59;100;98m⠁[0m[48;2;63;103;101m⠠[0m[48;2;66;105;103m⠈[0m[48;2;136;161;154m⡮[0m[48;2;215;224;212m⣳[0m[48;2;218;226;214m⢝[0m[48;2;218;226;214m⡮[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⢽[0m[48;2;229;235;222m⠕[0m[48;2;52;94;93m⠀[0m[48;2;63;103;101m⠄[0m[48;2;65;104;102m⠂[0m[48;2;100;133;128m⡼[0m[48;2;217;225;213m⡳[0m[48;2;217;225;213m⣝[0m[48;2;196;208;198m⠂[0m[48;2;62;103;101m⠄[0m[48;2;63;103;101m⠐[0m[48;2;64;104;102m⢀[0m[48;2;183;198;189m⣗[0m[48;2;216;225;213m⢗[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⡺[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⢗[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⢵[0m
[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⡺[0m[48;2;223;230;218m⡇[0m[48;2;65;104;102m⠂[0m[48;2;63;103;101m⢁[0m[48;2;63;103;101m⠈[0m[48;2;60;101;99m⡰[0m[48;2;225;231;219m⡯[0m[48;2;216;224;212m⣺[0m[48;2;171;188;179m⠁[0m[48;2;65;104;102m⠠[0m[48;2;63;103;101m⠁[0m[48;2;52;94;93m⣨[0m[48;2;227;233;221m⣻[0m[48;2;176;193;184m⠀[0m[48;2;65;105;103m⢡[0m[48;2;68;107;105m⡠[0m[48;2;154;175;168m⡯[0m[48;2;213;222;211m⡫[0m[48;2;118;147;141m⠂[0m[48;2;65;105;103m⠁[0m[48;2;63;103;101m⠠[0m[48;2;54;96;95m⠐[0m[48;2;225;232;219m⠩[0m[48;2;134;159;153m⡀[0m[48;2;66;105;103m⠌[0m[48;2;63;103;101m⠀[0m[48;2;61;102;100m⠂[0m[48;2;202;213;202m⢯[0m[48;2;217;225;213m⡺[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⡮[0m[48;2;218;226;214m⡗[0m[48;2;218;226;214m⠋[0m[48;2;115;144;139m⠠[0m[48;2;65;104;102m⠈[0m[48;2;63;103;101m⡀[0m[48;2;58;99;98m⠂[0m[48;2;215;223;212m⡯[0m[48;2;218;226;214m⡯[0m[48;2;219;227;215m⠎[0m[48;2;78;115;112m⢀[0m[48;2;64;104;102m⠂[0m[48;2;63;103;101m⢁[0m[48;2;53;95;94m⢰[0m[48;2;228;234;221m⡳[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⢯[0m[48;2;218;226;214m⣳[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⡽[0m
[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣫[0m[48;2;217;226;214m⣻[0m[48;2;211;220;209m⠂[0m[48;2;59;100;98m⢈[0m[48;2;63;103;101m⠀[0m[48;2;65;105;103m⠄[0m[48;2;166;185;176m⡯[0m[48;2;216;224;212m⣫[0m[48;2;227;233;221m⡎[0m[48;2;55;97;95m⠀[0m[48;2;63;103;101m⠂[0m[48;2;65;105;103m⡐[0m[48;2;129;155;149m⢸[0m[48;2;214;223;211m⡺[0m[48;2;104;135;131m⣄[0m[48;2;56;97;96m⠠[0m[48;2;231;237;224m⠉[0m[48;2;229;235;222m⡉[0m[48;2;183;198;188m⠄[0m[48;2;59;100;98m⢁[0m[48;2;63;103;101m⠈[0m[48;2;63;103;101m⣠[0m[48;2;57;99;97m⣢[0m[48;2;133;159;152m⡻[0m[48;2;195;208;197m⣆[0m[48;2;63;103;101m⠠[0m[48;2;63;103;101m⠁[0m[48;2;64;104;102m⠂[0m[48;2;74;112;109m⡈[0m[48;2;222;229;217m⢉[0m[48;2;223;230;217m⠈[0m[48;2;192;205;195m⢄[0m[48;2;69;108;105m⡰[0m[48;2;66;105;103m⣼[0m[48;2;122;149;144m⡐[0m[48;2;65;105;103m⠠[0m[48;2;63;103;101m⠐[0m[48;2;63;103;101m⢀[0m[48;2;123;151;145m⠡[0m[48;2;140;164;157m⠐[0m[48;2;56;98;96m⣰[0m[48;2;62;102;100m⡀[0m[48;2;63;103;101m⠐[0m[48;2;63;103;101m⡀[0m[48;2;59;100;99m⠄[0m[48;2;138;163;156m⢈[0m[48;2;143;167;159m⣜[0m[48;2;217;225;213m⣗[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⡺[0m[48;2;218;226;214m⡽[0m[48;2;218;226;214m⣕[0m[48;2;218;226;214m⢷[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢝[0m[48;2;218;226;214m⡽[0m
[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⡺[0m[48;2;89;124;120m⣔[0m[48;2;59;100;98m⢦[0m[48;2;57;98;97m⢖[0m[48;2;46;89;89m⡼[0m[48;2;229;235;222m⣝[0m[48;2;218;226;214m⢞[0m[48;2;224;230;218m⣞[0m[48;2;59;100;98m⡤[0m[48;2;62;102;100m⡥[0m[48;2;61;101;100m⡤[0m[48;2;49;92;91m⡦[0m[48;2;220;228;216m⣻[0m[48;2;216;224;213m⡪[0m[48;2;207;218;206m⣗[0m[48;2;66;106;104m⢦[0m[48;2;52;94;93m⢦[0m[48;2;52;94;93m⢦[0m[48;2;57;98;97m⣲[0m[48;2;125;152;146m⢳[0m[48;2;228;234;222m⡳[0m[48;2;217;225;213m⡵[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⢮[0m[48;2;227;233;220m⡳[0m[48;2;96;129;125m⡧[0m[48;2;53;95;94m⡦[0m[48;2;52;94;93m⡦[0m[48;2;53;95;94m⣲[0m[48;2;82;118;115m⡺[0m[48;2;198;210;199m⣪[0m[48;2;219;227;215m⢞[0m[48;2;218;226;214m⣞[0m[48;2;219;227;215m⢞[0m[48;2;127;154;148m⢶[0m[48;2;52;94;93m⢴[0m[48;2;52;94;93m⢔[0m[48;2;65;105;103m⣖[0m[48;2;179;196;186m⢯[0m[48;2;218;226;214m⣳[0m[48;2;226;232;220m⡳[0m[48;2;82;119;115m⡵[0m[48;2;52;94;93m⡴[0m[48;2;53;95;93m⣔[0m[48;2;145;168;161m⣗[0m[48;2;218;226;214m⢵[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⣻[0m[48;2;218;226;214m⡪[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣳[0m[48;2;218;226;214m⢝[0m[48;2;218;226;214m⣞[0m
[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⡮[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢽[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⡯[0m[48;2;218;226;214m⣺[0m[48;2;218;226;214m⢕[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⢽[0m[48;2;217;225;213m⢝[0m[48;2;217;225;213m⣝[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⢯[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⣳[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⢯[0m[48;2;218;226;214m⡺[0m[48;2;218;226;214m⡵[0m[48;2;218;226;214m⣫[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⣳[0m[48;2;218;226;214m⡳[0m[48;2;218;226;214m⡽[0m[48;2;218;226;214m⣝[0m[48;2;218;226;214m⢵[0m[48;2;218;226;214m⣫[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⣳[0m[48;2;218;226;214m⡣[0m[48;2;218;226;214m⣯[0m[48;2;218;226;214m⡺[0m[48;2;218;226;214m⣕[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⢵[0m[48;2;218;226;214m⡫[0m[48;2;218;226;214m⣞[0m[48;2;218;226;214m⢵[0m[48;2;218;226;214m⡫[0m[48;2;218;226;214m⣞[0m[48;2;218;226;214m⢮[0m[48;2;218;226;214m⣻[0m[48;2;218;226;214m⡪[0m[48;2;218;226;214m⣗[0m[48;2;218;226;214m⣗[0m
`
cmd := &cobra.Command{
Use: "version",
Example: `
ksctl version --help
`,
Short: "ksctl version",
Long: "To get version for ksctl components",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(logoKsctl)
k.l.Note(k.Ctx, "Components", color.HiGreenString("ksctl:cli"), color.HiBlueString(config.Version), color.HiGreenString("ksctl:core"), color.HiBlueString(config.KsctlCoreVer))
k.l.Note(k.Ctx, "Build Information", "date", config.BuildDate)
},
}
return cmd
}
0707010000001B000041ED0000000000000000000000026818E78D00000000000000000000000000000000000000000000001000000000ksctl-2.8.0/gen0707010000001C000081A40000000000000000000000016818E78D000005E8000000000000000000000000000000000000001800000000ksctl-2.8.0/gen/docs.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"github.com/ksctl/cli/v2/cmd"
"github.com/spf13/cobra/doc"
)
func filePrepender(filename string) string {
cmdName := filepath.Base(filename)
cmdName = cmdName[:len(cmdName)-3] // Remove .md extension
return fmt.Sprintf(`---
title: %s
description: Command documentation for %s
---
`, cmdName, cmdName)
}
func linkHandler(name string) string {
return name
}
func main() {
outputDir := filepath.Join("gen", "docs")
if err := os.MkdirAll(outputDir, 0755); err != nil {
log.Fatal(err)
}
c, err := cmd.New()
if err != nil {
c.CliLog.Error("cli initialization failed", "Reason", err)
os.Exit(1)
}
cc, err := c.ForDocs()
if err != nil {
c.CliLog.Error("failed to get the cobra root command", "Reason", err)
os.Exit(1)
}
if err := doc.GenMarkdownTreeCustom(cc, outputDir, filePrepender, linkHandler); err != nil {
log.Fatal(err)
}
}
0707010000001D000081A40000000000000000000000016818E78D00002C72000000000000000000000000000000000000001300000000ksctl-2.8.0/go.modmodule github.com/ksctl/cli/v2
go 1.24.1
require (
github.com/charmbracelet/bubbles v0.21.0
github.com/charmbracelet/bubbletea v1.3.4
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834
github.com/creack/pty v1.1.24
github.com/fatih/color v1.18.0
github.com/ksctl/ksctl/v2 v2.6.0
github.com/pterm/pterm v0.12.80
github.com/rodaine/table v1.3.0
github.com/spf13/cobra v1.9.1
golang.org/x/mod v0.22.0
golang.org/x/term v0.31.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/client-go v0.32.2
)
require (
al.essio.dev/pkg/shellescape v1.5.1 // indirect
atomicgo.dev/cursor v0.2.0 // indirect
atomicgo.dev/keyboard v0.2.9 // indirect
atomicgo.dev/schedule v0.1.0 // indirect
dario.cat/mergo v1.0.1 // indirect
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6 v6.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.3.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.3.0 // indirect
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
github.com/Masterminds/squirrel v1.5.4 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2 v1.36.2 // indirect
github.com/aws/aws-sdk-go-v2/config v1.28.7 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.48 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.33 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.33 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.1 // indirect
github.com/aws/aws-sdk-go-v2/service/eks v1.56.0 // indirect
github.com/aws/aws-sdk-go-v2/service/iam v1.39.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect
github.com/aws/aws-sdk-go-v2/service/pricing v1.32.8 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 // indirect
github.com/aws/smithy-go v1.22.2 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/x/ansi v0.8.0 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/containerd/console v1.0.3 // indirect
github.com/containerd/containerd v1.7.23 // indirect
github.com/containerd/errdefs v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/cyphar/filepath-securejoin v0.3.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/cli v26.1.1+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v28.0.0+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/evanphx/json-patch v5.9.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.20.4 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/gosuri/uitable v0.0.4 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jmoiron/sqlx v1.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/lithammer/fuzzysearch v1.1.8 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/spdystream v0.5.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.19.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rubenv/sql-migrate v1.7.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
go.mongodb.org/mongo-driver v1.17.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
go.opentelemetry.io/otel v1.28.0 // indirect
go.opentelemetry.io/otel/metric v1.28.0 // indirect
go.opentelemetry.io/otel/trace v1.28.0 // indirect
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/time v0.7.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 // indirect
google.golang.org/grpc v1.65.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gotest.tools/v3 v3.5.1 // indirect
helm.sh/helm/v3 v3.16.4 // indirect
k8s.io/api v0.32.2 // indirect
k8s.io/apiextensions-apiserver v0.32.2 // indirect
k8s.io/apimachinery v0.32.2 // indirect
k8s.io/apiserver v0.32.2 // indirect
k8s.io/cli-runtime v0.31.3 // indirect
k8s.io/component-base v0.32.2 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
k8s.io/kubectl v0.31.3 // indirect
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
oras.land/oras-go v1.2.5 // indirect
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/kind v0.27.0 // indirect
sigs.k8s.io/kustomize/api v0.17.2 // indirect
sigs.k8s.io/kustomize/kyaml v0.17.1 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)
0707010000001E000081A40000000000000000000000016818E78D00012134000000000000000000000000000000000000001300000000ksctl-2.8.0/go.sumal.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXyho=
al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890=
atomicgo.dev/assert v0.0.2 h1:FiKeMiZSgRrZsPo9qn/7vmr7mCsh5SZyXY4YGYiYwrg=
atomicgo.dev/assert v0.0.2/go.mod h1:ut4NcI3QDdJtlmAxQULOmA13Gz6e2DWbSAS8RUOmNYQ=
atomicgo.dev/cursor v0.2.0 h1:H6XN5alUJ52FZZUkI7AlJbUc1aW38GWZalpYRPpoPOw=
atomicgo.dev/cursor v0.2.0/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU=
atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8=
atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ=
atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs=
atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0 h1:+m0M/LFxN43KvULkDNfdXOgrjtg6UYJPFBJyuEcRCAw=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0/go.mod h1:PwOyop78lveYMRs6oCxjiVyBdyCgIYH6XHIVZO9/SFQ=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6 v6.3.0 h1:Dc9miZr1Mhaqbb3cmJCRokkG16uk8JKkqOADf084zy4=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v6 v6.3.0/go.mod h1:CHo9QYhWEvrKVeXsEMJSl2bpmYYNu6aG12JsSaFBXlY=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v5 v5.0.0 h1:5n7dPVqsWfVKw+ZiEKSd3Kzu7gwBkbEBkeXb8rgaE9Q=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v5 v5.0.0/go.mod h1:HcZY0PHPo/7d75p99lB6lK0qYOP4vLRJUBpiehYXtLQ=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.3.0 h1:4ZTvMq5AWtRIPM06RzdfKwKyVJ0eUOfm4QUBVDQFqQ4=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6 v6.3.0/go.mod h1:drbnYtukMoZqUQq9hJASf41w3RB4VoTJPoPpe+XDHPU=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.0.0 h1:lMW1lD/17LUA5z1XTURo7LcVG2ICBPlyMHjIUrcFZNQ=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.0.0/go.mod h1:ceIuwmxDWptoW3eCqSXlnPsZFKh4X+R38dWPv7GS9Vs=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0 h1:PTFGRSlMKCQelWwxUyYVEUqseBJVemLyqWJjvMyt0do=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0/go.mod h1:LRr2FzBTQlONPPa5HREE5+RjSCTXl7BwOvYOaWTqCaI=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsIIvxVT+uE6yrNldntJKlLRgxGbZ85kgtz5SNBhMw=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0/go.mod h1:AW8VEadnhw9xox+VaVd9sP7NjzOAnaZBLRH6Tq3cJ38=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0 h1:pPvTJ1dY0sA35JOeFq6TsY2xj6Z85Yo23Pj4wCCvu4o=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0/go.mod h1:mLfWfj8v3jfWKsL9G4eoBoXVcsqcIUTapmdKy7uGOp0=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 h1:QM6sE5k2ZT/vI5BEe0r7mqjsUSnhVBFbOsVkEuaEfiA=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0/go.mod h1:243D9iHbcQXoFUtgHJwL7gl2zx1aDuDMjvBZVGr2uW0=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0 h1:wxQx2Bt4xzPIKvW59WQf1tJNx/ZZKPfN+EhPX3Z6CYY=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.3.0/go.mod h1:TpiwjwnW/khS0LKs4vW5UmmT9OWcxaveS8U7+tlknzo=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ=
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs=
github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8=
github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII=
github.com/MarvinJWendt/testza v0.2.10/go.mod h1:pd+VWsoGUiFtq+hRKSU1Bktnn+DMCSrDrXDpX2bG66k=
github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzXjB69adAhzZkI=
github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c=
github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE=
github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4=
github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZNZQ=
github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
github.com/aws/aws-sdk-go-v2 v1.36.2 h1:Ub6I4lq/71+tPb/atswvToaLGVMxKZvjYDVOWEExOcU=
github.com/aws/aws-sdk-go-v2 v1.36.2/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
github.com/aws/aws-sdk-go-v2/config v1.28.7 h1:GduUnoTXlhkgnxTD93g1nv4tVPILbdNQOzav+Wpg7AE=
github.com/aws/aws-sdk-go-v2/config v1.28.7/go.mod h1:vZGX6GVkIE8uECSUHB6MWAUsd4ZcG2Yq/dMa4refR3M=
github.com/aws/aws-sdk-go-v2/credentials v1.17.48 h1:IYdLD1qTJ0zanRavulofmqut4afs45mOWEI+MzZtTfQ=
github.com/aws/aws-sdk-go-v2/credentials v1.17.48/go.mod h1:tOscxHN3CGmuX9idQ3+qbkzrjVIx32lqDSU1/0d/qXs=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 h1:kqOrpojG71DxJm/KDPO+Z/y1phm1JlC8/iT+5XRmAn8=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22/go.mod h1:NtSFajXVVL8TA2QNngagVZmUtXciyrHOt7xgz4faS/M=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.33 h1:knLyPMw3r3JsU8MFHWctE4/e2qWbPaxDYLlohPvnY8c=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.33/go.mod h1:EBp2HQ3f+XCB+5J+IoEbGhoV7CpJbnrsd4asNXmTL0A=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.33 h1:K0+Ne08zqti8J9jwENxZ5NoUyBnaFDTu3apwQJWrwwA=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.33/go.mod h1:K97stwwzaWzmqxO8yLGHhClbVW1tC6VT1pDLk1pGrq4=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.1 h1:YbNopxjd9baM83YEEmkaYHi+NuJt0AszeaSLqo0CVr0=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.198.1/go.mod h1:mwr3iRm8u1+kkEx4ftDM2Q6Yr0XQFBKrP036ng+k5Lk=
github.com/aws/aws-sdk-go-v2/service/eks v1.56.0 h1:x31cGGE/t/QkrHVh5m2uWvYwDiaDXpj88nh6OdnI5r0=
github.com/aws/aws-sdk-go-v2/service/eks v1.56.0/go.mod h1:kNUWaiotRWCnfQlprrxSMg8ALqbZyA9xLCwKXuLumSk=
github.com/aws/aws-sdk-go-v2/service/iam v1.39.2 h1:2JLLGua711n8vn773xw2iwGh0zxLJJ3UDWQ2L7fy0wY=
github.com/aws/aws-sdk-go-v2/service/iam v1.39.2/go.mod h1:ZpAQJqd/i2bgRVa4vTa1ZX96sWgd3MZ/dxkABRXqvyI=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQzZHqe/3FE+cqwfH+0p5Jo8PFM/QYQSmeZ+M=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0=
github.com/aws/aws-sdk-go-v2/service/pricing v1.32.8 h1:R3X3UwwZKYLCNVVeJ+WLefvrjI5HonYCMlf40BYvJ8E=
github.com/aws/aws-sdk-go-v2/service/pricing v1.32.8/go.mod h1:4kkTK4zhY31emmt9VGgq3S+ElECNsiI5h6bqSBt71b0=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 h1:CvuUmnXI7ebaUAhbJcDy9YQx8wHR69eZ9I7q5hszt/g=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.8/go.mod h1:XDeGv1opzwm8ubxddF0cgqkZWsyOtw4lr6dxwmb6YQg=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 h1:F2rBfNAL5UyswqoeWv9zs74N/NanhK16ydHW1pahX6E=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7/go.mod h1:JfyQ0g2JG8+Krq0EuZNnRwX0mU0HrwY/tG6JNfcqh4k=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 h1:Xgv/hyNgvLda/M9l9qxXc4UFSgppnRczLxlMs5Ae/QY=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.3/go.mod h1:5Gn+d+VaaRgsjewpMvGazt0WfcFO+Md4wLOuBfGR9Bc=
github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70=
github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng=
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk=
github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI=
github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE=
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA=
github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ=
github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw=
github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM=
github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4=
github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8=
github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc=
github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v26.1.1+incompatible h1:bE1/uE2tCa08fMv+7ikLR/RDPoCqytwrLtkIkSzxLvw=
github.com/docker/cli v26.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v28.0.0+incompatible h1:Olh0KS820sJ7nPsBKChVhk5pzqcwDR15fumfAd/p9hM=
github.com/docker/docker v28.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4=
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI=
github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs=
github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU=
github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k=
github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 h1:SJ+NtwL6QaZ21U+IrK7d0gGgpjGGvd2kz+FzTHVzdqI=
github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2/go.mod h1:Tv1PlzqC9t8wNnpPdctvtSUOPUUg4SHeE6vR1Ir2hmg=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
github.com/gookit/goutil v0.6.18 h1:MUVj0G16flubWT8zYVicIuisUiHdgirPAkmnfD2kKgw=
github.com/gookit/goutil v0.6.18/go.mod h1:AY/5sAwKe7Xck+mEbuxj0n/bc3qwrGNe3Oeulln7zBA=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY=
github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/ksctl/ksctl/v2 v2.6.0 h1:Rt74UievbnEECZBzy92OsECqafgXihMyGIlKu2aozS4=
github.com/ksctl/ksctl/v2 v2.6.0/go.mod h1:0+AcbNu7OpWLX3a6GU9ZhkZSG8oJDfOrnoJ9KUXAUdE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY=
github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI=
github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg=
github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE=
github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU=
github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE=
github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8=
github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s=
github.com/pterm/pterm v0.12.80 h1:mM55B+GnKUnLMUSqhdINe4s6tOuVQIetQ3my8JGyAIg=
github.com/pterm/pterm v0.12.80/go.mod h1:c6DeF9bSnOSeFPZlfs4ZRAFcf5SCoTwvwQ5xaKGQlHo=
github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4=
github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rodaine/table v1.3.0 h1:4/3S3SVkHnVZX91EHFvAMV7K42AnJ0XuymRR2C5HlGE=
github.com/rodaine/table v1.3.0/go.mod h1:47zRsHar4zw0jgxGxL9YtFfs7EGN6B/TaS+/Dmk4WxU=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rubenv/sql-migrate v1.7.0 h1:HtQq1xyTN2ISmQDggnh0c9U3JlP8apWh8YO2jzlXpTI=
github.com/rubenv/sql-migrate v1.7.0/go.mod h1:S4wtDEG1CKn+0ShpTtzWhFpHHI5PvCUtiGI+C+Z2THE=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM=
go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY=
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
helm.sh/helm/v3 v3.16.4 h1:rBn/h9MACw+QlhxQTjpl8Ifx+VTWaYsw3rguGBYBzr0=
helm.sh/helm/v3 v3.16.4/go.mod h1:k8QPotUt57wWbi90w3LNmg3/MWcLPigVv+0/X4B8BzA=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw=
k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y=
k8s.io/apiextensions-apiserver v0.32.2 h1:2YMk285jWMk2188V2AERy5yDwBYrjgWYggscghPCvV4=
k8s.io/apiextensions-apiserver v0.32.2/go.mod h1:GPwf8sph7YlJT3H6aKUWtd0E+oyShk/YHWQHf/OOgCA=
k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ=
k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/apiserver v0.32.2 h1:WzyxAu4mvLkQxwD9hGa4ZfExo3yZZaYzoYvvVDlM6vw=
k8s.io/apiserver v0.32.2/go.mod h1:PEwREHiHNU2oFdte7BjzA1ZyjWjuckORLIK/wLV5goM=
k8s.io/cli-runtime v0.31.3 h1:fEQD9Xokir78y7pVK/fCJN090/iYNrLHpFbGU4ul9TI=
k8s.io/cli-runtime v0.31.3/go.mod h1:Q2jkyTpl+f6AtodQvgDI8io3jrfr+Z0LyQBPJJ2Btq8=
k8s.io/client-go v0.32.2 h1:4dYCD4Nz+9RApM2b/3BtVvBHw54QjMFUl1OLcJG5yOA=
k8s.io/client-go v0.32.2/go.mod h1:fpZ4oJXclZ3r2nDOv+Ux3XcJutfrwjKTCHz2H3sww94=
k8s.io/component-base v0.32.2 h1:1aUL5Vdmu7qNo4ZsE+569PV5zFatM9hl+lb3dEea2zU=
k8s.io/component-base v0.32.2/go.mod h1:PXJ61Vx9Lg+P5mS8TLd7bCIr+eMJRQTyXe8KvkrvJq0=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
k8s.io/kubectl v0.31.3 h1:3r111pCjPsvnR98oLLxDMwAeM6OPGmPty6gSKaLTQes=
k8s.io/kubectl v0.31.3/go.mod h1:lhMECDCbJN8He12qcKqs2QfmVo9Pue30geovBVpH5fs=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo=
oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
sigs.k8s.io/kind v0.27.0 h1:PQ3f0iAWNIj66LYkZ1ivhEg/+Zb6UPMbO+qVei/INZA=
sigs.k8s.io/kind v0.27.0/go.mod h1:RZVFmy6qcwlSWwp6xeIUv7kXCPF3i8MXsEXxW/J+gJY=
sigs.k8s.io/kustomize/api v0.17.2 h1:E7/Fjk7V5fboiuijoZHgs4aHuexi5Y2loXlVOAVAG5g=
sigs.k8s.io/kustomize/api v0.17.2/go.mod h1:UWTz9Ct+MvoeQsHcJ5e+vziRRkwimm3HytpZgIYqye0=
sigs.k8s.io/kustomize/kyaml v0.17.1 h1:TnxYQxFXzbmNG6gOINgGWQt09GghzgTP6mIurOgrLCQ=
sigs.k8s.io/kustomize/kyaml v0.17.1/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U=
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
0707010000001F000081A40000000000000000000000016818E78D00000345000000000000000000000000000000000000001200000000ksctl-2.8.0/kt.md## here is the kt for the profiling of the code cli
```go
package main
import (
"fmt"
"log"
"net/http"
_ "net/http/pprof"
"os"
"os/signal"
"sync"
"github.com/ksctl/cli/cli/cmd"
)
func main() {
cc := make(chan os.Signal, 1)
signal.Notify(cc, os.Interrupt)
signal.Notify(cc, os.Kill)
wg := new(sync.WaitGroup)
go func() {
wg.Add(1)
fmt.Println(http.ListenAndServe("localhost:8080", nil))
}()
cmd.Execute()
select {
case sig := <-cc:
log.Println("Received terminate, graceful shutdown", sig)
os.Exit(1)
}
wg.Wait()
}
```
```shell
go run . create local -n demo --yes
```
```shell
go tool pprof \
-raw -output=cpu.txt \
'http://localhost:8080/debug/pprof/profile?seconds=120'
```
```shell
./stackcollapse-go.pl cpu.txt | flamegraph.pl > flame.svg
```
<https://github.com/brendangregg/FlameGraph>
07070100000020000081A40000000000000000000000016818E78D00000383000000000000000000000000000000000000001400000000ksctl-2.8.0/main.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"os"
"github.com/ksctl/cli/v2/cmd"
)
func main() {
c, err := cmd.New()
if err != nil {
c.CliLog.Error("cli initialization failed", "Reason", err)
os.Exit(1)
}
err = c.Execute()
if err != nil {
c.CliLog.Error("command execution failed", "Reason", err)
os.Exit(1)
}
}
07070100000021000041ED0000000000000000000000026818E78D00000000000000000000000000000000000000000000001000000000ksctl-2.8.0/pkg07070100000022000041ED0000000000000000000000026818E78D00000000000000000000000000000000000000000000001400000000ksctl-2.8.0/pkg/cli07070100000023000081A40000000000000000000000016818E78D00001B44000000000000000000000000000000000000002400000000ksctl-2.8.0/pkg/cli/blueprint_ui.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"encoding/json"
"fmt"
"io"
"strings"
"github.com/charmbracelet/lipgloss"
"github.com/fatih/color"
"github.com/ksctl/ksctl/v2/pkg/handler/cluster/controller"
)
// BlueprintUI is responsible for rendering the cluster blueprint with enhanced UI
type BlueprintUI struct {
writer io.Writer
}
// NewBlueprintUI creates a new instance of BlueprintUI
func NewBlueprintUI(w io.Writer) *BlueprintUI {
return &BlueprintUI{
writer: w,
}
}
// RenderClusterBlueprint renders the cluster metadata with enhanced UI
func (ui *BlueprintUI) RenderClusterBlueprint(meta controller.Metadata) {
parentBox := lipgloss.NewStyle().
BorderStyle(lipgloss.RoundedBorder()).
BorderForeground(lipgloss.Color("8")).
Padding(1, 2).
Width(80).
Align(lipgloss.Center)
banner := lipgloss.NewStyle().
Padding(0, 1).
Width(70).
Align(lipgloss.Center)
sectionTitle := lipgloss.NewStyle().
Foreground(lipgloss.Color("13")).
Bold(true).
MarginTop(1).
Padding(0, 1)
infoBlock := lipgloss.NewStyle().
BorderStyle(lipgloss.RoundedBorder()).
BorderForeground(lipgloss.Color("10")).
Padding(1, 2).
MarginTop(1).
Width(70)
keyValueRow := func(key, value string) string {
return lipgloss.JoinHorizontal(lipgloss.Top,
lipgloss.NewStyle().Foreground(lipgloss.Color("14")).PaddingRight(3).Width(22).Align(lipgloss.Left).Render(key),
lipgloss.NewStyle().Width(40).Render(value),
)
}
var parentBoxContent strings.Builder
bannerContent := fmt.Sprintf("✨ %s ✨\n\n%s",
lipgloss.NewStyle().Bold(true).Align(lipgloss.Center).Foreground(lipgloss.Color("#FFFFFF")).Render("Cluster Blueprint"),
lipgloss.NewStyle().Italic(true).Align(lipgloss.Center).Foreground(lipgloss.Color("#DDDDDD")).Render("Your Kubernetes cluster plan"))
parentBoxContent.WriteString(banner.Render(bannerContent))
parentBoxContent.WriteString("\n\n")
{
var content strings.Builder
content.WriteString(keyValueRow("Name", meta.ClusterName))
content.WriteString("\n")
content.WriteString(keyValueRow("Region", meta.Region))
content.WriteString("\n")
content.WriteString(keyValueRow("Cloud", string(meta.Provider)))
content.WriteString("\n")
content.WriteString(keyValueRow("Type", string(meta.ClusterType)))
contentBlock := infoBlock.Render(content.String())
titleBlock := sectionTitle.Render("🔑 Key Attributes")
fullSection := lipgloss.JoinVertical(lipgloss.Left, titleBlock, contentBlock)
parentBoxContent.WriteString(fullSection)
parentBoxContent.WriteString("\n")
}
// Infrastructure section
if meta.NoCP > 0 || meta.NoWP > 0 || meta.NoDS > 0 || len(meta.ManagedNodeType) > 0 {
var content strings.Builder
if meta.NoCP > 0 {
content.WriteString(keyValueRow("Control Plane", fmt.Sprintf("%d × %s", meta.NoCP, color.HiMagentaString(meta.ControlPlaneNodeType))))
content.WriteString("\n")
}
if meta.NoWP > 0 {
content.WriteString(keyValueRow("Worker Nodes", fmt.Sprintf("%d × %s", meta.NoWP, color.HiMagentaString(meta.WorkerPlaneNodeType))))
content.WriteString("\n")
}
if meta.NoDS > 0 {
content.WriteString(keyValueRow("Etcd Nodes", fmt.Sprintf("%d × %s", meta.NoDS, color.HiMagentaString(meta.DataStoreNodeType))))
content.WriteString("\n")
}
if meta.LoadBalancerNodeType != "" {
content.WriteString(keyValueRow("Load Balancer", color.HiMagentaString(meta.LoadBalancerNodeType)))
content.WriteString("\n")
}
if len(meta.ManagedNodeType) > 0 {
content.WriteString(keyValueRow("Managed Nodes", fmt.Sprintf("%d × %s", meta.NoMP, color.HiMagentaString(meta.ManagedNodeType))))
}
// Trim trailing newline if present
contentStr := strings.TrimSuffix(content.String(), "\n")
contentBlock := infoBlock.Render(contentStr)
titleBlock := sectionTitle.Render("💻 Infrastructure")
fullSection := lipgloss.JoinVertical(lipgloss.Left, titleBlock, contentBlock)
parentBoxContent.WriteString(fullSection)
parentBoxContent.WriteString("\n")
}
// Kubernetes configuration section
if meta.K8sDistro != "" || meta.EtcdVersion != "" || meta.K8sVersion != "" {
var content strings.Builder
if meta.K8sDistro != "" {
content.WriteString(keyValueRow("Bootstrap Provider", string(meta.K8sDistro)))
content.WriteString("\n")
}
if meta.K8sVersion != "" {
content.WriteString(keyValueRow("Kubernetes Version", meta.K8sVersion))
content.WriteString("\n")
}
if meta.EtcdVersion != "" {
content.WriteString(keyValueRow("Etcd Version", meta.EtcdVersion))
}
contentStr := strings.TrimSuffix(content.String(), "\n")
contentBlock := infoBlock.Render(contentStr)
titleBlock := sectionTitle.Render("🔧 Kubernetes Configuration")
fullSection := lipgloss.JoinVertical(lipgloss.Left, titleBlock, contentBlock)
parentBoxContent.WriteString(fullSection)
parentBoxContent.WriteString("\n")
}
if len(meta.Addons) > 0 {
var contentBuilder strings.Builder
for i, addon := range meta.Addons {
addonTitle := color.HiMagentaString(addon.Name)
config := addon.Config
vConfig := ""
if config == nil {
vConfig = "No configuration available"
} else {
var v any
if err := json.Unmarshal([]byte(*config), &v); err != nil {
vConfig = "Invalid configuration format"
} else {
_v, _ := json.MarshalIndent(v, "\t", " ")
vConfig = string(_v)
}
}
addonInfo := fmt.Sprintf("%s\n\t%s: %s\n\t%s: %s",
addonTitle,
color.HiCyanString("From"),
color.HiGreenString(addon.Label),
color.HiCyanString("Config"),
color.HiGreenString(vConfig),
)
if addon.IsCNI {
addonInfo += "\n\t" + color.HiCyanString("CNI Add-on")
}
contentBuilder.WriteString(addonInfo)
if i < len(meta.Addons)-1 {
contentBuilder.WriteString("\n\n") // Add spacing between addons
}
}
contentBlock := infoBlock.Render(contentBuilder.String())
titleBlock := sectionTitle.Render("🧩 Add-ons")
fullSection := lipgloss.JoinVertical(lipgloss.Left, titleBlock, contentBlock)
parentBoxContent.WriteString(fullSection)
}
noteStyle := lipgloss.NewStyle().
Italic(true).
Foreground(lipgloss.Color("#919191")).
Padding(1, 0).
MarginTop(1).
Align(lipgloss.Center)
parentBoxContent.WriteString("\n\n")
parentBoxContent.WriteString(noteStyle.Render("Your cluster will be provisioned with these specifications"))
fmt.Fprintln(ui.writer, parentBox.Render(parentBoxContent.String()))
}
07070100000024000081A40000000000000000000000016818E78D00001C11000000000000000000000000000000000000002800000000ksctl-2.8.0/pkg/cli/card_recommend_1.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"fmt"
"github.com/fatih/color"
"github.com/ksctl/ksctl/v2/pkg/consts"
"github.com/ksctl/ksctl/v2/pkg/provider/optimizer"
"strings"
)
type cardRecommendation struct {
rr optimizer.RegionRecommendation
oo *optimizer.RecommendationAcrossRegions
clusterType consts.KsctlClusterType
}
const (
RenewableLowThreshold = 10.0
RenewableMediumThreshold = 40.0
LowCarbonLowThreshold = 10.0
LowCarbonMediumThreshold = 40.0
DirectCo2LowThreshold = 200.0
DirectCo2MediumThreshold = 400.0
LCACarbonIntensityMediumThreshold = 100.0
LCACarbonIntensityHighThreshold = 200.0
)
func (c cardRecommendation) GetUpper() string {
priceStr := strings.Builder{}
priceDrop := (c.oo.CurrentTotalCost - c.rr.TotalCost) / c.oo.CurrentTotalCost * 100
priceStr.WriteString(fmt.Sprintf(
"Price: %s %s\n",
color.MagentaString(fmt.Sprintf("$%.2f", c.rr.TotalCost)),
color.HiGreenString(fmt.Sprintf("↓ %.0f%%", priceDrop)),
))
if c.rr.Region.Emission != nil {
dco2Val := fmt.Sprintf("%.2f %s", c.rr.Region.Emission.DirectCarbonIntensity, c.rr.Region.Emission.Unit)
if c.rr.Region.Emission.DirectCarbonIntensity > DirectCo2MediumThreshold {
dco2Val = color.HiRedString(dco2Val)
} else if c.rr.Region.Emission.DirectCarbonIntensity > DirectCo2LowThreshold {
dco2Val = color.HiYellowString(dco2Val)
} else {
dco2Val = color.HiGreenString(dco2Val)
}
renewableVal := fmt.Sprintf("%.1f%%", c.rr.Region.Emission.RenewablePercentage)
if c.rr.Region.Emission.RenewablePercentage < RenewableLowThreshold {
renewableVal = color.HiRedString(renewableVal)
} else if c.rr.Region.Emission.RenewablePercentage < RenewableMediumThreshold {
renewableVal = color.HiYellowString(renewableVal)
} else {
renewableVal = color.HiGreenString(renewableVal)
}
lowco2Val := fmt.Sprintf("%.1f%%", c.rr.Region.Emission.LowCarbonPercentage)
if c.rr.Region.Emission.LowCarbonPercentage < LowCarbonLowThreshold {
lowco2Val = color.HiRedString(lowco2Val)
} else if c.rr.Region.Emission.LowCarbonPercentage < LowCarbonMediumThreshold {
lowco2Val = color.HiYellowString(lowco2Val)
} else {
lowco2Val = color.HiGreenString(lowco2Val)
}
lcaintensityVal := fmt.Sprintf("%.1f %s", c.rr.Region.Emission.LCACarbonIntensity, c.rr.Region.Emission.Unit)
if c.rr.Region.Emission.LCACarbonIntensity > LCACarbonIntensityHighThreshold {
lcaintensityVal = color.HiRedString(lcaintensityVal)
} else if c.rr.Region.Emission.LCACarbonIntensity > LCACarbonIntensityMediumThreshold {
lcaintensityVal = color.HiYellowString(lcaintensityVal)
} else {
lcaintensityVal = color.HiGreenString(lcaintensityVal)
}
e_r := c.rr.Region.Emission
e_R := c.oo.CurrentRegion.Emission
if e_R != nil {
dco2Change := (e_r.DirectCarbonIntensity - e_R.DirectCarbonIntensity) / e_R.DirectCarbonIntensity * 100
if dco2Change > 0 {
dco2Val += color.HiRedString(fmt.Sprintf(" ↑ %.0f%%", dco2Change))
} else {
dco2Val += color.HiGreenString(fmt.Sprintf(" ↓ %.0f%%", -dco2Change))
}
renewableChange := (e_r.RenewablePercentage - e_R.RenewablePercentage) / e_R.RenewablePercentage * 100
if renewableChange > 0 {
renewableVal += color.HiGreenString(fmt.Sprintf(" ↑ %.0f%%", renewableChange))
} else {
renewableVal += color.HiRedString(fmt.Sprintf(" ↓ %.0f%%", -renewableChange))
}
lowco2Change := (e_r.LowCarbonPercentage - e_R.LowCarbonPercentage) / e_R.LowCarbonPercentage * 100
if lowco2Change > 0 {
lowco2Val += color.HiGreenString(fmt.Sprintf(" ↑ %.0f%%", lowco2Change))
} else {
lowco2Val += color.HiRedString(fmt.Sprintf(" ↓ %.0f%%", -lowco2Change))
}
lcaintensityChange := (e_r.LCACarbonIntensity - e_R.LCACarbonIntensity) / e_R.LCACarbonIntensity * 100
if lcaintensityChange > 0 {
lcaintensityVal += color.HiRedString(fmt.Sprintf(" ↑ %.0f%%", lcaintensityChange))
} else {
lcaintensityVal += color.HiGreenString(fmt.Sprintf(" ↓ %.0f%%", -lcaintensityChange))
}
}
priceStr.WriteString(fmt.Sprintf("🌍 Direct Co2: %s\n", dco2Val))
priceStr.WriteString(fmt.Sprintf("🌱 Renewable: %s\n", renewableVal))
priceStr.WriteString(fmt.Sprintf("💨 Low Carbon: %s\n", lowco2Val))
priceStr.WriteString(fmt.Sprintf("🔄 Lifecycle Co2: %s\n", lcaintensityVal))
} else {
priceStr.WriteString(color.HiYellowString("Emissions data is currently unavailable 🌍\n"))
}
return priceStr.String()
}
func (c cardRecommendation) GetLower() string {
specsStr := strings.Builder{}
specsStr.WriteString(fmt.Sprintf("Region: %s\n", color.HiCyanString(c.rr.Region.Name)))
if c.clusterType == consts.ClusterTypeSelfMang {
specsStr.WriteString(fmt.Sprintf("ControlPlane: %s x %d\n", c.oo.InstanceTypeCP, c.oo.ControlPlaneCount))
specsStr.WriteString(fmt.Sprintf("Worker: %s x %d\n", c.oo.InstanceTypeWP, c.oo.WorkerPlaneCount))
specsStr.WriteString(fmt.Sprintf("Etcd: %s x %d\n", c.oo.InstanceTypeDS, c.oo.DataStoreCount))
specsStr.WriteString(fmt.Sprintf("LoadBalancer: %s\n", c.oo.InstanceTypeLB))
} else {
specsStr.WriteString(fmt.Sprintf("ManagedOffering: %s\n", c.oo.ManagedOffering))
specsStr.WriteString(fmt.Sprintf("Worker: %s x %d\n", c.oo.InstanceTypeWP, c.oo.WorkerPlaneCount))
}
return specsStr.String()
}
type cardRecommendations struct {
mm consts.KsctlClusterType
oo *optimizer.RecommendationAcrossRegions
tt []cardRecommendation
lenOfItems int
}
func (c cardRecommendations) LenOfItems() int {
return c.lenOfItems
}
func (c cardRecommendations) GetItem(i int) CardItem {
return c.tt[i]
}
func (c cardRecommendations) GetInstruction() string {
instructions := "← → to navigate • enter to select plan • q to skip changing region"
instructions += " • Currently it costs " + fmt.Sprintf("`$%.2f`", c.oo.CurrentTotalCost) + " in " + color.HiCyanString(c.oo.CurrentRegion.Name)
return instructions
}
func (c cardRecommendations) GetResult(i int) string {
return c.oo.RegionRecommendations[i].Region.Sku
}
func (c cardRecommendations) GetCardConfiguration() (cardWidth, noOfVisibleItems int) {
return 45, 2
}
func ConverterForRecommendationIOutputForCards(oo *optimizer.RecommendationAcrossRegions, tt consts.KsctlClusterType) CardPack {
res := new(cardRecommendations)
res.oo = oo
res.mm = tt
res.lenOfItems = len(oo.RegionRecommendations)
for i, _ := range oo.RegionRecommendations {
res.tt = append(res.tt, cardRecommendation{
oo.RegionRecommendations[i],
oo,
tt,
})
}
return res
}
07070100000025000081A40000000000000000000000016818E78D00000EA6000000000000000000000000000000000000002300000000ksctl-2.8.0/pkg/cli/card_region.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"fmt"
"github.com/fatih/color"
"github.com/ksctl/ksctl/v2/pkg/provider"
"strings"
)
type cardRegion struct {
b provider.RegionOutput
}
func (c cardRegion) GetUpper() string {
priceStr := strings.Builder{}
if c.b.Emission != nil {
dco2Val := fmt.Sprintf("%.2f %s", c.b.Emission.DirectCarbonIntensity, c.b.Emission.Unit)
if c.b.Emission.DirectCarbonIntensity > DirectCo2MediumThreshold {
dco2Val = color.HiRedString(dco2Val)
} else if c.b.Emission.DirectCarbonIntensity > DirectCo2LowThreshold {
dco2Val = color.HiYellowString(dco2Val)
} else {
dco2Val = color.HiGreenString(dco2Val)
}
renewableVal := fmt.Sprintf("%.1f%%", c.b.Emission.RenewablePercentage)
if c.b.Emission.RenewablePercentage < RenewableLowThreshold {
renewableVal = color.HiRedString(renewableVal)
} else if c.b.Emission.RenewablePercentage < RenewableMediumThreshold {
renewableVal = color.HiYellowString(renewableVal)
} else {
renewableVal = color.HiGreenString(renewableVal)
}
lowco2Val := fmt.Sprintf("%.1f%%", c.b.Emission.LowCarbonPercentage)
if c.b.Emission.LowCarbonPercentage < LowCarbonLowThreshold {
lowco2Val = color.HiRedString(lowco2Val)
} else if c.b.Emission.LowCarbonPercentage < LowCarbonMediumThreshold {
lowco2Val = color.HiYellowString(lowco2Val)
} else {
lowco2Val = color.HiGreenString(lowco2Val)
}
lcaintensityVal := fmt.Sprintf("%.1f %s", c.b.Emission.LCACarbonIntensity, c.b.Emission.Unit)
if c.b.Emission.LCACarbonIntensity > LCACarbonIntensityHighThreshold {
lcaintensityVal = color.HiRedString(lcaintensityVal)
} else if c.b.Emission.LCACarbonIntensity > LCACarbonIntensityMediumThreshold {
lcaintensityVal = color.HiYellowString(lcaintensityVal)
} else {
lcaintensityVal = color.HiGreenString(lcaintensityVal)
}
priceStr.WriteString(fmt.Sprintf("🌍 Direct Co2: %s\n", dco2Val))
priceStr.WriteString(fmt.Sprintf("🌱 Renewable: %s\n", renewableVal))
priceStr.WriteString(fmt.Sprintf("💨 Low Carbon: %s\n", lowco2Val))
priceStr.WriteString(fmt.Sprintf("🔄 Lifecycle Co2: %s\n", lcaintensityVal))
} else {
priceStr.WriteString(color.HiYellowString("Emissions data is currently unavailable 🌍\n"))
}
return priceStr.String()
}
func (c cardRegion) GetLower() string {
return "Name: " + color.HiMagentaString(c.b.Name) + "\nCode: " + color.HiCyanString(c.b.Sku)
}
type cardRegions struct {
rr []provider.RegionOutput
tt []cardRegion
lenItems int
}
func (c cardRegions) LenOfItems() int {
return c.lenItems
}
func (c cardRegions) GetItem(i int) CardItem {
return c.tt[i]
}
func (c cardRegions) GetInstruction() string {
return "← → to navigate • enter to select region • q to quit"
}
func (c cardRegions) GetResult(i int) string {
return c.rr[i].Sku
}
func (c cardRegions) GetCardConfiguration() (cardWidth, noOfVisibleItems int) {
return 45, 2
}
func ConverterForRegionOutputForCards(regions provider.RegionsOutput) CardPack {
res := new(cardRegions)
res.lenItems = len(regions)
res.rr = regions
for i, _ := range regions {
res.tt = append(res.tt, cardRegion{regions[i]})
}
return res
}
07070100000026000081A40000000000000000000000016818E78D00000B01000000000000000000000000000000000000001F00000000ksctl-2.8.0/pkg/cli/card_vm.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"fmt"
"github.com/fatih/color"
"github.com/ksctl/ksctl/v2/pkg/provider"
"strings"
)
type cardVM struct {
rr provider.InstanceRegionOutput
}
func (c cardVM) GetUpper() string {
resp := strings.Builder{}
resp.WriteString(fmt.Sprintf(
"Price: %s\n",
color.HiMagentaString("%s %.2f", c.rr.Price.Currency, c.rr.GetCost()),
))
if c.rr.EmboddedEmissions != nil {
resp.WriteString(fmt.Sprintf("🏭 Embodied: %.2f %s\n", c.rr.EmboddedEmissions.EmboddedCo2, c.rr.EmboddedEmissions.Co2Unit))
} else {
resp.WriteString(color.HiYellowString("Emissions data is currently unavailable 🌍\n"))
}
return resp.String()
}
func (c cardVM) GetLower() string {
resp := strings.Builder{}
resp.WriteString(fmt.Sprintf("Code: %s\n", color.HiMagentaString(c.rr.Sku)))
resp.WriteString(fmt.Sprintf("vCPUs: %d\n", c.rr.VCpus))
resp.WriteString(fmt.Sprintf("Memory: %d GB\n", c.rr.Memory))
arch := string(c.rr.CpuArch)
if arch == string(provider.ArchArm64) {
arch = color.HiCyanString(arch)
} else {
arch = color.HiBlueString(arch)
}
resp.WriteString(fmt.Sprintf("Arch: %s\n", arch))
diskSku := c.rr.Disk.Sku
diskSize := c.rr.Disk.Size
diskTier := c.rr.Disk.Tier
if diskSku != nil {
resp.WriteString(fmt.Sprintf("Disk: %s\n", *diskSku))
}
if diskTier != nil {
resp.WriteString(fmt.Sprintf("DiskTier: %s\n", *diskTier))
}
if diskSize != nil {
resp.WriteString(fmt.Sprintf("DiskSize: %d GB", *diskSize))
}
return resp.String()
}
type cardVMs struct {
rr provider.InstancesRegionOutput
tt []cardVM
lenOfItems int
}
func (c cardVMs) LenOfItems() int {
return c.lenOfItems
}
func (c cardVMs) GetItem(i int) CardItem {
return c.tt[i]
}
func (c cardVMs) GetInstruction() string {
return "← → to navigate • enter to select instanceType • q to quit"
}
func (c cardVMs) GetResult(i int) string {
return c.rr[i].Sku
}
func (c cardVMs) GetCardConfiguration() (cardWidth, noOfVisibleItems int) {
return 33, 3
}
func ConverterForInstanceTypesForCards(vms provider.InstancesRegionOutput) CardPack {
res := new(cardVMs)
res.lenOfItems = len(vms)
res.rr = vms
for i, _ := range vms {
res.tt = append(res.tt, cardVM{vms[i]})
}
return res
}
07070100000027000081A40000000000000000000000016818E78D000018D2000000000000000000000000000000000000001D00000000ksctl-2.8.0/pkg/cli/cards.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"fmt"
"github.com/charmbracelet/bubbles/help"
"github.com/charmbracelet/bubbles/key"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"strings"
)
func (p *genericMenuDriven) CardSelection(pack CardPack) (string, error) {
t := tea.NewProgram(newCardRunner(pack))
finalModel, err := t.Run()
if err != nil {
return "", err
}
m, ok := finalModel.(cardRunner)
if !ok {
return "", fmt.Errorf("failed to cast final model to cardRunner type")
}
if m.selectedPlan >= 0 && m.selectedPlan < pack.LenOfItems() {
plan := pack.GetResult(m.selectedPlan)
return plan, nil
}
if m.quitting {
return "", nil
}
return "", fmt.Errorf("internal problem. invalid selected plan index: %d", m.selectedPlan)
}
type cardRunner struct {
b CardPack
currentPlan int
windowWidth int
windowHeight int
help help.Model
keys keyMap
quitting bool
selectedPlan int // -1 means no selection yet
}
type keyMap struct {
left key.Binding
right key.Binding
selected key.Binding
quit key.Binding
help key.Binding
}
func (k keyMap) ShortHelp() []key.Binding {
return []key.Binding{k.help, k.quit}
}
func (k keyMap) FullHelp() [][]key.Binding {
return [][]key.Binding{
{k.left, k.right, k.selected},
{k.help, k.quit},
}
}
func newKeyMap() keyMap {
return keyMap{
left: key.NewBinding(
key.WithKeys("left", "h"),
key.WithHelp("←/h", "previous plan"),
),
right: key.NewBinding(
key.WithKeys("right", "l"),
key.WithHelp("→/l", "next plan"),
),
selected: key.NewBinding(
key.WithKeys("enter", " "),
key.WithHelp("enter/space", "select plan"),
),
help: key.NewBinding(
key.WithKeys("?"),
key.WithHelp("?", "toggle help"),
),
quit: key.NewBinding(
key.WithKeys("q", "esc", "ctrl+c"),
key.WithHelp("q/esc", "quit"),
),
}
}
func newCardRunner(b CardPack) cardRunner {
return cardRunner{
b: b,
currentPlan: 0,
help: help.New(),
keys: newKeyMap(),
selectedPlan: -1,
}
}
func (m cardRunner) Init() tea.Cmd {
return nil
}
func (m cardRunner) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch {
case key.Matches(msg, m.keys.quit):
m.quitting = true
return m, tea.Quit
case key.Matches(msg, m.keys.help):
m.help.ShowAll = !m.help.ShowAll
return m, nil
case key.Matches(msg, m.keys.left):
// Move to previous plan
if m.currentPlan > 0 {
m.currentPlan--
}
return m, nil
case key.Matches(msg, m.keys.right):
// Move to next plan
if m.currentPlan < m.b.LenOfItems()-1 {
m.currentPlan++
}
return m, nil
case key.Matches(msg, m.keys.selected):
// Select current plan
m.selectedPlan = m.currentPlan
return m, tea.Quit
}
case tea.WindowSizeMsg:
m.windowWidth = msg.Width
m.windowHeight = msg.Height
return m, nil
}
return m, nil
}
func (m cardRunner) View() string {
if m.quitting {
if m.selectedPlan >= 0 && m.selectedPlan < m.b.LenOfItems() {
return m.b.GetResult(m.selectedPlan)
}
return ""
}
maxWidth := m.windowWidth
if maxWidth == 0 {
maxWidth = 100 // Default width
}
cardWidth, visibleCards := m.b.GetCardConfiguration()
// Add some top padding
var builder strings.Builder
builder.WriteString("\n\n")
// Determine which cards to show
var startIdx int
implLen := m.b.LenOfItems()
if m.currentPlan == 0 {
startIdx = 0
} else if m.currentPlan == implLen-1 {
startIdx = max(0, implLen-visibleCards)
} else {
startIdx = max(0, m.currentPlan-1)
}
endIdx := min(implLen, startIdx+visibleCards)
cards := make([]string, endIdx-startIdx)
for i := startIdx; i < endIdx; i++ {
plan := m.b.GetItem(i)
isActive := i == m.currentPlan
var borderColor, textColor, separatorColor lipgloss.Color
var borderStyle lipgloss.Border
if isActive {
borderColor = lipgloss.Color("10")
textColor = lipgloss.Color("#EEEEC7")
separatorColor = lipgloss.Color("#555555")
borderStyle = lipgloss.RoundedBorder()
} else {
borderColor = lipgloss.Color("#555555")
textColor = lipgloss.Color("#AAAAAA")
separatorColor = lipgloss.Color("#555555")
borderStyle = lipgloss.RoundedBorder()
}
priceStyle := lipgloss.NewStyle().
Foreground(textColor).
Width(cardWidth - 3).
Align(lipgloss.Center).
PaddingTop(1)
if isActive {
priceStyle = priceStyle.Bold(true)
}
priceSection := priceStyle.Render(plan.GetUpper())
separator := lipgloss.NewStyle().
Foreground(separatorColor).
Width(cardWidth - 3).
Align(lipgloss.Center).
PaddingTop(1).
PaddingBottom(1).
Render(strings.Repeat("─", cardWidth-6))
specsStyle := lipgloss.NewStyle().
Foreground(textColor).
Width(cardWidth - 3).
Align(lipgloss.Center).
PaddingLeft(1)
if isActive {
specsStyle = specsStyle.Bold(true)
}
specsSection := specsStyle.Render(plan.GetLower())
cardStyle := lipgloss.NewStyle().
Border(borderStyle).
BorderForeground(borderColor).
Width(cardWidth).
Padding(0, 1)
cardContent := lipgloss.JoinVertical(lipgloss.Left,
priceSection,
separator,
specsSection,
)
cards[i-startIdx] = cardStyle.Render(cardContent)
}
var rowContent strings.Builder
rowContent.WriteString(lipgloss.JoinHorizontal(lipgloss.Top, cards...))
centeredStyle := lipgloss.NewStyle().Width(maxWidth).Align(lipgloss.Center)
builder.WriteString(centeredStyle.Render(rowContent.String()))
builder.WriteString("\n\n")
instructionStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#888888")).
Align(lipgloss.Center).
Width(maxWidth)
builder.WriteString(instructionStyle.Render(m.b.GetInstruction() + "\n\n"))
return builder.String()
}
07070100000028000081A40000000000000000000000016818E78D0000178A000000000000000000000000000000000000001D00000000ksctl-2.8.0/pkg/cli/debug.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"bufio"
"fmt"
"io"
"os"
"strconv"
"strings"
"time"
"github.com/fatih/color"
)
type debugMenuDriven struct {
progress ProgressAnimation
}
func (p *debugMenuDriven) CardSelection(element CardPack) (string, error) {
for i := 0; i < element.LenOfItems(); i++ {
e := element.GetItem(i)
gg := fmt.Sprintf("--[%d]--%s\n%s\n-------\n", i, e.GetUpper(), e.GetLower())
fmt.Println(gg)
}
fmt.Sprintln(element.GetInstruction())
fmt.Printf("Enter the index? for not selecting press -1")
var response string
_, err := fmt.Scanln(&response)
if err != nil {
return "", err
}
if len(response) == 0 {
return "", nil
}
if response == "-1" {
return "", nil
}
v, err := strconv.Atoi(response)
if err != nil {
return "", err
}
return element.GetResult(v), nil
}
type debugSpinner struct {
chars []string
done chan bool
active bool
startTime time.Time
}
func NewDebugMenuDriven() *debugMenuDriven {
return &debugMenuDriven{}
}
func (p *debugMenuDriven) GetProgressAnimation() ProgressAnimation {
if p.progress != nil {
return p.progress
}
p.progress = &debugSpinner{
chars: []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"},
done: make(chan bool),
active: false,
}
return p.progress
}
func (s *debugSpinner) Start(msg ...any) {
if s.active {
return
}
s.done = make(chan bool)
fmt.Println(msg...)
s.active = true
s.startTime = time.Now()
go func() {
for i := 0; ; i = (i + 1) % len(s.chars) {
select {
case <-s.done:
fmt.Println() // Clear the spinner
return
default:
elapsed := time.Since(s.startTime).Round(time.Second)
fmt.Printf("%s %s", s.chars[i], elapsed)
time.Sleep(100 * time.Millisecond)
}
}
}()
}
func (s *debugSpinner) StopWithSuccess(msg ...any) {
if !s.active {
return
}
fmt.Println(msg...)
s.done <- true
s.active = false
}
func (s *debugSpinner) StopWithFailure(msg ...any) {
if !s.active {
return
}
fmt.Println(msg...)
s.done <- true
s.active = false
}
func (s *debugSpinner) Stop() {
if !s.active {
return
}
s.done <- true
s.active = false
}
func (p *debugMenuDriven) Confirmation(prompt string, opts ...func(*option) error) (proceed bool, err error) {
o, err := processOptions(opts)
if err != nil {
return false, err
}
fmt.Println(prompt)
fmt.Printf("Proceed? [y/N]{default: %s}: ", color.HiGreenString(o.defaultValue))
var response string
_, err = fmt.Scanln(&response)
if err != nil {
return false, err
}
if len(response) == 0 {
if o.defaultValue != "" {
response = o.defaultValue
} else {
return false, nil
}
}
if response != "y" && response != "Y" && response != "yes" {
return false, nil
}
fmt.Println()
return true, nil
}
func (p *debugMenuDriven) TextInput(prompt string, opts ...func(*option) error) (string, error) {
o, err := processOptions(opts)
if err != nil {
return "", err
}
reader := bufio.NewReader(os.Stdin)
fmt.Printf("%s {default: %s}: ", prompt, color.HiGreenString(o.defaultValue))
response, err := reader.ReadString('\n')
if err != nil && io.EOF != err {
return "", err
}
response = strings.TrimSpace(response)
if len(response) == 0 {
if o.defaultValue != "" {
response = o.defaultValue
}
}
fmt.Println("Got response:", response)
fmt.Println()
return response, nil
}
func (p *debugMenuDriven) TextInputPassword(prompt string) (string, error) {
fmt.Println(prompt)
var response string
_, err := fmt.Scanln(&response)
if err != nil {
return "", err
}
return response, nil
}
func (p *debugMenuDriven) DropDown(prompt string, options map[string]string, opts ...func(*option) error) (string, error) {
o, err := processOptions(opts)
if err != nil {
return "", err
}
fmt.Printf("%s {default: %s}\n", prompt, color.HiGreenString(o.defaultValue))
fmt.Println("Options[make sure to enter value and not the key]:")
for k, v := range options {
fmt.Printf("%s: %s\n", k, color.HiCyanString(v))
}
var response string
_, err = fmt.Scanln(&response)
if err != nil {
return "", err
}
if len(response) == 0 {
if o.defaultValue != "" {
response = o.defaultValue
}
}
fmt.Println("Got response:", response)
fmt.Println()
return response, nil
}
func (p *debugMenuDriven) DropDownList(prompt string, options []string, opts ...func(*option) error) (string, error) {
o, err := processOptions(opts)
if err != nil {
return "", err
}
fmt.Printf("%s {default: %s}\n", prompt, color.HiGreenString(o.defaultValue))
fmt.Println("Options:")
for _, v := range options {
fmt.Println(color.HiCyanString(v))
}
var response string
_, err = fmt.Scanln(&response)
if err != nil {
return "", err
}
if len(response) == 0 {
if o.defaultValue != "" {
response = o.defaultValue
}
}
fmt.Println("Got response:", response)
fmt.Println()
return response, nil
}
func (p *debugMenuDriven) MultiSelect(prompt string, options map[string]string, opts ...func(*option) error) ([]string, error) {
fmt.Println("Options[make sure to enter value and not the key]:")
for k, v := range options {
fmt.Printf("%s: %s\n", k, color.HiCyanString(v))
}
var response string
_, err := fmt.Scanln(&response)
if err != nil {
return nil, err
}
if len(response) == 0 {
return nil, fmt.Errorf("No response provided")
}
fmt.Println("Got response:", response)
fmt.Println()
return strings.Split(response, ","), nil
}
07070100000029000081A40000000000000000000000016818E78D000004AD000000000000000000000000000000000000001D00000000ksctl-2.8.0/pkg/cli/flags.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import "github.com/spf13/cobra"
func MarkFlagsRequired(command *cobra.Command, flagNames ...string) error {
for _, flagName := range flagNames {
if err := command.MarkFlagRequired(flagName); err != nil {
return err
}
}
return nil
}
func AddVerboseFlag(command *cobra.Command, verbose *bool) {
command.PersistentFlags().BoolVarP(verbose, "verbose", "v", false, "Enable verbose output")
}
func AddDebugMode(command *cobra.Command, debugRun *bool) {
command.PersistentFlags().BoolVar(debugRun, "debug-cli", false, "Its used to run debug mode against cli's menudriven interface")
}
0707010000002A000081A40000000000000000000000016818E78D00000309000000000000000000000000000000000000001F00000000ksctl-2.8.0/pkg/cli/mapping.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import "github.com/spf13/cobra"
func RegisterCommand(parent *cobra.Command, children ...*cobra.Command) {
for _, child := range children {
parent.AddCommand(child)
}
}
0707010000002B000081A40000000000000000000000016818E78D000007B4000000000000000000000000000000000000002200000000ksctl-2.8.0/pkg/cli/menudriven.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
type ProgressAnimation interface {
Start(msg ...any)
StopWithSuccess(msg ...any)
StopWithFailure(msg ...any)
Stop()
}
type option struct {
defaultValue string
}
func WithDefaultValue(defaultValue string) func(*option) error {
return func(o *option) error {
o.defaultValue = defaultValue
return nil
}
}
type CardItem interface {
GetUpper() string
GetLower() string
}
type CardPack interface {
LenOfItems() int
GetItem(i int) CardItem
GetInstruction() string
GetResult(index int) string
GetCardConfiguration() (cardWidth, noOfVisibleItems int)
}
type MenuDriven interface {
GetProgressAnimation() ProgressAnimation
Confirmation(prompt string, opts ...func(*option) error) (proceed bool, err error)
TextInput(prompt string, opts ...func(*option) error) (string, error)
TextInputPassword(prompt string) (string, error)
DropDown(prompt string, options map[string]string, opts ...func(*option) error) (string, error)
DropDownList(prompt string, options []string, opts ...func(*option) error) (string, error)
MultiSelect(prompt string, options map[string]string, opts ...func(*option) error) ([]string, error)
CardSelection(element CardPack) (string, error)
}
func processOptions(opts []func(*option) error) (option, error) {
var o option
for _, opt := range opts {
if err := opt(&o); err != nil {
return o, err
}
}
return o, nil
}
0707010000002C000081A40000000000000000000000016818E78D00001012000000000000000000000000000000000000001D00000000ksctl-2.8.0/pkg/cli/pterm.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"fmt"
"os"
"strings"
"github.com/pterm/pterm"
)
type genericMenuDriven struct {
progress ProgressAnimation
}
type spinner struct {
c pterm.SpinnerPrinter
s *pterm.SpinnerPrinter
active bool
}
func NewMenuDriven() *genericMenuDriven {
return &genericMenuDriven{}
}
func (p *genericMenuDriven) GetProgressAnimation() ProgressAnimation {
if p.progress != nil {
return p.progress
}
spin := pterm.DefaultSpinner
spin.Sequence = []string{"⡏", "⡟", "⡿", "⢿", "⣻", "⣽", "⣾", "⣷", "⣯", "⣏"}
p.progress = &spinner{
c: spin,
}
return p.progress
}
func (s *spinner) Start(msg ...any) {
if s.active {
return
}
s.s, _ = s.c.Start(msg...)
}
func (s *spinner) StopWithSuccess(msg ...any) {
s.s.Success(msg...)
s.s = nil
s.active = false
}
func (s *spinner) Stop() {
_, _ = fmt.Fprint(os.Stderr, "\r"+strings.Repeat(" ", pterm.GetTerminalWidth())) // Clear the spinner
_ = s.s.Stop()
s.s = nil
s.active = false
}
func (s *spinner) StopWithFailure(msg ...any) {
s.s.Fail(msg...)
s.s = nil
s.active = false
}
func (p *genericMenuDriven) Confirmation(prompt string, opts ...func(*option) error) (proceed bool, err error) {
o, err := processOptions(opts)
if err != nil {
return false, err
}
x := pterm.DefaultInteractiveConfirm
if len(o.defaultValue) != 0 {
x = *x.WithDefaultValue(o.defaultValue == "yes")
}
return x.Show(prompt)
}
func (p *genericMenuDriven) TextInput(prompt string, opts ...func(*option) error) (string, error) {
o, err := processOptions(opts)
if err != nil {
return "", err
}
if len(o.defaultValue) == 0 {
return pterm.DefaultInteractiveTextInput.Show(prompt)
}
x := pterm.DefaultInteractiveTextInput.WithDefaultValue(o.defaultValue)
return x.Show(prompt)
}
func (p *genericMenuDriven) TextInputPassword(prompt string) (string, error) {
x := pterm.DefaultInteractiveTextInput.WithMask("*")
return x.Show(prompt)
}
func (p *genericMenuDriven) DropDown(prompt string, options map[string]string, opts ...func(*option) error) (string, error) {
o, err := processOptions(opts)
if err != nil {
return "", err
}
var _options []string
for k := range options {
_options = append(_options, k)
}
x := pterm.DefaultInteractiveSelect.WithOptions(_options)
if len(o.defaultValue) != 0 {
for k, v := range options {
if v == o.defaultValue {
o.defaultValue = k
break
}
}
x = x.WithDefaultOption(o.defaultValue)
}
if v, err := x.Show(prompt); err != nil {
return "", err
} else {
return options[v], nil
}
}
func (p *genericMenuDriven) DropDownList(prompt string, options []string, opts ...func(*option) error) (string, error) {
o, err := processOptions(opts)
if err != nil {
return "", err
}
x := pterm.DefaultInteractiveSelect.WithOptions(options)
if len(o.defaultValue) != 0 {
x = x.WithDefaultOption(o.defaultValue)
}
if v, err := x.Show(prompt); err != nil {
return "", err
} else {
return v, nil
}
}
func (p *genericMenuDriven) MultiSelect(prompt string, options map[string]string, opts ...func(*option) error) ([]string, error) {
var _options []string
for k, _ := range options {
_options = append(_options, k)
}
x := pterm.DefaultInteractiveMultiselect.WithOptions(_options)
if v, err := x.Show(prompt); err != nil {
return nil, err
} else {
if len(v) == 0 {
return nil, fmt.Errorf("no options selected")
}
_v := make([]string, 0, len(v))
for _, k := range v {
_v = append(_v, options[k])
}
return _v, nil
}
}
0707010000002D000081A40000000000000000000000016818E78D000031C5000000000000000000000000000000000000002200000000ksctl-2.8.0/pkg/cli/summary_ui.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cli
import (
"fmt"
"io"
"strings"
"github.com/charmbracelet/lipgloss"
"github.com/fatih/color"
"github.com/ksctl/ksctl/v2/pkg/handler/cluster/common"
)
// SummaryUI is responsible for rendering cluster summary with enhanced UI
type SummaryUI struct {
writer io.Writer
}
// NewSummaryUI creates a new instance of SummaryUI
func NewSummaryUI(w io.Writer) *SummaryUI {
return &SummaryUI{
writer: w,
}
}
// RenderClusterSummary renders the cluster summary with enhanced UI
func (ui *SummaryUI) RenderClusterSummary(summary *common.SummaryOutput) {
parentBox := lipgloss.NewStyle().
BorderStyle(lipgloss.RoundedBorder()).
BorderForeground(lipgloss.Color("8")).
Padding(1, 2).
Width(90).
Align(lipgloss.Center)
banner := lipgloss.NewStyle().
Padding(0, 1).
Width(80).
Align(lipgloss.Center)
sectionTitle := lipgloss.NewStyle().
Foreground(lipgloss.Color("13")).
Bold(true).
MarginTop(1).
Padding(0, 1)
infoBlock := lipgloss.NewStyle().
BorderStyle(lipgloss.RoundedBorder()).
BorderForeground(lipgloss.Color("10")).
Padding(1, 2).
MarginTop(1).
Width(80)
keyValueRow := func(key, value string) string {
return lipgloss.JoinHorizontal(lipgloss.Top,
lipgloss.NewStyle().Foreground(lipgloss.Color("14")).PaddingRight(3).Width(28).Align(lipgloss.Left).Render(key),
lipgloss.NewStyle().Width(50).Render(value),
)
}
var parentBoxContent strings.Builder
bannerContent := fmt.Sprintf("✨ %s ✨\n\n%s",
lipgloss.NewStyle().Bold(true).Align(lipgloss.Center).Foreground(lipgloss.Color("#FFFFFF")).Render("Cluster Summary"),
lipgloss.NewStyle().Italic(true).Align(lipgloss.Center).Foreground(lipgloss.Color("#DDDDDD")).Render("Health and status of your Kubernetes cluster"))
parentBoxContent.WriteString(banner.Render(bannerContent))
parentBoxContent.WriteString("\n\n")
// Cluster basics section
{
var content strings.Builder
if summary.ClusterName != "" {
content.WriteString(keyValueRow("Name", summary.ClusterName))
content.WriteString("\n")
}
if summary.CloudProvider != "" {
content.WriteString(keyValueRow("Cloud", summary.CloudProvider))
content.WriteString("\n")
}
if summary.ClusterType != "" {
content.WriteString(keyValueRow("Type", summary.ClusterType))
content.WriteString("\n")
}
if summary.RoundTripLatency != "" {
content.WriteString(keyValueRow("Round Trip Latency", summary.RoundTripLatency))
content.WriteString("\n")
}
if summary.KubernetesVersion != "" {
content.WriteString(keyValueRow("Kubernetes Version", summary.KubernetesVersion))
content.WriteString("\n")
}
if summary.APIServerHealthCheck != nil {
if !summary.APIServerHealthCheck.Healthy {
content.WriteString(keyValueRow("API Server Health", color.HiRedString("Unhealthy")))
} else {
content.WriteString(keyValueRow("API Server Health", color.HiGreenString("Healthy")))
}
content.WriteString("\n")
if len(summary.APIServerHealthCheck.FailedComponents) > 0 {
v := color.HiRedString(strings.Join(summary.APIServerHealthCheck.FailedComponents, ", "))
content.WriteString(keyValueRow("Components Unhealthy", v))
content.WriteString("\n")
}
}
for k, v := range summary.ControlPlaneComponentVers {
content.WriteString(keyValueRow(k, v))
content.WriteString("\n")
}
contentStr := strings.TrimSuffix(content.String(), "\n")
contentBlock := infoBlock.Render(contentStr)
titleBlock := sectionTitle.Render("🔑 Key Attributes")
fullSection := lipgloss.JoinVertical(lipgloss.Left, titleBlock, contentBlock)
parentBoxContent.WriteString(fullSection)
parentBoxContent.WriteString("\n")
}
yesNoColor := func(v bool, inverse bool) (string, func(format string, a ...interface{}) string) {
if v {
if inverse {
return "Yes", color.HiRedString
}
return "Yes", color.HiGreenString
}
if inverse {
return "No", color.HiGreenString
}
return "No", color.HiRedString
}
applyYesNoColor := func(msg string, formatter func(format string, a ...interface{}) string) string {
return formatter(msg)
}
{
// Nodes section
var content strings.Builder
for _, node := range summary.Nodes {
content.WriteString("\n")
content.WriteString(color.HiMagentaString(node.Name))
content.WriteString("\n")
content.WriteString(keyValueRow(" Ready", applyYesNoColor(yesNoColor(node.Ready, false))))
content.WriteString("\n")
content.WriteString(keyValueRow(" Kubelet Healthy", applyYesNoColor(yesNoColor(node.KubeletHealthy, false))))
content.WriteString("\n")
content.WriteString(keyValueRow(" Memory Pressure", applyYesNoColor(yesNoColor(node.MemoryPressure, true))))
content.WriteString("\n")
content.WriteString(keyValueRow(" Disk Pressure", applyYesNoColor(yesNoColor(node.DiskPressure, true))))
content.WriteString("\n")
content.WriteString(keyValueRow(" Unreachable", applyYesNoColor(yesNoColor(node.NetworkUnavailable, true))))
content.WriteString("\n")
content.WriteString(keyValueRow(" Kubelet Version", node.KubeletVersion))
content.WriteString("\n")
content.WriteString(keyValueRow(" CRI", node.ContainerRuntimeVersion))
content.WriteString("\n")
cpuUtlization := "😢 Currently Unavailable"
if node.CPUUtilization > 0 {
cpuUtlization = fmt.Sprintf("%.2f%% (Total: %s)", node.CPUUtilization, node.CPUUnits)
}
memoryUtlization := "😢 Currently Unavailable"
if node.MemoryUtilization > 0 {
memoryUtlization = fmt.Sprintf("%.2f%% (Total: %s)", node.MemoryUtilization, node.MemUnits)
}
content.WriteString(keyValueRow(" CPU Usage", cpuUtlization))
content.WriteString("\n")
content.WriteString(keyValueRow(" Memory Usage", memoryUtlization))
content.WriteString("\n")
}
contentStr := strings.TrimSuffix(content.String(), "\n")
contentBlock := infoBlock.Render(contentStr)
titleBlock := sectionTitle.Render("🤖 Nodes")
fullSection := lipgloss.JoinVertical(lipgloss.Left, titleBlock, contentBlock)
parentBoxContent.WriteString(fullSection)
parentBoxContent.WriteString("\n")
}
{
// Workload section
var content strings.Builder
workloads := summary.WorkloadSummary
content.WriteString(keyValueRow("Deployments", fmt.Sprintf("%d", workloads.Deployments)))
content.WriteString("\n")
content.WriteString(keyValueRow("StatefulSets", fmt.Sprintf("%d", workloads.StatefulSets)))
content.WriteString("\n")
content.WriteString(keyValueRow("DaemonSets", fmt.Sprintf("%d", workloads.DaemonSets)))
content.WriteString("\n")
content.WriteString(keyValueRow("CronJobs", fmt.Sprintf("%d", workloads.CronJobs)))
content.WriteString("\n")
content.WriteString(keyValueRow("Namespaces", fmt.Sprintf("%d", workloads.Namespaces)))
content.WriteString("\n")
content.WriteString(keyValueRow("Persistent Volumes", fmt.Sprintf("%d", workloads.PV)))
content.WriteString("\n")
content.WriteString(keyValueRow("Persistent Volume Claims", fmt.Sprintf("%d", workloads.PVC)))
content.WriteString("\n")
content.WriteString(keyValueRow("Storage Class", fmt.Sprintf("%d", workloads.SC)))
content.WriteString("\n")
content.WriteString(keyValueRow("Service (ClusterIP)", fmt.Sprintf("%d", workloads.ClusterIPSVC)))
content.WriteString("\n")
content.WriteString(keyValueRow("Service (LoadBalancer)", fmt.Sprintf("%d", workloads.LoadbalancerSVC)))
content.WriteString("\n")
content.WriteString(keyValueRow("Pods (Running)", fmt.Sprintf("%d", workloads.RunningPods)))
content.WriteString("\n")
if len(workloads.UnHealthyPods) > 0 {
content.WriteString(color.HiMagentaString("Unhealthy Pods"))
content.WriteString("\n")
}
for _, pod := range workloads.UnHealthyPods {
content.WriteString("\n")
content.WriteString(" " + color.HiRedString(pod.Name+"@"+pod.Namespace))
content.WriteString("\n")
objectRef := []string{}
for _, ref := range pod.OwnerRef {
v := fmt.Sprintf("%s/%s@%s", ref.Kind, ref.Name, pod.Namespace)
objectRef = append(objectRef, v)
}
if len(objectRef) == 0 {
objectRef = append(objectRef, "No Owner Reference")
}
content.WriteString(keyValueRow(" "+"Owner Ref", strings.Join(objectRef, ", ")))
content.WriteString("\n")
content.WriteString(keyValueRow(" "+"Failed", applyYesNoColor(yesNoColor(pod.IsFailed, true))))
content.WriteString("\n")
content.WriteString(keyValueRow(" "+"Pending", applyYesNoColor(yesNoColor(pod.IsPending, true))))
content.WriteString("\n")
for _, container := range pod.FailedContainers {
content.WriteString("\n")
content.WriteString(keyValueRow(" "+"Container", container.Name))
content.WriteString("\n")
content.WriteString(keyValueRow(" "+"Restart Count", fmt.Sprintf("%d", container.RestartCount)))
content.WriteString("\n")
content.WriteString(keyValueRow(" "+"Reason", container.WaitingProblem.Reason))
content.WriteString("\n")
content.WriteString(keyValueRow(" "+"Message", container.WaitingProblem.Message))
content.WriteString("\n")
}
content.WriteString("\n")
}
contentStr := strings.TrimSuffix(content.String(), "\n")
contentBlock := infoBlock.Render(contentStr)
titleBlock := sectionTitle.Render("📦 Workloads")
fullSection := lipgloss.JoinVertical(lipgloss.Left, titleBlock, contentBlock)
parentBoxContent.WriteString(fullSection)
parentBoxContent.WriteString("\n")
}
{
// Recent events section
var content strings.Builder
if len(summary.RecentWarningEvents) == 0 {
content.WriteString(color.HiGreenString("No recent events"))
content.WriteString("\n")
} else {
content.WriteString(color.HiMagentaString("Recent Warning Events (Past 24h)"))
content.WriteString("\n")
for _, event := range summary.RecentWarningEvents {
content.WriteString("\n")
content.WriteString(color.HiMagentaString(fmt.Sprintf("%s/%s@%s", event.Kind, event.Name, event.Namespace)))
content.WriteString("\n")
content.WriteString(keyValueRow(" Reason", event.Reason))
content.WriteString("\n")
content.WriteString(keyValueRow(" Message", event.Message))
content.WriteString("\n")
content.WriteString(keyValueRow(" Happened On", event.Time.String()))
content.WriteString("\n")
content.WriteString(keyValueRow(" Reported By", event.ReportedBy))
content.WriteString("\n")
}
}
contentStr := strings.TrimSuffix(content.String(), "\n")
contentBlock := infoBlock.Render(contentStr)
titleBlock := sectionTitle.Render("📢 Events")
fullSection := lipgloss.JoinVertical(lipgloss.Left, titleBlock, contentBlock)
parentBoxContent.WriteString(fullSection)
parentBoxContent.WriteString("\n")
}
{
// Issues section
var content strings.Builder
if len(summary.DetectedIssues) == 0 {
content.WriteString(color.HiGreenString("No issues detected"))
content.WriteString("\n")
} else {
content.WriteString(color.HiMagentaString("Detected Issues"))
content.WriteString("\n")
severityColor := func(severity string) string {
switch severity {
case "Critical":
return color.New(color.BgRed, color.FgBlack).Add(color.Bold).Sprintf("%s", severity)
case "Error":
return color.HiRedString(severity)
case "Warning":
return color.HiYellowString(severity)
default:
return severity
}
}
for _, issue := range summary.DetectedIssues {
content.WriteString(color.HiMagentaString(issue.Component))
content.WriteString("\n")
content.WriteString(keyValueRow(" Severity", severityColor(issue.Severity)))
content.WriteString("\n")
content.WriteString(keyValueRow(" Message", issue.Message))
content.WriteString("\n")
content.WriteString(keyValueRow(" Recommendation", issue.Recommendation))
content.WriteString("\n")
}
}
contentStr := strings.TrimSuffix(content.String(), "\n")
contentBlock := infoBlock.Render(contentStr)
titleBlock := sectionTitle.Render("🚩 Problems")
fullSection := lipgloss.JoinVertical(lipgloss.Left, titleBlock, contentBlock)
parentBoxContent.WriteString(fullSection)
parentBoxContent.WriteString("\n")
}
fmt.Fprintln(ui.writer, parentBox.Render(parentBoxContent.String()))
}
0707010000002E000041ED0000000000000000000000026818E78D00000000000000000000000000000000000000000000001700000000ksctl-2.8.0/pkg/config0707010000002F000081A40000000000000000000000016818E78D00000883000000000000000000000000000000000000002000000000ksctl-2.8.0/pkg/config/cache.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
)
type UpdateCache struct {
LastChecked time.Time `json:"lastChecked"`
AvailableVersions bool `json:"availableVersions"`
UpdateCheckInterval time.Duration `json:"updateCheckInterval"`
}
var DefaultUpdateCache = &UpdateCache{
UpdateCheckInterval: time.Hour * 24,
}
func locateUpdateCacheFile() (string, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return "", err
}
configDir := filepath.Join(homeDir, ".config", "ksctl")
configFile := filepath.Join(configDir, "cache-update.json")
if _, err := os.Stat(configDir); os.IsNotExist(err) {
if err := os.MkdirAll(configDir, 0755); err != nil {
return configFile, fmt.Errorf("failed to create directory %s: %v", configDir, err)
}
}
return configFile, nil
}
func LoadUpdateCache(c *UpdateCache) (errC error) {
configFile, err := locateUpdateCacheFile()
if err != nil {
return err
}
file, err := os.Open(configFile)
if err != nil {
if os.IsNotExist(err) {
// NOTE: writing default config
c = DefaultUpdateCache
return SaveUpdateCache(c)
}
return fmt.Errorf("failed to open file %s: %v", configFile, err)
}
defer file.Close()
return json.NewDecoder(file).Decode(c)
}
func SaveUpdateCache(c *UpdateCache) error {
configFile, err := locateUpdateCacheFile()
if err != nil {
return err
}
file, err := os.Create(configFile)
if err != nil {
return fmt.Errorf("failed to create file %s: %v", configFile, err)
}
defer file.Close()
return json.NewEncoder(file).Encode(c)
}
07070100000030000081A40000000000000000000000016818E78D00000B53000000000000000000000000000000000000002100000000ksctl-2.8.0/pkg/config/config.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"github.com/ksctl/ksctl/v2/pkg/consts"
"github.com/ksctl/ksctl/v2/pkg/utilities"
)
// ksctl config in ~/.config/ksctl/config.json (handled by ksctl:cli)
// ksctl credentials in ~/.config/ksctl/creds/(aws|azure|mongodb).json (handled by ksctl:cli)
// ksctl state in ~/.ksctl/state/..... (handled by the ksctl:core:storage)
// NOTE
// store all the credentials in the local file system and for state we decide on the external storage
// we will retrieve the credentials and load them in the context.Background() and pass it to the respective clients
// So we need to initialize the HostStorage() to get the credentials from the local file system once thats done
// we can populate the context.Background() with the credentials. and initialze the client which that preferedstateStore unless specified in the command argument
type Config struct {
PreferedStateStore consts.KsctlStore `json:"preferedStateStore"`
Telemetry *bool `json:"telemetry,omitempty"`
}
func LoadConfig(c *Config) (errC error) {
configFile, err := locateConfig()
if err != nil {
return err
}
file, err := os.Open(configFile)
if err != nil {
if os.IsNotExist(err) {
// NOTE: writing default config
*c = Config{
PreferedStateStore: consts.StoreLocal,
Telemetry: utilities.Ptr(true),
}
return SaveConfig(c)
}
return fmt.Errorf("failed to open file %s: %v", configFile, err)
}
defer file.Close()
return json.NewDecoder(file).Decode(c)
}
func locateConfig() (fileLoc string, err error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return "", err
}
configDir := filepath.Join(homeDir, ".config", "ksctl")
configFile := filepath.Join(configDir, "config.json")
if _, err := os.Stat(configDir); os.IsNotExist(err) {
if err := os.MkdirAll(configDir, 0755); err != nil {
return configFile, fmt.Errorf("failed to create directory %s: %v", configDir, err)
}
}
return configFile, nil
}
func SaveConfig(c *Config) error {
configFile, err := locateConfig()
if err != nil {
return err
}
file, err := os.Create(configFile)
if err != nil {
return fmt.Errorf("failed to create file %s: %v", configFile, err)
}
defer file.Close()
return json.NewEncoder(file).Encode(c)
}
07070100000031000081A40000000000000000000000016818E78D00000C52000000000000000000000000000000000000002000000000ksctl-2.8.0/pkg/config/creds.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"github.com/ksctl/ksctl/v2/pkg/consts"
ksctlErrors "github.com/ksctl/ksctl/v2/pkg/errors"
"github.com/ksctl/ksctl/v2/pkg/statefile"
)
func locateCreds(s, prefix string) (fileLoc string, err error) {
if len(s) == 0 {
return "", fmt.Errorf("sku is empty")
}
homeDir, err := os.UserHomeDir()
if err != nil {
return "", err
}
configDir := filepath.Join(homeDir, ".config", "ksctl", "creds")
configFile := filepath.Join(configDir, prefix+s+".json")
if _, err := os.Stat(configDir); os.IsNotExist(err) {
if err := os.MkdirAll(configDir, 0755); err != nil {
return configFile, fmt.Errorf("failed to create directory %s: %v", configDir, err)
}
}
return configFile, nil
}
func SaveStorageCreds[T statefile.CredentialsMongodb](c *T, s consts.KsctlStore) error {
credsFile, err := locateCreds(string(s), "s-")
if err != nil {
return err
}
file, err := os.Create(credsFile)
if err != nil {
return fmt.Errorf("failed to create file %s: %v", credsFile, err)
}
defer file.Close()
return json.NewEncoder(file).Encode(c)
}
func LoadStorageCreds[T statefile.CredentialsMongodb](c *T, s consts.KsctlStore) (errC error) {
credsFile, err := locateCreds(string(s), "s-")
if err != nil {
return err
}
file, err := os.Open(credsFile)
if err != nil {
if os.IsNotExist(err) {
return ksctlErrors.WrapErrorf(
ksctlErrors.ErrNilCredentials,
"credentials for storage driver %s not found",
s,
)
}
return fmt.Errorf("failed to open file %s: %v", credsFile, err)
}
defer file.Close()
return json.NewDecoder(file).Decode(c)
}
func SaveCloudCreds[T statefile.CredentialsAws | statefile.CredentialsAzure](c *T, s consts.KsctlCloud) error {
credsFile, err := locateCreds(string(s), "c-")
if err != nil {
return err
}
file, err := os.Create(credsFile)
if err != nil {
return fmt.Errorf("failed to create file %s: %v", credsFile, err)
}
defer file.Close()
return json.NewEncoder(file).Encode(c)
}
func LoadCloudCreds[T statefile.CredentialsAws | statefile.CredentialsAzure](c *T, s consts.KsctlCloud) (errC error) {
credsFile, err := locateCreds(string(s), "c-")
if err != nil {
return err
}
file, err := os.Open(credsFile)
if err != nil {
if os.IsNotExist(err) {
return ksctlErrors.WrapErrorf(
ksctlErrors.ErrNilCredentials,
"credentials for cloud %s not found",
s,
)
}
return fmt.Errorf("failed to open file %s: %v", credsFile, err)
}
defer file.Close()
return json.NewDecoder(file).Decode(c)
}
07070100000032000081A40000000000000000000000016818E78D00000307000000000000000000000000000000000000001F00000000ksctl-2.8.0/pkg/config/vars.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
// change this using ldflags
var (
Version string = "dev"
KsctlCoreVer string = "dev"
BuildDate string
)
func InDevMode() bool {
return Version == "dev"
}
07070100000033000041ED0000000000000000000000026818E78D00000000000000000000000000000000000000000000001700000000ksctl-2.8.0/pkg/logger07070100000034000081A40000000000000000000000016818E78D00002A45000000000000000000000000000000000000002A00000000ksctl-2.8.0/pkg/logger/general_logging.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package logger
import (
"context"
"fmt"
"io"
"os"
"reflect"
"strings"
"sync"
"github.com/charmbracelet/lipgloss"
"github.com/fatih/color"
"github.com/ksctl/ksctl/v2/pkg/logger"
"github.com/rodaine/table"
"golang.org/x/term"
"time"
)
type GeneralLog struct {
mu *sync.Mutex
writter io.Writer
level uint
started time.Time
}
var (
warnLvl = logger.CustomExternalLogLevel(color.New(color.FgBlack, color.BgYellow).Sprintf("[W]"))
infoLvl = logger.CustomExternalLogLevel(color.New(color.FgBlack, color.BgBlue).Sprintf("[I]"))
noteLvl = logger.CustomExternalLogLevel(color.New(color.FgBlack, color.BgCyan).Sprintf("[N]"))
dbgLvl = logger.CustomExternalLogLevel(color.New(color.FgBlack, color.BgMagenta).Sprintf("[D]"))
passLvl = logger.CustomExternalLogLevel(color.New(color.FgBlack, color.BgGreen).Sprintf("[S]"))
errLvl = logger.CustomExternalLogLevel(color.New(color.FgBlack, color.BgRed).Sprintf("[E]"))
)
func NewLogger(verbose int, out io.Writer) *GeneralLog {
var ve uint
if verbose < 0 {
ve = 9
}
return &GeneralLog{
writter: out,
level: ve,
mu: new(sync.Mutex),
started: time.Now().UTC(),
}
}
func (l *GeneralLog) getTime() string {
return color.HiBlackString(fmt.Sprintf("(%s)", time.Since(l.started).Round(time.Second).String()))
}
func formGroups(v ...any) (format string, vals []any) {
if len(v) == 0 {
return "\n", nil
}
_format := strings.Builder{}
defer func() {
format = _format.String()
}()
i := 0
for ; i+1 < len(v); i += 2 {
if !reflect.TypeOf(v[i+1]).Implements(reflect.TypeOf((*error)(nil)).Elem()) &&
(reflect.TypeOf(v[i+1]).Kind() == reflect.Interface ||
reflect.TypeOf(v[i+1]).Kind() == reflect.Ptr ||
reflect.TypeOf(v[i+1]).Kind() == reflect.Struct) {
_format.WriteString(fmt.Sprintf("%s", v[i]) + "=%#v ")
} else {
_format.WriteString(fmt.Sprintf("%s", v[i]) + "=%v ")
}
vals = append(vals, v[i+1])
}
for ; i < len(v); i++ {
_format.WriteString("!!EXTRA:%v ")
vals = append(vals, v[i])
}
_format.WriteString("\n")
return
}
func isLogEnabled(level uint, msgType logger.CustomExternalLogLevel) bool {
if msgType == dbgLvl {
return level >= 9
}
return true
}
func (l *GeneralLog) logErrorf(disablePrefix bool, msg string, args ...any) error {
l.mu.Lock()
defer l.mu.Unlock()
if !disablePrefix {
prefix := fmt.Sprintf("%s%s ", l.getTime(), errLvl)
msg = prefix + msg
}
format, _args := formGroups(args...)
var errMsg error
if _args == nil {
errMsg = fmt.Errorf("%s %s", msg, format)
} else {
errMsg = fmt.Errorf(msg+" "+format, _args...)
}
return errMsg
}
func (l *GeneralLog) log(useGroupFormer bool, msgType logger.CustomExternalLogLevel, msg string, args ...any) {
if !isLogEnabled(l.level, msgType) {
return
}
l.mu.Lock()
defer l.mu.Unlock()
prefix := fmt.Sprintf("%s ", msgType)
elapsedTime := color.HiBlackString(fmt.Sprintf("(%s)", time.Since(l.started).Round(time.Second).String()))
// Get terminal width for right-aligned time
width, _, err := term.GetSize(int(os.Stdout.Fd()))
if err != nil || width <= 0 {
// Fallback to a reasonable default width if we can't determine terminal size
width = 120
}
if useGroupFormer {
msgColored := ""
switch msgType {
case passLvl:
msgColored = color.HiGreenString(msg)
case warnLvl:
msgColored = color.HiYellowString(msg)
case dbgLvl:
msgColored = color.HiMagentaString(msg)
case noteLvl:
msgColored = color.HiCyanString(msg)
case infoLvl:
msgColored = color.HiBlueString(msg)
case errLvl:
msgColored = color.HiRedString(msg)
}
// Create the base message with prefix and colored text
baseMsg := prefix + msgColored
format, _args := formGroups(args...)
if _args == nil {
if msgType == errLvl {
l.boxBox(
"🛑 We Have Problem", msgColored+" "+format, "Red")
return
}
// Format the message with right-aligned elapsed time
formattedMessage := formatWithRightAlignedTime(baseMsg+" "+format, elapsedTime, width)
fmt.Fprint(l.writter, formattedMessage)
} else {
if msgType == errLvl {
l.boxBox(
"🛑 We Have Problem", fmt.Sprintf(msgColored+" "+format, _args...), "Red")
return
}
// Format the message with args and right-aligned elapsed time
fullMsg := fmt.Sprintf(baseMsg+" "+format, _args...)
formattedMessage := formatWithRightAlignedTime(fullMsg, elapsedTime, width)
fmt.Fprint(l.writter, formattedMessage)
}
} else {
// Format non-group messages with right-aligned time
fullMsg := fmt.Sprintf(prefix+msg+"\n", args...)
formattedMessage := formatWithRightAlignedTime(fullMsg, elapsedTime, width)
fmt.Fprint(l.writter, formattedMessage)
}
}
func (l *GeneralLog) ExternalLogHandler(ctx context.Context, msgType logger.CustomExternalLogLevel, message string) {
if msgType == logger.LogDebug {
msgType = dbgLvl
} else if msgType == logger.LogError {
msgType = errLvl
} else if msgType == logger.LogInfo {
msgType = infoLvl
} else if msgType == logger.LogWarning {
msgType = warnLvl
} else if msgType == logger.LogSuccess {
msgType = passLvl
} else if msgType == logger.LogNote {
msgType = noteLvl
}
l.log(false, msgType, message)
}
func (l *GeneralLog) ExternalLogHandlerf(ctx context.Context, msgType logger.CustomExternalLogLevel, format string, args ...interface{}) {
if msgType == logger.LogDebug {
msgType = dbgLvl
} else if msgType == logger.LogError {
msgType = errLvl
} else if msgType == logger.LogInfo {
msgType = infoLvl
} else if msgType == logger.LogWarning {
msgType = warnLvl
} else if msgType == logger.LogSuccess {
msgType = passLvl
} else if msgType == logger.LogNote {
msgType = noteLvl
}
l.log(false, msgType, format, args...)
}
func (l *GeneralLog) Print(ctx context.Context, msg string, args ...any) {
l.log(true, infoLvl, msg, args...)
}
func (l *GeneralLog) Success(ctx context.Context, msg string, args ...any) {
l.log(true, passLvl, msg, args...)
}
func (l *GeneralLog) Note(ctx context.Context, msg string, args ...any) {
l.log(true, noteLvl, msg, args...)
}
func (l *GeneralLog) Debug(ctx context.Context, msg string, args ...any) {
l.log(true, dbgLvl, msg, args...)
}
func (l *GeneralLog) Error(msg string, args ...any) {
l.log(true, errLvl, msg, args...)
}
func (l *GeneralLog) NewError(ctx context.Context, msg string, args ...any) error {
return l.logErrorf(true, msg, args...)
}
func (l *GeneralLog) Warn(ctx context.Context, msg string, args ...any) {
l.log(true, warnLvl, msg, args...)
}
func (l *GeneralLog) Table(ctx context.Context, headers []string, data [][]string) {
headerFmt := color.New(color.FgHiBlack, color.Underline).SprintfFunc()
columnFmt := color.New(color.FgHiGreen).SprintfFunc()
var dataToPrint [][]interface{} = make([][]interface{}, 0, len(data))
for _, v := range data {
var row []interface{}
for _, vv := range v {
row = append(row, vv)
}
dataToPrint = append(dataToPrint, row)
}
var header []interface{}
for _, v := range headers {
header = append(header, v)
}
tbl := table.New(header...)
tbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt)
for _, row := range dataToPrint {
tbl.AddRow(row...)
}
tbl.Print()
}
func (l *GeneralLog) boxBox(title, lines string, colorName string) {
var borderColor lipgloss.Color
switch colorName {
case "Red":
borderColor = lipgloss.Color("9") // Bright red
case "Green":
borderColor = lipgloss.Color("10") // Bright green
default:
borderColor = lipgloss.Color("#555555") // Default gray
}
width := max(min(len(lines), 80), min(len(title), 80)) + 2
var builder strings.Builder
builder.WriteString("\n\n")
boxStyle := lipgloss.NewStyle().
BorderStyle(lipgloss.RoundedBorder()).
BorderForeground(borderColor).
Padding(1)
contentStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#FFFFFF")).
Align(lipgloss.Center).
PaddingLeft(1).
PaddingRight(1).Width(width)
titleStyle := lipgloss.NewStyle().
Foreground(borderColor).
Align(lipgloss.Center).
Bold(true).
PaddingBottom(1).
PaddingLeft(1).
PaddingRight(1).Width(width)
cardContent := lipgloss.JoinVertical(lipgloss.Center,
titleStyle.Render(title),
contentStyle.Render(lines),
)
builder.WriteString(boxStyle.Render(cardContent))
builder.WriteString("\n\n")
fmt.Fprintln(l.writter, builder.String())
}
func (l *GeneralLog) Box(ctx context.Context, title string, lines string) {
l.Debug(ctx, "PostUpdate Box", "title", len(title), "lines", len(lines))
l.boxBox(title, lines, "Green")
}
// formatWithRightAlignedTime formats a log message with the elapsed time right-aligned
func formatWithRightAlignedTime(message string, elapsedTime string, width int) string {
// Strip ANSI color codes for length calculation
plainMessage := stripANSIColors(message)
plainTime := stripANSIColors(elapsedTime)
// Check if message has newline
endsWithNewline := strings.HasSuffix(plainMessage, "\n")
// Remove trailing newline for calculations
if endsWithNewline {
plainMessage = plainMessage[:len(plainMessage)-1]
}
// Get the actual message without newline for length calculation
messageWithoutNewline := message
if endsWithNewline && len(message) > 0 {
messageWithoutNewline = message[:len(message)-1]
}
// Calculate available space and padding needed
msgLen := len(plainMessage)
timeLen := len(plainTime)
// Ensure we have enough space, accounting for at least 2 spaces between message and time
padding := max(width-msgLen-timeLen, 2)
// Build the formatted line
var result strings.Builder
result.WriteString(messageWithoutNewline)
result.WriteString(strings.Repeat(" ", padding))
result.WriteString(elapsedTime)
if endsWithNewline {
result.WriteString("\n")
}
return result.String()
}
// stripANSIColors removes ANSI color codes from a string to get its visual length
func stripANSIColors(s string) string {
// ANSI escape code regex: \x1b\[[0-9;]*m
var result strings.Builder
inEscapeSeq := false
for _, r := range s {
if inEscapeSeq {
if r == 'm' {
inEscapeSeq = false
}
continue
}
if r == '\x1b' {
inEscapeSeq = true
continue
}
if !inEscapeSeq {
result.WriteRune(r)
}
}
return result.String()
}
07070100000035000081A40000000000000000000000016818E78D00000742000000000000000000000000000000000000002600000000ksctl-2.8.0/pkg/logger/logger_test.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package logger
import (
"context"
"fmt"
"os"
"testing"
"github.com/ksctl/ksctl/v2/pkg/logger"
)
var (
gL logger.Logger
dummyCtx = context.TODO()
)
func TestMain(m *testing.M) {
gL = NewLogger(-1, os.Stdout)
_ = NewLogger(0, os.Stdout)
exitVal := m.Run()
os.Exit(exitVal)
}
func TestPrinters(t *testing.T) {
t.Run("Success", func(t *testing.T) {
gL.Success(dummyCtx, "FAKE", "type", "success")
})
t.Run("Warn", func(t *testing.T) {
gL.Warn(dummyCtx, "FAKE", "type", "warn")
})
t.Run("Error", func(t *testing.T) {
gL.Error("FAKE", "type", "error")
})
t.Run("Debug", func(t *testing.T) {
gL.Debug(dummyCtx, "FAKE", "type", "debugging")
})
t.Run("Note", func(t *testing.T) {
gL.Note(dummyCtx, "FAKE", "type", "note")
})
t.Run("Print", func(t *testing.T) {
gL.Print(dummyCtx, "FAKE", "type", "print")
})
t.Run("Box", func(t *testing.T) {
gL.Box(dummyCtx, "Abcd", "1")
gL.Box(dummyCtx, "Abcddedefe", "1")
gL.Box(dummyCtx, "KUBECONFIG env var", "/jknc/csdc")
gL.Box(dummyCtx, "KUBECONFIG env var", "jknc")
})
t.Run("external", func(t *testing.T) {
gL.ExternalLogHandler(dummyCtx, logger.LogSuccess, "cdcc")
gL.ExternalLogHandlerf(dummyCtx, logger.LogSuccess, "cdcc", "Reason", fmt.Errorf("Error"))
})
}
07070100000036000041ED0000000000000000000000026818E78D00000000000000000000000000000000000000000000001A00000000ksctl-2.8.0/pkg/telemetry07070100000037000081A40000000000000000000000016818E78D000011F8000000000000000000000000000000000000002700000000ksctl-2.8.0/pkg/telemetry/telemetry.go// Copyright 2025 Ksctl Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package telemetry
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"runtime"
"github.com/fatih/color"
"github.com/ksctl/cli/v2/pkg/config"
"github.com/ksctl/ksctl/v2/pkg/addons"
"github.com/ksctl/ksctl/v2/pkg/consts"
"github.com/ksctl/ksctl/v2/pkg/logger"
)
var (
clientIdentity string
)
type TelemetryEvent string
const (
EventClusterCreate TelemetryEvent = "cluster_create"
EventClusterDelete TelemetryEvent = "cluster_delete"
EventClusterConnect TelemetryEvent = "cluster_connect"
EventClusterList TelemetryEvent = "cluster_list"
EventClusterGet TelemetryEvent = "cluster_get"
EventClusterScaleDown TelemetryEvent = "cluster_scaledown"
EventClusterScaleUp TelemetryEvent = "cluster_scaleup"
EventClusterUpgrade TelemetryEvent = "cli_upgrade"
EventClusterAddonEnable TelemetryEvent = "cluster_addon_enable"
EventClusterAddonDisable TelemetryEvent = "cluster_addon_disable"
)
type TelemetryAddon struct {
Sku string `json:"sku"`
Version string `json:"version"`
Label string `json:"label"`
}
func TranslateMetadata(addons addons.ClusterAddons) []TelemetryAddon {
var telemetryAddons []TelemetryAddon
for _, addon := range addons {
telemetryAddons = append(telemetryAddons, TelemetryAddon{
Sku: addon.Name,
Label: addon.Label,
})
}
return telemetryAddons
}
type TelemetryMeta struct {
CloudProvider consts.KsctlCloud `json:"cloud_provider"`
StorageDriver consts.KsctlStore `json:"storage_driver"`
Region string `json:"cloud_provider_region"`
ClusterType consts.KsctlClusterType `json:"cluster_type"`
BootstrapProvider consts.KsctlKubernetes `json:"bootstrap_provider"`
K8sVersion string `json:"k8s_version"`
Addons []TelemetryAddon `json:"addons"`
}
type TelemetryData struct {
ClientIdentity string `json:"client_identity"`
UserId string `json:"client_id"`
Event TelemetryEvent `json:"event"`
KsctlVer string `json:"ksctl_ver"`
OS string `json:"os"`
Arch string `json:"arch"`
Data TelemetryMeta `json:"meta"`
}
type Telemetry struct {
userId string
ksctlVer string
endpoint string
active bool
os string
arch string
}
func NewTelemetry(active *bool) *Telemetry {
return &Telemetry{
userId: "ksctl:cli",
endpoint: "https://telemetry.ksctl.com",
ksctlVer: config.Version,
active: active == nil || *active,
os: runtime.GOOS,
arch: runtime.GOARCH,
}
}
func IntegrityCheck() {
if config.InDevMode() {
return
}
if len(config.Version) == 0 {
color.New(color.BgHiRed, color.FgHiBlack).Println("corrupted version")
os.Exit(2)
}
if len(clientIdentity) == 0 {
color.New(color.BgHiRed, color.FgHiBlack).Println("corrupted cli identity")
os.Exit(2)
}
if len(config.KsctlCoreVer) == 0 {
color.New(color.BgHiRed, color.FgHiBlack).Println("corrupted core version")
os.Exit(2)
}
if len(config.BuildDate) == 0 {
color.New(color.BgHiRed, color.FgHiBlack).Println("corrupted build date")
os.Exit(2)
}
}
func (t *Telemetry) Send(ctx context.Context, l logger.Logger, event TelemetryEvent, data TelemetryMeta) error {
if !t.active {
return nil
}
telemetryData := TelemetryData{
ClientIdentity: clientIdentity,
UserId: t.userId,
KsctlVer: t.ksctlVer,
Event: event,
Data: data,
OS: t.os,
Arch: t.arch,
}
if config.InDevMode() {
return nil
}
payloadBuf := new(bytes.Buffer)
if err := json.NewEncoder(payloadBuf).Encode(telemetryData); err != nil {
return err
}
if res, err := http.Post(t.endpoint, "application/json", payloadBuf); err != nil {
return err
} else {
if res.StatusCode != http.StatusOK {
return fmt.Errorf("failed to send telemetry, status code: %d", res.StatusCode)
}
l.Debug(ctx, "Telemetry sent successfully")
return nil
}
}
07070100000038000041ED0000000000000000000000026818E78D00000000000000000000000000000000000000000000001400000000ksctl-2.8.0/scripts07070100000039000081ED0000000000000000000000016818E78D000000D9000000000000000000000000000000000000001F00000000ksctl-2.8.0/scripts/builder.sh#!/bin/bash
cd .. || echo -e "\033[1;31mUnable to cd into ksctl root\033[0m\n"
go mod tidy
go build -v -o ksctl .
chmod +x ksctl
sudo mv -v ksctl /usr/local/bin/ksctl
echo -e "\033[1;32mINSTALL COMPLETE\033[0m\n"
0707010000003A000081A40000000000000000000000016818E78D00000A33000000000000000000000000000000000000002000000000ksctl-2.8.0/scripts/install.ps1#Requires -Version 5
$erroractionpreference = 'stop' # quit if anything goes wrong
if (($PSVersionTable.PSVersion.Major) -lt 5) {
Write-Output "PowerShell 5 or later is required to run Ksctl."
Write-Output "Upgrade PowerShell: https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-windows-powershell"
break
}
Write-Host "Welcome to Installation" -ForegroundColor DarkGreen
Write-Host "Available Releases" -ForegroundColor Cyan
$response = Invoke-RestMethod "https://api.github.com/repos/ksctl/cli/releases"
# get the release version
foreach ($release in $response.tag_name) {
Write-Host "${release}" -ForegroundColor Cyan
}
$ReleaseVersion= Read-Host -Prompt "Enter the ksctl version to install"
$Arch= Read-Host -Prompt "Enter [1] for amd64 or x86_64 and [0] for arm64"
Set-Location $env:USERPROFILE
if ($Arch -eq 1) {
$Arch="amd64"
} elseif ($Arch -eq 0) {
$Arch="arm64"
} else {
Write-Host "Invalid Arch" -ForegroundColor Red
Exit 1
}
Invoke-WebRequest -Uri https://github.com/ksctl/cli/releases/download/v${ReleaseVersion}/ksctl-cli_${ReleaseVersion}_checksums.txt -OutFile ksctl_${ReleaseVersion}_checksums.txt
Invoke-WebRequest -Uri https://github.com/ksctl/cli/releases/download/v${ReleaseVersion}/ksctl-cli_${ReleaseVersion}_windows_${Arch}.tar.gz -OutFile ksctl_${ReleaseVersion}_windows_${Arch}.tar.gz
Invoke-WebRequest -Uri https://github.com/ksctl/cli/releases/download/v${ReleaseVersion}/ksctl-cli_${ReleaseVersion}_windows_${Arch}.tar.gz.cert -OutFile ksctl_${ReleaseVersion}_windows_${Arch}.tar.gz.cert
# TODO: Add the checksum verification
# file=$(sha256sum ksctl_${RELEASE_VERSION}_${OS}_${ARCH}.tar.gz | awk '{print $1}')
# checksum=$(cat ksctl_${RELEASE_VERSION}_checksums.txt | grep ksctl_${RELEASE_VERSION}_${OS}_${ARCH}.tar.gz | awk '{print $1}')
# if [[ $file != $checksum ]]; then
# echo -e "${Red}Checksum didn't matched!${NoColor}"
# exit 1
# else
# echo -e "${Green}CheckSum are verified${NoColor}"
# fi
tar -xvf ksctl_${ReleaseVersion}_windows_${Arch}.tar.gz
$localAppDataPath = $env:LOCALAPPDATA
$ksctl = Join-Path "$localAppDataPath" 'ksctl'
Write-Information "Path of AppDataPath $ksctl"
New-Item -ItemType Directory -Force -Path $ksctl | Out-Null
Copy-Item ksctl.exe -Destination "$ksctl/" -Force | Out-Null
Remove-Item ksctl*
Write-Host "[V] Finished Installation" -ForegroundColor DarkGreen
Write-Host ""
Write-Host "To run ksctl globally, please follow these steps:" -ForegroundColor Cyan
Write-Host ""
Write-Host " 1. Run the following command as administrator: ``setx PATH `"`$env:path;$ksctl`" -m``"
0707010000003B000081ED0000000000000000000000016818E78D0000106E000000000000000000000000000000000000001F00000000ksctl-2.8.0/scripts/install.sh#!/usr/bin/env python3
import os
import platform
import subprocess
import urllib.request
import hashlib
import tarfile
import shutil
import json
class Colors:
Red = '\033[1;31m'
Green = '\033[1;32m'
Blue = '\033[1;34m'
Yellow = '\033[1;33m'
NoColor = '\033[0m'
def print_colored(message, color):
print(f"{color}{message}{Colors.NoColor}")
def get_latest_release():
url = "https://api.github.com/repos/ksctl/cli/releases/latest"
with urllib.request.urlopen(url) as response:
data = response.read()
release_info = json.loads(data)
return release_info["tag_name"]
def download_file(url, local_filename):
print_colored(f"Downloading {url} to {local_filename}", Colors.Blue)
try:
with urllib.request.urlopen(url) as response:
with open(local_filename, 'wb') as out_file:
shutil.copyfileobj(response, out_file)
except urllib.error.HTTPError as e:
print_colored(f"HTTP Error: {e.code} - {e.reason}", Colors.Red)
exit(1)
except urllib.error.URLError as e:
print_colored(f"URL Error: {e.reason}", Colors.Red)
exit(1)
def verify_checksum(file_path, expected_checksum):
sha256_hash = hashlib.sha256()
with open(file_path, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
calculated_checksum = sha256_hash.hexdigest()
return calculated_checksum == expected_checksum
def main():
print_colored("All necessary dependencies are present.", Colors.Green)
ksctl_version = os.getenv("KSCTL_VERSION")
if not ksctl_version:
print_colored("Fetching latest release version...", Colors.Blue)
ksctl_version = get_latest_release()
print_colored(f"Using version: {ksctl_version}", Colors.Green)
os_name = platform.system()
arch = platform.machine()
if arch == "x86_64":
arch = "amd64"
elif arch == "arm64":
arch = "arm64"
else:
print_colored(f"Unsupported architecture: {arch}", Colors.Red)
exit(1)
if os_name not in ["Linux", "Darwin"]:
print_colored(f"Unsupported OS: {os_name}", Colors.Red)
exit(1)
os_name = os_name.lower()
print_colored(f"Detected OS: {os_name}, Architecture: {arch}", Colors.Green)
print_colored("Downloading files...", Colors.Blue)
download_url_base = f"https://github.com/ksctl/cli/releases/download/{ksctl_version}"
tar_file = f"ksctl-cli_{ksctl_version[1:]}_{os_name}_{arch}.tar.gz"
checksum_file = f"ksctl-cli_{ksctl_version[1:]}_checksums.txt"
download_file(f"{download_url_base}/{tar_file}", tar_file)
download_file(f"{download_url_base}/{checksum_file}", checksum_file)
print_colored("Verifying checksum...", Colors.Blue)
with open(checksum_file, 'r') as f:
checksums = f.readlines()
expected_checksum = [line.split()[0] for line in checksums if tar_file in line][0]
if not verify_checksum(tar_file, expected_checksum):
print_colored("Checksum verification failed!", Colors.Red)
exit(1)
print_colored("Checksum verification passed.", Colors.Green)
print_colored("Installing ksctl...", Colors.Blue)
temp_dir = "/tmp/ksctl"
os.makedirs(temp_dir, exist_ok=True)
try:
with tarfile.open(tar_file, "r:gz") as tar:
tar.extractall(temp_dir)
ksctl_binary = os.path.join(temp_dir, "ksctl")
if not os.path.isfile(ksctl_binary):
print_colored(f"ksctl binary not found in the tarball", Colors.Red)
exit(1)
print_colored("Moving ksctl to /usr/local/bin (requires sudo)...", Colors.Blue)
subprocess.run(["sudo", "mv", "-v", ksctl_binary, "/usr/local/bin/ksctl"])
if shutil.which("ksctl"):
print_colored("INSTALL COMPLETE!", Colors.Green)
else:
print_colored("Installation failed. Please check the output for errors.", Colors.Red)
exit(1)
finally:
print_colored("Cleaning up temporary files...", Colors.Blue)
shutil.rmtree(temp_dir)
os.remove(tar_file)
os.remove(checksum_file)
if __name__ == "__main__":
main()
0707010000003C000081A40000000000000000000000016818E78D000002CA000000000000000000000000000000000000002200000000ksctl-2.8.0/scripts/uninstall.ps1#Requires -Version 5
$old_erroractionpreference = $erroractionpreference
$erroractionpreference = 'stop' # quit if anything goes wrong
if (($PSVersionTable.PSVersion.Major) -lt 5) {
Write-Output "PowerShell 5 or later is required to run Ksctl."
Write-Output "Upgrade PowerShell: https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-windows-powershell"
break
}
Write-Host "Sorry to see you go" -ForegroundColor DarkGreen
$localAppDataPath = $env:LOCALAPPDATA
$ksctl = Join-Path "$localAppDataPath" 'ksctl'
Remove-Item -Force $ksctl | Out-Null
Remove-Item -Force $env:USERPROFILE\.ksctl | Out-Null
Write-Host "[V] Finished Uninstallation" -ForegroundColor DarkGreen
Write-Host ""
0707010000003D000081ED0000000000000000000000016818E78D0000005F000000000000000000000000000000000000002100000000ksctl-2.8.0/scripts/uninstall.sh#!/bin/bash
sudo rm -vf /usr/local/bin/ksctl
echo -e "\033[1;32mUNINSTALL COMPLETE\033[0m\n"
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!637 blocks