File kubero-cli-2.4.7.obscpio of Package kubero-cli

07070100000000000041ED00000000000000000000000267BEEE0500000000000000000000000000000000000000000000001900000000kubero-cli-2.4.7/.github07070100000001000081A400000000000000000000000167BEEE05000001C9000000000000000000000000000000000000002800000000kubero-cli-2.4.7/.github/dependabot.yml# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file

version: 2
updates:
  - package-ecosystem: "gomod" 
    directory: "/" 
    schedule:
      interval: "weekly"
07070100000002000041ED00000000000000000000000267BEEE0500000000000000000000000000000000000000000000002300000000kubero-cli-2.4.7/.github/workflows07070100000003000081A400000000000000000000000167BEEE0500000364000000000000000000000000000000000000002F00000000kubero-cli-2.4.7/.github/workflows/release.ymlname: goreleaser

on:
  push:
    #branches:
    #  - master

    # Publish `v1.2.3` tags as releases.
    tags:
      - v*
  workflow_dispatch:

jobs:
  goreleaser:
    runs-on: ubuntu-latest
    steps:
      -
        name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      -
        name: Set version
        run: |
          echo -n $(git describe --tags --abbrev=0) > cmd/kuberoCli/VERSION
      -
        name: Set up Go
        uses: actions/setup-go@v3
        with:
          go-version: 1.21
      -
        name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v5
        with:
          distribution: goreleaser
          version: latest
          args: release --clean
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GORELEASER_GITHUB_TOKEN: ${{ secrets.GORELEASER_GITHUB_TOKEN }}07070100000004000081A400000000000000000000000167BEEE0500000180000000000000000000000000000000000000001C00000000kubero-cli-2.4.7/.gitignore# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

/kubero
kubero.yaml
credentials.yaml

notes.txt
pipeline.yaml
app.*.yaml
kind.yaml

cmd/kuberoCli/VERSION

.kubero07070100000005000081A400000000000000000000000167BEEE05000003F6000000000000000000000000000000000000002200000000kubero-cli-2.4.7/.goreleaser.yaml# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
before:
  hooks:
    # You may remove this if you don't use go modules.
    - go mod tidy
builds:
  -
    main: cmd/main.go
    binary: kubero
    env:
      - CGO_ENABLED=0
    goos:
      - linux
      - windows
      - darwin
archives:
  - name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}"
    format_overrides:
        - goos: windows
          format: zip
checksum:
  name_template: 'checksums.txt'
snapshot:
  name_template: "{{ incpatch .Version }}-next"
changelog:
  sort: asc
  filters:
    exclude:
      - '^docs:'
      - '^test:'
      - '^templates:'
      - '^.github:'
brews:
  -
    repository:
      owner: kubero-dev
      name: homebrew-kubero
      token: "{{ .Env.GORELEASER_GITHUB_TOKEN }}"
    homepage: "https://www.kubero.dev"
    description: "Manage your kubero applications with the CLI"
    test: |
      system "#{bin}/kubero", "--version"07070100000006000081A400000000000000000000000167BEEE0500002C5D000000000000000000000000000000000000001900000000kubero-cli-2.4.7/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.
07070100000007000081A400000000000000000000000167BEEE050000192E000000000000000000000000000000000000001B00000000kubero-cli-2.4.7/README.md# kubero-cli

![Version](https://img.shields.io/github/v/release/kubero-dev/kubero-cli)
![Build Status](https://img.shields.io/github/actions/workflow/status/kubero-dev/kubero-cli/build.yml?branch=main)
![License](https://img.shields.io/github/license/kubero-dev/kubero-cli)

A powerful and user-friendly Command Line Interface (CLI) for [Kubero](https://github.com/kubero-dev/kubero), the simplest Platform as a Service (PaaS) for Kubernetes.

> **Note:** Please report any issues in the [main repository](https://github.com/kubero-dev/kubero).

---

## Table of Contents

- [Features](#features)
- [Installation](#installation)
   - [Supported Platforms](#supported-platforms)
   - [1. Shortcut Installation](#1-shortcut-installation)
   - [2. Homebrew Installation](#2-homebrew-installation)
   - [3. Build from Source](#3-build-from-source)
- [Supported Providers](#supported-providers)
- [Usage](#usage)
   - [Command Overview](#command-overview)
- [Provider Credentials](#provider-credentials)
   - [Scaleway](#scaleway)
   - [Linode](#linode)
   - [DigitalOcean](#digitalocean)
   - [Google GKE](#google-gke)
- [Development Guide](#development-guide)
- [Contributing](#contributing)
- [License](#license)
- [Acknowledgments](#acknowledgments)

---

## Features

- **Easy Cluster Deployment:** Quickly create Kubernetes clusters on supported providers.
- **App Management:** Simplify application deployment and management.
- **Pipeline Integration:** Seamlessly integrate CI/CD pipelines.
- **User-Friendly Commands:** Intuitive CLI commands for efficient workflows.
- **Dashboard Access:** Easy access to the Kubero dashboard for monitoring.

---

## Installation

### Supported Platforms

- **macOS**
- **Linux**

### 1. Shortcut Installation

Install Kubero CLI with a single command:

```shell
curl -fsSL get.kubero.dev | bash
```

### 2. Homebrew Installation

If Homebrew is not installed, install it first:

```shell
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```

Install Kubero CLI via Homebrew:

```shell
brew tap kubero-dev/kubero
brew install kubero-cli
```

### 3. Build from Source

For advanced use cases, build and package the binary manually.

#### Requirements

- [Git](https://git-scm.com/downloads)
- [Go](https://go.dev/doc/install)
- [UPX](https://github.com/upx/upx/releases/)

#### Steps

1. **Clone the Repository:**

   ```shell
   git clone https://github.com/kubero-dev/kubero-cli.git
   ```

2. **Navigate to the Project Directory:**

   ```shell
   cd kubero-cli
   ```

3. **Create a Version Tag (Optional):**

   ```shell
   git tag -a v1.0 -m "Version 1.0"
   ```

4. **Build and Package the Binary:**

   ```shell
   cd cmd
   go build -ldflags "-s -w -X main.version=$(git describe --tags --abbrev=0) -X main.commit=$(git rev-parse --short HEAD) -X main.date=$(date +%Y-%m-%d)" -trimpath -o kubero-cli
   upx kubero-cli
   mv kubero-cli ../kubero
   cd ..
   ```

5. **Move the Binary to Your PATH:**

   ```shell
   sudo mv kubero /usr/local/bin/
   ```

6. **Reload Shell Configuration:**

   ```shell
   source "$HOME/.$(basename ${SHELL})rc"
   ```

7. **Verify Installation:**

   ```shell
   kubero --version
   ```

---

## Supported Providers

Kubero CLI currently supports the following cloud providers:

- **Scaleway**
- **Linode**
- **DigitalOcean**
- **Google GKE**
- **Kind** (local clusters)

### Coming Soon

- **Vultr**
- **Oracle Cloud OCI/OKE**
- **Exoscale**
- **Swissflow**

---

## Usage

### Command Overview

```plaintext
kubero
├── install                # Create a Kubernetes cluster and install Kubero with all required components
├── list                   # List all running clusters
├── login                  # Log in to Kubero and save credentials
├── logout                 # Log out from Kubero and remove saved credentials
├── create                 # Create new app and pipeline configurations
│   ├── app
│   └── pipeline
├── up                     # Deploy apps and pipelines
│   ├── app
│   └── pipeline
├── down                   # Remove apps and pipelines
│   ├── app
│   └── pipeline
├── fetch                  # Sync configurations to local files
│   ├── app
│   └── pipeline
├── dashboard              # Open the Kubero dashboard
├── tunnel                 # Open a tunnel to a NAT-ed cluster
├── instance               # Manage Kubero instances
│   ├── create             # Create an instance configuration
│   ├── delete             # Delete an instance configuration
│   └── select             # Select an active instance
├── config                 # View available configurations
│   ├── addons             # List addons
│   ├── buildpacks         # List buildpacks
│   └── podsizes           # List pod size configurations
└── help                   # Display help for commands
```

---

## Provider Credentials

Set the appropriate environment variables for your cloud provider before using Kubero CLI.

### Scaleway

```shell
export SCALEWAY_ACCESS_TOKEN=your_access_token
export SCALEWAY_PROJECT_ID=your_project_id
export SCALEWAY_ORGANIZATION_ID=your_organization_id
```

### Linode

```shell
export LINODE_ACCESS_TOKEN=your_access_token
```

### DigitalOcean

```shell
export DIGITALOCEAN_ACCESS_TOKEN=your_access_token
```

### Google GKE

```shell
export GOOGLE_API_KEY=your_api_key
```

---

## Development Guide

### Enable Development Mode

To enable development mode for testing and debugging, create a `VERSION` file:

```shell
echo "dev" > cmd/kuberoCli/VERSION
```

---

## Contributing

We welcome contributions from the community! Please check out our [Contributing Guidelines](https://github.com/kubero-dev/kubero/blob/main/CONTRIBUTING.md) for more information.

---

## License

This project is licensed under the [MIT License](LICENSE).

---

## Acknowledgments

- **[Kubero](https://github.com/kubero-dev/kubero):** The simplest PaaS for Kubernetes.
- **[Go](https://golang.org/):** The programming language used for development.
- **Community Contributors:** Thank you to all who have contributed to this project.

---

Thank you for using **kubero-cli**! If you have suggestions or encounter issues, please open an issue in the [main repository](https://github.com/kubero-dev/kubero).

---
07070100000008000081A400000000000000000000000167BEEE050000002C000000000000000000000000000000000000001A00000000kubero-cli-2.4.7/_headers/*
  Content-Type: text/plain; charset=UTF-807070100000009000041ED00000000000000000000000267BEEE0500000000000000000000000000000000000000000000001500000000kubero-cli-2.4.7/cmd0707010000000A000041ED00000000000000000000000267BEEE0500000000000000000000000000000000000000000000001F00000000kubero-cli-2.4.7/cmd/kuberoCli0707010000000B000081A400000000000000000000000167BEEE0500000B68000000000000000000000000000000000000002600000000kubero-cli-2.4.7/cmd/kuberoCli/app.gopackage kuberoCli

import (
	"encoding/json"
	"kubero/pkg/kuberoApi"
	"log"
	"os"
	"strings"

	"github.com/i582/cfmt/cmd/cfmt"
	"github.com/olekukonko/tablewriter"
	"github.com/spf13/viper"
)

func appsList() {

	pipelineResp, _ := api.GetPipelineApps(pipelineName)

	var pl Pipeline
	jsonUnmarshalErr := json.Unmarshal(pipelineResp.Body(), &pl)
	if jsonUnmarshalErr != nil {
		log.Fatal(jsonUnmarshalErr)
		return
	}

	for _, phase := range pl.Phases {
		if !phase.Enabled {
			continue
		}
		_, _ = cfmt.Print("\n")

		_, _ = cfmt.Println("{{  " + strings.ToUpper(phase.Name) + "}}::bold|white" + " (" + phase.Context + ")")

		table := tablewriter.NewWriter(os.Stdout)
		table.SetHeader([]string{
			"Name",
			"Phase",
			"Pipeline",
			"Repository",
			"Domain",
		})
		table.SetBorder(false)

		for _, app := range phase.Apps {
			table.Append([]string{
				app.Name,
				app.Phase,
				app.Pipeline,
				app.Gitrepo.CloneURL + ":" +
					app.Gitrepo.DefaultBranch,
				app.Domain,
			})
		}

		printCLI(table, pipelineResp)
	}
}

func getAllRemoteApps() []string {
	apps, _ := api.GetApps()
	var appShortList []appShort
	jsonUnmarshalErr := json.Unmarshal(apps.Body(), &appShortList)
	if jsonUnmarshalErr != nil {
		log.Fatal(jsonUnmarshalErr)
		return nil
	}

	var appsList []string
	for _, app := range appShortList {
		if pipelineName != "" && app.Pipeline != pipelineName {
			continue
		}
		if stageName != "" && app.Phase != stageName {
			continue
		}
		if appName != "" && app.Name != appName {
			continue
		}
		appsList = append(appsList, app.Name)
	}

	return appsList
}

func getAllLocalApps() []string {

	baseDir := getIACBaseDir()
	dir := baseDir + "/" + pipelineName + "/" + stageName

	var appsList []string
	appFiles, err := os.ReadDir(dir)
	if err != nil {
		log.Fatal(err)
	}

	for _, appFileName := range appFiles {

		// remove the .yaml extension
		appName := strings.TrimSuffix(appFileName.Name(), ".yaml")

		a := loadLocalApp(pipelineName, stageName, appName)
		if a.Kind == "KuberoApp" && a.Metadata.Name != "" {
			appsList = append(appsList, a.Metadata.Name)
		}
	}
	return appsList
}

func loadLocalApp(pipelineName string, stageName string, appName string) kuberoApi.AppCRD {

	appConfig := loadAppConfig(pipelineName, stageName, appName)

	var appCRD kuberoApi.AppCRD

	appConfigUnmarshalErr := appConfig.Unmarshal(&appCRD)
	if appConfigUnmarshalErr != nil {
		log.Fatal(appConfigUnmarshalErr)
		return kuberoApi.AppCRD{}
	}

	return appCRD
}

func loadAppConfig(pipelineName string, stageName string, appName string) *viper.Viper {

	baseDir := getIACBaseDir()
	dir := baseDir + "/" + pipelineName + "/" + stageName

	appConfig := viper.New()
	appConfig.SetConfigName(appName)
	appConfig.SetConfigType("yaml")
	appConfig.AddConfigPath(dir)
	readInConfigErr := appConfig.ReadInConfig()
	if readInConfigErr != nil {
		log.Fatal(readInConfigErr)
		return nil
	}

	return appConfig
}
0707010000000C000081A400000000000000000000000167BEEE0500000274000000000000000000000000000000000000002900000000kubero-cli-2.4.7/cmd/kuberoCli/config.gopackage kuberoCli

import (
	"github.com/spf13/cobra"
)

// configCmd represents the config command
var configCmd = &cobra.Command{
	Use:   "config",
	Short: "Show your configuration",
	/*
			Long: `A longer description that spans multiple lines and likely contains examples
		and usage of using your command. For example:

		Cobra is a CLI library for Go that empowers applications.
		This application is a tool to generate the needed files
		to quickly create a Cobra application.`,
			Run: func(cmd *cobra.Command, args []string) {
				fmt.Println("config called")
			},
	*/
}

func init() {
	rootCmd.AddCommand(configCmd)
}
0707010000000D000081A400000000000000000000000167BEEE05000003E3000000000000000000000000000000000000002F00000000kubero-cli-2.4.7/cmd/kuberoCli/configAddons.gopackage kuberoCli

import (
	"encoding/json"
	"os"
	"strconv"

	"github.com/go-resty/resty/v2"
	"github.com/olekukonko/tablewriter"
	"github.com/spf13/cobra"
)

// addonsCmd represents the addons command
var addonsCmd = &cobra.Command{
	Use:   "addons",
	Short: "A brief description of your command",
	Run: func(cmd *cobra.Command, args []string) {
		resp, _ := api.GetAddons()
		//fmt.Println(resp)
		printAddons(resp)
	},
}

func init() {
	configCmd.AddCommand(addonsCmd)
}

// print the response as a table
func printAddons(r *resty.Response) {

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Name", "Description", "Version", "Beta", "Enabled"})
	table.SetRowLine(true)
	//table.SetBorder(false)

	var addonsList []Addon
	json.Unmarshal(r.Body(), &addonsList)

	for _, addon := range addonsList {
		table.Append([]string{addon.ID, addon.Description, addon.Version.Installed, strconv.FormatBool(addon.Beta), strconv.FormatBool(addon.Enabled)})
	}

	printCLI(table, r)
}
0707010000000E000081A400000000000000000000000167BEEE05000006A5000000000000000000000000000000000000003300000000kubero-cli-2.4.7/cmd/kuberoCli/configBuildpacks.gopackage kuberoCli

import (
	"encoding/json"
	"os"

	"github.com/go-resty/resty/v2"
	"github.com/olekukonko/tablewriter"
	"github.com/spf13/cobra"
)

// buildpacksCmd represents the buildpacks command
var buildpacksCmd = &cobra.Command{
	Use:   "buildpacks",
	Short: "List the available buildpacks",
	Run: func(cmd *cobra.Command, args []string) {
		resp, _ := api.GetBuildpacks()
		printBuildpacks(resp)
	},
}

func init() {
	configCmd.AddCommand(buildpacksCmd)
}

var buildPacksSimpleList []string

func loadBuildpacks() {

	b, _ := api.GetBuildpacks()

	var buildPacks buildPacks
	json.Unmarshal(b.Body(), &buildPacks)

	for _, buildPack := range buildPacks {
		buildPacksSimpleList = append(buildPacksSimpleList, buildPack.Name)
	}

	//buildPacks = []string{"java", "node", "python", "ruby", "php"}
}

// print the response as a table
func printBuildpacks(r *resty.Response) {

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Name", "Language", "Phase", "Image", "Command"})
	table.SetAutoMergeCells(true)
	table.SetRowLine(true)
	//table.SetBorder(false)

	var buildPacksList buildPacks
	json.Unmarshal(r.Body(), &buildPacksList)

	for _, podsize := range buildPacksList {
		table.Append([]string{
			podsize.Name,
			podsize.Language,
			"Fetch",
			podsize.Fetch.Repository + ":" + podsize.Fetch.Tag,
			"git clone",
		})
		table.Append([]string{
			podsize.Name,
			podsize.Language,
			"Build",
			podsize.Build.Repository + ":" + podsize.Build.Tag,
			podsize.Build.Command,
		})
		table.Append([]string{
			podsize.Name,
			podsize.Language,
			"Run",
			podsize.Run.Repository + ":" + podsize.Run.Tag + " (ro)",
			podsize.Run.Command,
		})
	}

	printCLI(table, r)
}
0707010000000F000081A400000000000000000000000167BEEE0500000378000000000000000000000000000000000000003100000000kubero-cli-2.4.7/cmd/kuberoCli/configPodsizes.gopackage kuberoCli

import (
	"encoding/json"
	"fmt"
	"os"

	"github.com/go-resty/resty/v2"
	"github.com/olekukonko/tablewriter"
	"github.com/spf13/cobra"
)

var podSizesCmd = &cobra.Command{
	Use:   "podsizes",
	Short: "List the available pod sizes",
	Run: func(cmd *cobra.Command, args []string) {
		resp, _ := api.GetPodsize()
		printPodSizes(resp)
	},
}

func init() {
	configCmd.AddCommand(podSizesCmd)
}

func printPodSizes(r *resty.Response) {

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Name", "Description"})
	//table.SetBorder(false)

	var podsizeList []PodSize
	unmarshalErr := json.Unmarshal(r.Body(), &podsizeList)
	if unmarshalErr != nil {
		fmt.Println("Failed to unmarshal the response body:", unmarshalErr)
		return
	}

	for _, podsize := range podsizeList {
		table.Append([]string{podsize.Name, podsize.Description})
	}

	printCLI(table, r)
}
07070100000010000081A400000000000000000000000167BEEE0500001ED9000000000000000000000000000000000000002900000000kubero-cli-2.4.7/cmd/kuberoCli/create.gopackage kuberoCli

/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/

import (
	"fmt"
	"kubero/pkg/kuberoApi"

	"os"
	"strconv"

	"github.com/AlecAivazis/survey/v2"
	"github.com/i582/cfmt/cmd/cfmt"
	"github.com/spf13/cobra"
	"gopkg.in/yaml.v3"
)

// createCmd represents the create command
var createCmd = &cobra.Command{
	Use:     "create",
	Aliases: []string{"cr", "add", "new"},
	Short:   "Create a new pipeline and/or app",
	Long:    `Initiate a new pipeline and app in your current repository.`,
	Run: func(cmd *cobra.Command, args []string) {
		createPipelineAndApp()
	},
}

func init() {
	rootCmd.AddCommand(createCmd)
	createCmd.PersistentFlags().StringVarP(&pipelineName, "pipeline", "p", "", "name of the pipeline")
}

func createPipelineAndApp() {
	createPipelineAndApp := promptLine("Create a new pipeline", "[y,n]", "y")
	if createPipelineAndApp == "y" {
		createPipeline()
	}

	pipelinesList := getAllLocalPipelines()
	ensurePipelineIsSet(pipelinesList)
	ensureStageNameIsSet()
	ensureAppNameIsSet()
	createApp()
}

func appForm() kuberoApi.AppCRD {

	var appCRD kuberoApi.AppCRD

	appconfig := loadAppConfig(pipelineName, stageName, appName)
	pipelineConfig := loadPipelineConfig(pipelineName)

	appCRD.APIVersion = "application.kubero.dev/v1alpha1"
	appCRD.Kind = "KuberoApp"

	appCRD.Spec.Name = appName
	appCRD.Spec.Pipeline = pipelineName
	appCRD.Spec.Phase = stageName

	appCRD.Spec.Domain = promptLine("Domain", "", appconfig.GetString("spec.domain"))

	unmarshalKeyErr := pipelineConfig.UnmarshalKey("spec.git.repository", &appCRD.Spec.Gitrepo)
	if unmarshalKeyErr != nil {
		fmt.Println(unmarshalKeyErr)
		return kuberoApi.AppCRD{}
	}

	gitURL := pipelineConfig.GetString("spec.git.repository.sshurl")
	appCRD.Spec.Branch = promptLine("Branch", gitURL+":", appconfig.GetString("spec.branch"))

	appCRD.Spec.Buildpack = pipelineConfig.GetString("spec.buildpack.name")

	autodeployDefault := "n"
	if !appconfig.GetBool("spec.autodeploy") {
		autodeployDefault = "y"
	}
	autodeploy := promptLine("Autodeploy", "[y,n]", autodeployDefault)
	if autodeploy == "Y" {
		appCRD.Spec.Autodeploy = true
	} else {
		appCRD.Spec.Autodeploy = false
	}

	envCount, _ := strconv.Atoi(promptLine("Number of Env Vars", "", "0"))
	appCRD.Spec.EnvVars = []string{}
	for i := 0; i < envCount; i++ {
		appCRD.Spec.EnvVars = append(appCRD.Spec.EnvVars, promptLine("Env Var", "", ""))
	}

	appCRD.Spec.Image.ContainerPort, _ = strconv.Atoi(promptLine("Container Port", "8080", appconfig.GetString("spec.image.containerport")))

	appCRD.Spec.Web = kuberoApi.Web{}
	appCRD.Spec.Web.ReplicaCount, _ = strconv.Atoi(promptLine("Web Pods", "1", appconfig.GetString("spec.web.replicacount")))

	appCRD.Spec.Worker = kuberoApi.Worker{}
	appCRD.Spec.Worker.ReplicaCount, _ = strconv.Atoi(promptLine("Worker Pods", "0", appconfig.GetString("spec.worker.replicacount")))

	return appCRD
}

func createApp() {

	appCRD := appForm()

	writeAppYaml(appCRD)

	_, _ = cfmt.Println("\n\n{{Created appCRD.yaml}}::green")
}

func writeAppYaml(appCRD kuberoApi.AppCRD) {
	// write pipeline.yaml
	yamlData, err := yaml.Marshal(&appCRD)

	if err != nil || appCRD.Spec.Name == "" {
		panic("Unable to write data into the file")
	}

	fileName := ".kubero/" + appCRD.Spec.Pipeline + "/" + appCRD.Spec.Phase + "/" + appCRD.Spec.Name + ".yaml"

	err = os.WriteFile(fileName, yamlData, 0644)
	if err != nil {
		panic("Unable to write data into the file")
	}
}

func createPipeline() kuberoApi.PipelineCRD {

	loadConfigs(pipelineName)

	loadRepositories()
	loadContexts()
	loadBuildpacks()
	pipelineCRD := pipelinesForm()

	writePipelineYaml(pipelineCRD)

	_, _ = cfmt.Println("\n\n{{Created pipeline.yaml}}::green")
	_, _ = cfmt.Println(pipelineName)

	return pipelineCRD
}

func writePipelineYaml(pipeline kuberoApi.PipelineCRD) {
	basePath := "/.kubero/" //TODO Make it dynamic

	gitdir := getGitdir()
	dir := gitdir + basePath + pipelineName
	err := os.MkdirAll(dir, 0755)

	if err != nil {
		fmt.Println(err)
		panic("Unable to create directory")
	}

	yamlData, err := yaml.Marshal(&pipeline)

	// iterate over phases to create the directory
	for _, phase := range pipeline.Spec.Phases {
		if phase.Enabled {
			err := os.MkdirAll(dir+"/"+phase.Name, 0755)
			if err != nil {
				fmt.Println(err)
				panic("Unable to create directory")
			}
		}
	}

	if err != nil {
		fmt.Printf("Error while Marshaling. %v", err)
	}
	//fmt.Println(string(yamlData))

	fileName := dir + "/pipeline.yaml"
	err = os.WriteFile(fileName, yamlData, 0644)
	if err != nil {
		panic("Unable to write data into the file")
	}
}

func pipelinesForm() kuberoApi.PipelineCRD {

	var pipelineCRD kuberoApi.PipelineCRD

	if pipelineName == "" {
		pipelineName = promptLine("Define a PipelineName name", "", "")
	}
	pipelineCRD.Spec.Name = pipelineName

	pipelineCRD.APIVersion = "application.kubero.dev/v1alpha1"
	pipelineCRD.Kind = "KuberoPipeline"

	fmt.Println("")
	prompt := &survey.Select{
		Message: "Select a buildpack",
		Options: buildPacksSimpleList,
	}
	askOneErr := survey.AskOne(prompt, &pipelineCRD.Spec.Buildpack.Name)
	if askOneErr != nil {
		fmt.Println(askOneErr.Error())
		return kuberoApi.PipelineCRD{}
	}

	domain := pipelineConfig.GetString("spec.domain")
	pipelineCRD.Spec.Domain = promptLine("FQDN Domain ", "", domain)

	// those fields are deprecated and may be removed in the future
	pipelineCRD.Spec.DockerImage = ""
	pipelineCRD.Spec.DeploymentStrategy = "git"

	gitconnection := promptLine("Connect pipeline to a Git repository (GitOps)", "[y,n]", "n")

	contextDefault := contextSimpleList[0]
	if gitconnection == "y" {
		gitProvider := pipelineConfig.GetString("spec.git.repository.provider")
		pipelineCRD.Spec.Git.Repository.Provider = promptLine("Repository Provider", fmt.Sprint(repoSimpleList), gitProvider)

		gitURL := pipelineConfig.GetString("spec.git.repository.sshurl")
		pipelineCRD.Spec.Git.Repository.SshUrl = promptLine("Repository URL", "["+getGitRemote()+"]", gitURL)

		phaseReview := promptLine("enable reviewapps", "[y,n]", "n")
		if phaseReview == "y" {
			pipelineCRD.Spec.ReviewApps = true
			pipelineCRD.Spec.Phases = append(pipelineCRD.Spec.Phases, kuberoApi.Phase{
				Name:    "review",
				Enabled: true,
				Context: promptLine("Context for reviewapps", fmt.Sprint(contextSimpleList), contextDefault),
			})
		} else {
			pipelineCRD.Spec.ReviewApps = false
			pipelineCRD.Spec.Phases = append(pipelineCRD.Spec.Phases, kuberoApi.Phase{
				Name:    "review",
				Enabled: false,
				Context: "",
			})
		}
	}

	phaseTest := promptLine("enable test", "[y,n]", "n")
	if phaseTest == "y" {
		pipelineCRD.Spec.Phases = append(pipelineCRD.Spec.Phases, kuberoApi.Phase{
			Name:    "test",
			Enabled: true,
			Context: promptLine("Context for test", fmt.Sprint(contextSimpleList), contextDefault),
		})
	} else {
		pipelineCRD.Spec.Phases = append(pipelineCRD.Spec.Phases, kuberoApi.Phase{
			Name:    "test",
			Enabled: false,
			Context: "",
		})
	}

	phaseStage := promptLine("enable stage", "[y,n]", "n")
	if phaseStage == "y" {
		pipelineCRD.Spec.Phases = append(pipelineCRD.Spec.Phases, kuberoApi.Phase{
			Name:    "stage",
			Enabled: true,
			Context: promptLine("Context for stage", fmt.Sprint(contextSimpleList), contextDefault),
		})
	} else {
		pipelineCRD.Spec.Phases = append(pipelineCRD.Spec.Phases, kuberoApi.Phase{
			Name:    "stage",
			Enabled: false,
			Context: "",
		})
	}

	phaseProduction := promptLine("enable production", "[y,n]", "y")
	if phaseProduction != "n" {
		pipelineCRD.Spec.Phases = append(pipelineCRD.Spec.Phases, kuberoApi.Phase{
			Name:    "production",
			Enabled: true,
			Context: promptLine("Context for production ", fmt.Sprint(contextSimpleList), contextDefault),
		})
	} else {
		pipelineCRD.Spec.Phases = append(pipelineCRD.Spec.Phases, kuberoApi.Phase{
			Name:    "production",
			Enabled: false,
			Context: "",
		})
	}

	return pipelineCRD
}
07070100000011000081A400000000000000000000000167BEEE0500000328000000000000000000000000000000000000002C00000000kubero-cli-2.4.7/cmd/kuberoCli/createApp.gopackage kuberoCli

import (
	"github.com/spf13/cobra"
)

// appCmd represents the app command
var createAppCmd = &cobra.Command{
	Use:   "app",
	Short: "Create a new app in a Pipeline",
	Long: `Create a new app in a Pipeline.

If called without arguments, it will ask for all the required information`,
	Run: func(cmd *cobra.Command, args []string) {

		pipelinesList := getAllLocalPipelines()
		ensurePipelineIsSet(pipelinesList)
		ensureStageNameIsSet()
		ensureAppNameIsSet()
		createApp()

	},
}

func init() {
	createCmd.AddCommand(createAppCmd)
	createAppCmd.Flags().StringVarP(&pipelineName, "pipeline", "p", "", "Name of the pipeline")
	createAppCmd.Flags().StringVarP(&stageName, "stage", "s", "", "Name of the stage")
	createAppCmd.Flags().StringVarP(&appName, "app", "a", "", "Name of the app")
}
07070100000012000081A400000000000000000000000167BEEE0500000231000000000000000000000000000000000000003100000000kubero-cli-2.4.7/cmd/kuberoCli/createPipeline.gopackage kuberoCli

/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/

import (
	"fmt"

	"github.com/spf13/cobra"
)

var pipelineName string
var stageName string
var appName string

// pipelineCmd represents the pipeline command
var createPipelineCmd = &cobra.Command{
	Use:     "pipeline",
	Aliases: []string{"pl"},
	Short:   "Create a new pipeline",
	Long:    `Create a new Pipeline`,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println("create a new pipeline")

		_ = createPipeline()
	},
}

func init() {
	createCmd.AddCommand(createPipelineCmd)
}
07070100000013000081A400000000000000000000000167BEEE05000002B1000000000000000000000000000000000000002C00000000kubero-cli-2.4.7/cmd/kuberoCli/dashboard.gopackage kuberoCli

/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/

import (
	"github.com/pkg/browser"
	"github.com/spf13/cobra"
	"log"
)

// dashboardCmd represents the dashboard command
var dashboardCmd = &cobra.Command{
	Use:     "dashboard",
	Aliases: []string{"db"},
	Short:   "Opens the Kubero dashboard in your browser",
	Long:    `Use the dashboard subcommand to open the Kubero dashboard in your browser.`,
	Run: func(cmd *cobra.Command, args []string) {

		url := currentInstance.ApiUrl
		openURLErr := browser.OpenURL(url)
		if openURLErr != nil {
			log.Fatal("Failed to open the browser:", openURLErr)
			return
		}

	},
}

func init() {
	rootCmd.AddCommand(dashboardCmd)
}
07070100000014000081A400000000000000000000000167BEEE0500000CAC000000000000000000000000000000000000002800000000kubero-cli-2.4.7/cmd/kuberoCli/debug.gopackage kuberoCli

/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/

import (
	_ "embed"
	"os"
	"os/exec"
	"runtime"

	"github.com/i582/cfmt/cmd/cfmt"
	"github.com/spf13/cobra"
)

// debugCmd represents the debug command
var debugCmd = &cobra.Command{
	Use:     "debug",
	Aliases: []string{"dbg"},
	Short:   "Print debug informations",
	Long: `This command will print debug informations like:
	- Kubero CLI version
	- OS/Arch
	- Kubernetes version
	- Kuberop operator version
	- Kuberop operator namespace
	- Kubernetes metrics server version
	- Kubernetes cert-manager version`,
	Run: func(cmd *cobra.Command, args []string) {
		_, _ = cfmt.Println("{{Kubero CLI}}::bold")
		printCLIVersion()
		printOsArch()
		_, _ = cfmt.Println("\n{{Kubernetes}}::bold")
		printKubernetesVersion()
		_, _ = cfmt.Println("{{Kubero Operator}}::bold")
		checkKuberoOperator()
		_, _ = cfmt.Println("{{\nKubero UI}}::bold")
		checkKuberoUI()
		_, _ = cfmt.Println("{{\nCert Manager}}::bold")
		checkCertManager()
	},
}

func init() {
	rootCmd.AddCommand(debugCmd)
}

func printCLIVersion() {
	_, _ = cfmt.Println("kuberoCLIVersion: ", kuberoCliVersion)
}

func printOsArch() {
	_, _ = cfmt.Println("OS: ", runtime.GOOS)
	_, _ = cfmt.Println("Arch: ", runtime.GOARCH)
	_, _ = cfmt.Println("goVersion: ", runtime.Version())
}

func printKubernetesVersion() {
	hasKubectl := checkBinary("kubectl")
	if !hasKubectl {
		promptWarning("kubectl is not installed. Installer won't be able to install kubero. Please install kubectl and try again.")
		os.Exit(1)
	}
	version, _ := exec.Command("kubectl", "version", "-o", "yaml").Output()
	_, _ = cfmt.Println(string(version))
}

func checkKuberoOperator() {
	cmdOut, _ := exec.Command("kubectl", "get", "deployments.apps", "-n", "kubero-operator-system").Output()
	_, _ = cfmt.Print(string(cmdOut))

	_, _ = cfmt.Println("{{\nKubero Operator Image}}::bold")
	cmdOut, _ = exec.Command("kubectl", "get", "deployment", "kubero-operator-controller-manager", "-o=jsonpath={$.spec.template.spec.containers[:1].image}", "-n", "kubero-operator-system").Output()
	_, _ = cfmt.Print(string(cmdOut))
	_, _ = cfmt.Println("")
}

func checkKuberoUI() {
	cmdOut, _ := exec.Command("kubectl", "get", "deployments.apps", "-n", "kubero").Output()
	_, _ = cfmt.Print(string(cmdOut))

	_, _ = cfmt.Println("{{\nKubero UI Ingress}}::bold")
	cmdOut, _ = exec.Command("kubectl", "get", "ingress", "-n", "kubero").Output()
	_, _ = cfmt.Print(string(cmdOut))

	_, _ = cfmt.Println("{{\nKubero UI Secrets}}::bold")
	cmdOut, _ = exec.Command("kubectl", "get", "secrets", "-n", "kubero").Output()
	_, _ = cfmt.Print(string(cmdOut))

	_, _ = cfmt.Println("{{\nKubero UI Image}}::bold")
	cmdOut, _ = exec.Command("kubectl", "get", "deployment", "kubero", "-o=jsonpath={$.spec.template.spec.containers[:1].image}", "-n", "kubero").Output()
	_, _ = cfmt.Print(string(cmdOut))
	_, _ = cfmt.Println("")
}

func checkCertManager() {
	cmdOut, _ := exec.Command("kubectl", "get", "deployments.apps", "-n", "cert-manager").Output()
	_, _ = cfmt.Print(string(cmdOut))

	_, _ = cfmt.Println("{{\nCert Manager Cluster Issuers}}::bold")
	cmdOut, _ = exec.Command("kubectl", "get", "clusterissuers.cert-manager.io").Output()
	_, _ = cfmt.Print(string(cmdOut))
}
07070100000015000081A400000000000000000000000167BEEE05000007E5000000000000000000000000000000000000002700000000kubero-cli-2.4.7/cmd/kuberoCli/down.gopackage kuberoCli

import (
	"github.com/spf13/cobra"
)

// downCmd represents the down command
var downCmd = &cobra.Command{
	Use:     "down",
	Aliases: []string{"undeploy", "dn"},
	Short:   "Undeploy your pipelines and apps from the cluster",
	Long: `Use the pipeline or app subcommand to undeploy your pipelines and apps from the cluster
Subcommands:
  kubero down [pipeline|app]`,
	Run: func(cmd *cobra.Command, args []string) {
		if pipelineName != "" && appName == "" {
			downPipeline()
		} else if appName != "" {
			downApp()
		} else {
			downAllPipelines()
		}
	},
}

func init() {
	rootCmd.AddCommand(downCmd)
	downCmd.Flags().StringVarP(&pipelineName, "pipeline", "p", "", "name of the pipeline")
	downCmd.Flags().StringVarP(&stageName, "stage", "s", "", "Name of the stage [test|stage|production]")
	downCmd.Flags().StringVarP(&appName, "app", "a", "", "name of the app")
	downCmd.PersistentFlags().BoolVarP(&force, "force", "f", false, "Skip asking for confirmation")
}

func downPipeline() {
	pipelinesList := getAllRemotePipelines()
	ensurePipelineIsSet(pipelinesList)
	downPipelineByName(pipelineName)
}

func downPipelineByName(pipelineName string) {
	confirmationLine("Are you sure you want to undeploy the pipeline '"+pipelineName+"'?", "y")

	_, err := api.UnDeployPipeline(pipelineName)
	if err != nil {
		panic("Unable to undeploy Pipeline")
	}
}

func downApp() {

	pipelinesList := getAllRemotePipelines()
	ensurePipelineIsSet(pipelinesList)

	ensureStageNameIsSet()

	appsList := getAllRemoteApps()
	ensureAppNameIsSelected(appsList)

	confirmationLine("Are you sure you want to undeploy the app "+appName+" from "+stageName+" in "+pipelineName+"?", "y")

	_, err := api.UnDeployApp(pipelineName, stageName, appName)
	if err != nil {
		panic("Unable to undeploy App")
	}
}

func downAllPipelines() {
	confirmationLine("Are you sure you want to undeploy all pipelines?", "y")
	pipelinesList := getAllLocalPipelines()
	for _, pipeline := range pipelinesList {
		downPipelineByName(pipeline)
	}
}
07070100000016000081A400000000000000000000000167BEEE05000002BB000000000000000000000000000000000000002A00000000kubero-cli-2.4.7/cmd/kuberoCli/downApp.gopackage kuberoCli

/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/

import (
	"github.com/spf13/cobra"
)

// appCmd represents the app command
var downAppCmd = &cobra.Command{
	Use:   "app",
	Short: "Undeploy an apps from the cluster",
	Long:  `Use the app subcommand to undeploy your apps from the cluster`,
	Run: func(cmd *cobra.Command, args []string) {
		downApp()
	},
}

func init() {
	downCmd.AddCommand(downAppCmd)
	downAppCmd.Flags().StringVarP(&pipelineName, "pipeline", "p", "", "name of the pipeline")
	downAppCmd.Flags().StringVarP(&stageName, "stage", "s", "", "Name of the stage [test|stage|production]")
	downAppCmd.Flags().StringVarP(&appName, "app", "a", "", "name of the app")
}
07070100000017000081A400000000000000000000000167BEEE050000024B000000000000000000000000000000000000002F00000000kubero-cli-2.4.7/cmd/kuberoCli/downPipeline.gopackage kuberoCli

/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/

import (
	"github.com/spf13/cobra"
)

// appCmd represents the app command
var downPipelineCmd = &cobra.Command{
	Use:     "pipeline",
	Aliases: []string{"pl"},
	Short:   "Undeploy a pipeline from the cluster",
	Long:    `Use the pipeline subcommand to undeploy your pipelines from the cluster`,
	Run: func(cmd *cobra.Command, args []string) {
		downPipeline()
	},
}

func init() {
	downCmd.AddCommand(downPipelineCmd)
	downPipelineCmd.Flags().StringVarP(&pipelineName, "pipeline", "p", "", "name of the pipeline")
}
07070100000018000081A400000000000000000000000167BEEE0500000D6F000000000000000000000000000000000000002800000000kubero-cli-2.4.7/cmd/kuberoCli/fetch.gopackage kuberoCli

import (
	"encoding/json"
	"fmt"
	"kubero/pkg/kuberoApi"
	"os"

	"github.com/i582/cfmt/cmd/cfmt"
	"github.com/spf13/cobra"
)

// fetchCmd represents the fetch command
var fetchCmd = &cobra.Command{
	Use:     "fetch",
	Aliases: []string{"pull", "fe"},
	Short:   "Fetch your remote pipelines and apps to your local repository",
	Long:    `Use the pipeline or app subcommand to fetch your pipelines and apps to your local repository`,
	Run: func(cmd *cobra.Command, args []string) {
		if pipelineName != "" && appName == "" {
			fetchPipeline(pipelineName)
		} else if pipelineName != "" && appName != "" {
			ensureStageNameIsSet()
			fetchPipeline(pipelineName)
			fetchApp(appName, stageName, pipelineName)
		} else {
			fetchAllPipelines()
		}
	},
}

func init() {
	rootCmd.AddCommand(fetchCmd)
	fetchCmd.Flags().StringVarP(&pipelineName, "pipeline", "p", "", "Name of the pipeline")
	fetchCmd.Flags().StringVarP(&stageName, "stage", "s", "", "Name of the stage [test|stage|production]")
	fetchCmd.Flags().StringVarP(&appName, "app", "a", "", "Name of the app")
}

func fetchPipeline(pipelineName string) {
	confirmation := promptLine("Do you want to fetch the pipeline '"+pipelineName+"'?", "[y,n]", "y")
	if confirmation == "y" {
		_, _ = cfmt.Println("{{Fetching pipeline}}::yellow " + pipelineName)

		var pipeline kuberoApi.PipelineCRD

		pipeline.APIVersion = "application.kubero.dev/v1alpha1"
		pipeline.Kind = "KuberoPipeline"
		pipeline.Spec.Name = pipelineName
		pipeline.Metadata.Name = appName

		p, pipelineErr := api.GetPipeline(pipelineName)

		if pipelineErr != nil {
			if p == nil {
				_, _ = cfmt.Println("{{ERROR:}}::red Pipeline '" + pipelineName + "' not found ")
				os.Exit(1)
			}
			if p.StatusCode() == 404 {
				_, _ = cfmt.Println("{{ERROR:}}::red Pipeline '" + pipelineName + "' not found ")
				os.Exit(1)
			}
			fmt.Println(pipelineErr)
			os.Exit(1)
		}

		jsonUnmarshalErr := json.Unmarshal(p.Body(), &pipeline.Spec)
		if jsonUnmarshalErr != nil {
			fmt.Println(jsonUnmarshalErr)
			return
		}
		writePipelineYaml(pipeline)

	} else {
		return
	}
}

func fetchApp(appName string, stageName string, pipelineName string) {

	confirmation := promptLine("Do you want to fetch the app '"+appName+"' from '"+pipelineName+"'?", "[y,n]", "y")
	if confirmation == "y" {
		_, _ = cfmt.Println("{{Fetching app}}::yellow " + appName + "")
	} else {
		_, _ = cfmt.Println("{{Aborted}}::red")
		return
	}

	var app kuberoApi.AppCRD
	app.APIVersion = "application.kubero.dev/v1alpha1"
	app.Kind = "KuberoApp"

	app.Spec.Pipeline = pipelineName
	app.Spec.Phase = stageName
	app.Spec.Name = appName
	app.Metadata.Name = appName

	a, appErr := api.GetApp(pipelineName, stageName, appName)

	if appErr != nil {
		if a == nil {
			_, _ = cfmt.Println("{{ERROR:}}::red App '" + appName + "' not found ")
			os.Exit(1)
		}
		if a.StatusCode() == 404 {
			_, _ = cfmt.Println("{{ERROR:}}::red App '" + appName + "' not found ")
			os.Exit(1)
		}
		fmt.Println(appErr)
		os.Exit(1)
	}

	jsonUnmarshalErr := json.Unmarshal(a.Body(), &app)
	if jsonUnmarshalErr != nil {
		fmt.Println(jsonUnmarshalErr)
		return
	}

	writeAppYaml(app)

}

func fetchAllPipelines() {
	confirmation := promptLine("Are you sure you want to fetch all pipelines?", "[y,n]", "n")
	if confirmation == "y" {
		_, _ = cfmt.Println("{{Fetching all pipelines}}::yellow")
	} else {
		_, _ = cfmt.Println("{{Aborted}}::red")
		return
	}
}
07070100000019000081A400000000000000000000000167BEEE05000004A3000000000000000000000000000000000000002B00000000kubero-cli-2.4.7/cmd/kuberoCli/fetchApp.gopackage kuberoCli

/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/

import (
	"os"

	"github.com/i582/cfmt/cmd/cfmt"
	"github.com/spf13/cobra"
)

// appCmd represents the app command
var fetchAppCmd = &cobra.Command{
	Use:   "app",
	Short: "Fetch an app",
	Long:  `Fetch an app`,
	Run: func(cmd *cobra.Command, args []string) {

		pipelinesList := getAllRemotePipelines()
		if len(pipelinesList) == 0 {
			_, _ = cfmt.Println("\n{{ERROR:}}::red No pipelines found")
			os.Exit(1)
		}
		ensurePipelineIsSet(pipelinesList)
		ensureStageNameIsSet()
		fetchPipeline(pipelineName)

		appsList := getAllRemoteApps()
		if len(appsList) == 0 {
			_, _ = cfmt.Println("\n{{ERROR:}}::red No apps found in pipeline '" + pipelineName + "'")
			os.Exit(1)
		}

		ensureAppNameIsSelected(appsList)
		ensureAppNameIsSet()
		fetchApp(appName, stageName, pipelineName)
	},
}

func init() {
	fetchCmd.AddCommand(fetchAppCmd)
	fetchAppCmd.Flags().StringVarP(&pipelineName, "pipeline", "p", "", "Name of the pipeline")
	fetchAppCmd.Flags().StringVarP(&stageName, "stage", "s", "", "Name of the stage [test|stage|production]")
	fetchAppCmd.Flags().StringVarP(&appName, "app", "a", "", "Name of the app")
}
0707010000001A000081A400000000000000000000000167BEEE050000026B000000000000000000000000000000000000003000000000kubero-cli-2.4.7/cmd/kuberoCli/fetchPipeline.gopackage kuberoCli

/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/

import (
	"github.com/spf13/cobra"
)

// pipelineCmd represents the pipeline command
var fetchPipelineCmd = &cobra.Command{
	Use:     "pipeline",
	Aliases: []string{"pl"},
	Short:   "Fetch a pipeline",
	Long:    `Fetch a pipeline`,
	Run: func(cmd *cobra.Command, args []string) {
		pipelinesList := getAllRemotePipelines()
		ensurePipelineIsSet(pipelinesList)
		fetchPipeline(pipelineName)
	},
}

func init() {
	fetchCmd.AddCommand(fetchPipelineCmd)
	fetchPipelineCmd.Flags().StringVarP(&pipelineName, "pipeline", "p", "", "Name of the pipeline")
}
0707010000001B000081A400000000000000000000000167BEEE050000A10A000000000000000000000000000000000000002A00000000kubero-cli-2.4.7/cmd/kuberoCli/install.gopackage kuberoCli

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"log"
	"math/rand"
	"os"
	"os/exec"
	"strings"
	"time"

	"encoding/json"

	"github.com/go-resty/resty/v2"
	"github.com/i582/cfmt/cmd/cfmt"
	"github.com/leaanthony/spinner"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"golang.org/x/crypto/bcrypt"
	"gopkg.in/yaml.v3"
	"k8s.io/client-go/tools/clientcmd"
)

// installCmd represents the install command
var installCmd = &cobra.Command{
	Use:   "install",
	Short: "Create a Kubernetes cluster and install all required components for kubero",
	Long: `This command will create a kubernetes cluster and install all required components 
for kubero on any kubernetes cluster.

required binaries:
 - kubectl
 - kind (optional)`,
	Run: func(cmd *cobra.Command, args []string) {
		rand.New(rand.NewSource(time.Now().UnixNano()))

		checkAllBinaries()

		switch argComponent {
		case "metrics":
			installMetrics()
			return
		case "certManager":
			installCertManager()
			return
		case "olm":
			installOLM()
			return
		case "kubero-operator":
			installKuberoOperator()
			return
		case "kubero-ui":
			installKuberoUi()
			return
		case "ingress":
			installIngress()
			return
		case "monitoring":
			installMonitoring()
			return
		case "kubernetes":
			installKubernetes()
			checkCluster()
			return
		case "":
			printInstallSteps()
			installKubernetes()     // 1
			checkCluster()          //
			installOLM()            // 2
			installKuberoOperator() // 3
			installIngress()        // 4
			installMetrics()        // 5
			installCertManager()    // 6
			installMonitoring()     // 7
			installKuberoUi()       // 8
			writeCLIConfig()        // 9
			printDNSinfo()
			finalMessage()
			return
		default:
			return
		}
	},
}

var argAdminPassword string
var argAdminUser string
var argDomain string
var argApiToken string
var argPort string
var argPortSecure string
var clusterType string
var argComponent string
var installOlm bool
var monitoringInstalled bool
var ingressControllerVersion = "v1.10.0" // https://github.com/kubernetes/ingress-nginx/tags -> controller-v1.5.1

// var clusterTypeSelection = "[scaleway,linode,gke,digitalocean,kind]"
var clusterTypeList = []string{"kind", "linode", "scaleway", "gke", "digitalocean"}

func init() {
	installCmd.Flags().StringVarP(&argComponent, "component", "c", "", "install component (kubernetes,olm,ingress,metrics,certManager,kubero-operator,monitoring,kubero-ui)")
	installCmd.Flags().StringVarP(&argAdminUser, "user", "u", "", "Admin username for the kubero UI")
	installCmd.Flags().StringVarP(&argAdminPassword, "user-password", "U", "", "Password for the admin user")
	installCmd.Flags().StringVarP(&argApiToken, "apiToken", "a", "", "API token for the admin user")
	installCmd.Flags().StringVarP(&argPort, "port", "p", "", "Kubero UI HTTP port")
	installCmd.Flags().StringVarP(&argPortSecure, "securePort", "P", "", "Kubero UI HTTPS port")
	installCmd.Flags().StringVarP(&argDomain, "domain", "d", "", "Domain name for the kubero UI")
	rootCmd.AddCommand(installCmd)

	installOlm = false
	monitoringInstalled = false
}

func checkAllBinaries() {
	_, _ = cfmt.Println("\n  Check for required binaries")
	if !checkBinary("kubectl") {
		_, _ = cfmt.Println("{{✗ kubectl is not installed}}::red")
	} else {
		_, _ = cfmt.Println("{{✓ kubectl is installed}}::lightGreen")
	}

	if !checkBinary("kind") {
		_, _ = cfmt.Println("{{⚠ kind is not installed}}::yellow (only required if you want to install a local kind cluster)")
	} else {
		_, _ = cfmt.Println("{{✓ kind is installed}}::lightGreen")
	}

	if !checkBinary("gcloud") {
		_, _ = cfmt.Println("{{⚠ gcloud is not installed}}::yellow (only required if you want to install a GKE cluster)")
	} else {
		_, _ = cfmt.Println("{{✓ gcloud is installed}}::lightGreen")
	}
}

func printInstallSteps() {

	_, _ = cfmt.Print(`
  Steps to install kubero:
    1. Create a kubernetes cluster {{(optional)}}::gray
    2. Install the OLM {{(optional)}}::gray
    3. Install the kubero operator {{(required)}}::gray
    4. Install the ingress controller {{(required)}}::gray
    5. Install the metrics server {{(optional, but recommended)}}::gray
    6. Install the cert-manager {{(optional)}}::gray
    7. Install the monitoring stack {{(optional, but recommended)}}::gray
    8. Install the kubero UI {{(optional, but highly recommended)}}::gray
    9. Write the kubero CLI config
`)
}

func checkBinary(binary string) bool {
	_, err := exec.LookPath(binary)
	return err == nil
}

func installKubernetes() {
	kubernetesInstall := promptLine("1) Create a kubernetes cluster", "[y,n]", "y")
	if kubernetesInstall != "y" {
		return
	}

	clusterType = selectFromList("Select a Kubernetes provider", clusterTypeList, "")

	switch clusterType {
	case "scaleway":
		installScaleway()
	case "linode":
		installLinode()
	case "gke":
		installGKE()
	case "digitalocean":
		installDigitalOcean()
	case "kind":
		installKind()
	default:
		_, _ = cfmt.Println("{{✗ Unknown cluster type}}::red")
		os.Exit(1)
	}

}

func tellAChucknorrisJoke() {

	jokesApi := resty.New().
		SetHeader("Accept", "application/json").
		SetHeader("Content-Type", "application/json").
		SetHeader("User-Agent", "kubero-cli/"+kuberoCliVersion).
		SetBaseURL("https://api.chucknorris.io/jokes/random")

	joke, _ := jokesApi.R().Get("?category=dev")
	var jokeResponse JokeResponse
	_ = json.Unmarshal(joke.Body(), &jokeResponse)
	_, _ = cfmt.Println("\r{{  " + jokeResponse.Value + "       }}::gray")
}

func mergeKubeconfig(kubeconfig []byte) error {

	newDefaultPathOptions := clientcmd.NewDefaultPathOptions()
	config1, _ := newDefaultPathOptions.GetStartingConfig()
	config2, err := clientcmd.Load(kubeconfig)
	if err != nil {
		return err
	}
	// append the second config to the first
	for k, v := range config2.Clusters {
		config1.Clusters[k] = v
	}
	for k, v := range config2.AuthInfos {
		config1.AuthInfos[k] = v
	}
	for k, v := range config2.Contexts {
		config1.Contexts[k] = v
	}

	config1.CurrentContext = config2.CurrentContext

	_ = clientcmd.ModifyConfig(clientcmd.DefaultClientConfig.ConfigAccess(), *config1, true)
	return nil
}

func checkCluster() {
	var outb, errb bytes.Buffer

	clusterInfo := exec.Command("kubectl", "cluster-info")
	clusterInfo.Stdout = &outb
	clusterInfo.Stderr = &errb
	err := clusterInfo.Run()
	if err != nil {
		fmt.Println(errb.String())
		fmt.Println(outb.String())
		log.Fatal("command failed : kubectl cluster-info")
	}
	fmt.Println(outb.String())

	out, _ := exec.Command("kubectl", "config", "get-contexts").Output()
	fmt.Println(string(out))

	clusterSelect := promptLine("Is the CURRENT cluster the one you wish to install Kubero?", "[y,n]", "y")
	if clusterSelect == "n" {
		os.Exit(0)
	}
}

func installOLM() {

	openshiftInstalled, _ := exec.Command("kubectl", "get", "deployment", "olm-operator", "-n", "openshift-operator-lifecycle-manager").Output()
	if len(openshiftInstalled) > 0 {
		_, _ = cfmt.Println("{{✓ OLM is already installed}}::lightGreen")
		return
	}

	//namespace := promptLine("Install OLM in which namespace?", "[openshift-operator-lifecycle-manager,olm]", "olm")
	namespace := "olm"
	olmInstalled, _ := exec.Command("kubectl", "get", "deployment", "olm-operator", "-n", namespace).Output()
	if len(olmInstalled) > 0 {
		_, _ = cfmt.Println("{{✓ OLM is already installed}}::lightGreen")
		return
	}

	olmInstall := promptLine("2) Install OLM", "[y,n]", "n")
	if olmInstall != "y" {
		installOlm = false
		return
	} else {
		installOlm = true
	}

	olmVersionList := getGithubVersionList("operator-framework/operator-lifecycle-manager", 10)
	olmRelease := selectFromList("Select OLM version", olmVersionList, "")
	olmURL := "https://github.com/operator-framework/operator-lifecycle-manager/releases/download/" + olmRelease

	olmSpinner := spinner.New("Install OLM")

	olmCRDInstalled, _ := exec.Command("kubectl", "get", "crd", "subscriptions.operators.coreos.com").Output()
	if len(olmCRDInstalled) > 0 {
		_, _ = cfmt.Println("{{✓ OLM CRD's already installed}}::lightGreen")
	} else {
		_, _ = cfmt.Println("  run command : kubectl create -f " + olmURL + "/olm.yaml")
		olmSpinner.Start("Installing OLM CRDs")
		_, olmCRDErr := exec.Command("kubectl", "create", "-f", olmURL+"/crds.yaml").Output()
		if olmCRDErr != nil {
			fmt.Println("")
			olmSpinner.Error("OLM CRD installation failed. Try running this command manually: kubectl create -f " + olmURL + "/crds.yaml")
			log.Fatal(olmCRDErr)
		} else {
			olmSpinner.Success("OLM CRDs installed successfully")
		}
	}

	_, _ = cfmt.Println("  run command : kubectl create -f " + olmURL + "/olm.yaml")
	olmSpinner.Start("Install OLM")

	_, olmOLMErr := exec.Command("kubectl", "create", "-f", olmURL+"/olm.yaml").Output()
	if olmOLMErr != nil {
		fmt.Println("")
		olmSpinner.Error("Failed to run command. Try running this command manually: kubectl create -f " + olmURL + "/olm.yaml")
		log.Fatal(olmOLMErr)
	}
	olmSpinner.Success("OLM installed successfully")

	olmWaitSpinner := spinner.New("Wait for OLM to be ready")
	_, _ = cfmt.Println("  run command : kubectl wait --for=condition=available deployment/olm-operator -n " + namespace + " --timeout=180s")
	olmWaitSpinner.Start("Wait for OLM to be ready")
	_, olmWaitErr := exec.Command("kubectl", "wait", "--for=condition=available", "deployment/olm-operator", "-n", namespace, "--timeout=180s").Output()
	if olmWaitErr != nil {
		olmWaitSpinner.Error("Failed to run command. Try running this command manually: kubectl wait --for=condition=available deployment/olm-operator -n " + namespace + " --timeout=180s")
		log.Fatal(olmWaitErr)
	}
	olmWaitSpinner.Success("OLM is ready")

	olmWaitCatalogSpinner := spinner.New("Wait for OLM Catalog to be ready")
	_, _ = cfmt.Println("  run command : kubectl wait --for=condition=available deployment/catalog-operator -n " + namespace + " --timeout=180s")
	olmWaitCatalogSpinner.Start("Wait for OLM Catalog to be ready")
	_, olmWaitCatalogErr := exec.Command("kubectl", "wait", "--for=condition=available", "deployment/catalog-operator", "-n", namespace, "--timeout=180s").Output()
	if olmWaitCatalogErr != nil {
		olmWaitCatalogSpinner.Error("Failed to run command. Try running this command manually: kubectl wait --for=condition=available deployment/catalog-operator -n " + namespace + " --timeout=180s")
		log.Fatal(olmWaitCatalogErr)
	}
	olmWaitCatalogSpinner.Success("OLM Catalog is ready")
}

func installMetrics() {

	installed, _ := exec.Command("kubectl", "get", "deployments.apps", "metrics-server", "-n", "kube-system").Output()
	if len(installed) > 0 {
		_, _ = cfmt.Println("{{✓ Metrics is already enabled}}::lightGreen")
		return
	}
	install := promptLine("5) Install Kubernetes internal metrics service (required for HPA, Horizontal Pod Autoscaling)", "[y,n]", "y")
	if install != "y" {
		return
	}

	//components := "https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml"
	components := "https://raw.githubusercontent.com/kubero-dev/kubero-operator/main/deploy/metrics-server.yaml"
	_, installErr := exec.Command("kubectl", "apply", "-f", components).Output()

	if installErr != nil {
		fmt.Println("failed to install metrics server")
		log.Fatal(installErr)
	}
	_, _ = cfmt.Println("{{✓ Metrics server installed}}::lightGreen")
}

func installIngress() {

	ingressInstalled, _ := exec.Command("kubectl", "get", "ns", "ingress-nginx").Output()
	if len(ingressInstalled) > 0 {
		_, _ = cfmt.Println("{{✓ Ingress is already installed}}::lightGreen")
		return
	}

	ingressInstall := promptLine("4) Install Ingress", "[y,n]", "y")
	if ingressInstall != "y" {
		return
	} else {

		if clusterType == "" {
			clusterType = selectFromList("Which cluster type have you installed?", clusterTypeList, "")
		}

		prefill := "baremetal"
		switch clusterType {
		case "kind":
			prefill = "kind"
		case "linode":
			prefill = "cloud"
		case "gke":
			prefill = "cloud"
		case "scaleway":
			prefill = "scw"
		case "digitalocean":
			prefill = "do"
		}

		ingressProviderList := []string{"kind", "aws", "baremetal", "cloud", "do", "exoscale", "scw"}
		ingressProvider := selectFromList("Provider [kind, aws, baremetal, cloud(Azure,Google,Oracle,Linode), do(digital ocean), exoscale, scw(scaleway)]", ingressProviderList, prefill)

		// ingressController version can bot be loaded from GitHub api, since the return is alphabetic
		ingressSpinner := spinner.New("Install Ingress")
		URL := "https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-" + ingressControllerVersion + "/deploy/static/provider/" + ingressProvider + "/deploy.yaml"
		_, _ = cfmt.Println("  run command : kubectl apply -f " + URL)
		ingressSpinner.Start("Install Ingress")
		_, ingressErr := exec.Command("kubectl", "apply", "-f", URL).Output()
		if ingressErr != nil {
			ingressSpinner.Error("Failed to run command. Try running this command manually: kubectl apply -f " + URL)
			log.Fatal(ingressErr)
		}

		ingressSpinner.Success("Ingress installed successfully")
	}

}

func installKuberoOperator() {

	_, _ = cfmt.Println("\n  {{3) Install Kubero Operator}}::bold")

	kuberoInstalled, _ := exec.Command("kubectl", "get", "operator", "kubero-operator.operators").Output()
	if len(kuberoInstalled) > 0 {
		_, _ = cfmt.Println("{{✓ Kubero Operator is already installed}}::lightGreen")
		return
	}

	if installOlm {
		installKuberoOLMOperator()
	} else {
		installKuberoOperatorSlim()
	}
}

func installKuberoOLMOperator() {

	kuberoSpinner := spinner.New("Install Kubero Operator")
	_, _ = cfmt.Println("  run command : kubectl apply -f https://operatorhub.io/install/kubero-operator.yaml")
	kuberoSpinner.Start("Install Kubero Operator")
	_, kuberoErr := exec.Command("kubectl", "apply", "-f", "https://operatorhub.io/install/kubero-operator.yaml").Output()
	if kuberoErr != nil {
		fmt.Println("")
		kuberoSpinner.Error("Failed to run command to install the Operator. Try running this command manually: kubectl apply -f https://operatorhub.io/install/kubero-operator.yaml")
		log.Fatal(kuberoErr)
	}

	kuberoSpinner.UpdateMessage("Wait for Kubero Operator to be ready")
	var kuberoWait []byte
	for len(kuberoWait) == 0 {
		// kubectl api-resources --api-group=application.kubero.dev --no-headers=true
		kuberoWait, _ = exec.Command("kubectl", "api-resources", "--api-group=application.kubero.dev", "--no-headers=true").Output()
		time.Sleep(1 * time.Second)
	}

	kuberoSpinner.Success("Kubero Operator installed successfully")

}

func installKuberoOperatorSlim() {

	kuberoSpinner := spinner.New("Install Kubero Operator")
	_, _ = cfmt.Println("  run command : kubectl apply -f https://raw.githubusercontent.com/kubero-dev/kubero-operator/main/deploy/operator.yaml")
	kuberoSpinner.Start("Install Kubero Operator")
	_, kuberoErr := exec.Command("kubectl", "apply", "-f", "https://raw.githubusercontent.com/kubero-dev/kubero-operator/main/deploy/operator.yaml").Output()
	if kuberoErr != nil {
		fmt.Println("")
		kuberoSpinner.Error("Failed to run command to install the Operator. Try running this command manually: kubectl apply -f https://raw.githubusercontent.com/kubero-dev/kubero-operator/main/deploy/operator.yaml")
		log.Fatal(kuberoErr)
	}

	kuberoSpinner.UpdateMessage("Wait for Kubero Operator CRD's to be installed")
	var kuberoWait []byte
	for len(kuberoWait) == 0 {
		// kubectl api-resources --api-group=application.kubero.dev --no-headers=true
		kuberoWait, _ = exec.Command("kubectl", "api-resources", "--api-group=application.kubero.dev", "--no-headers=true").Output()
		time.Sleep(1 * time.Second)
	}
	kuberoSpinner.UpdateMessage("Kubero Operator CRD's installed")

	time.Sleep(5 * time.Second)
	// kubectl wait --for=condition=available deployment/kubero -n kubero --timeout=180s
	kuberoSpinner.UpdateMessage("Wait for Kubero Operator to be ready")
	_, olmWaitErr := exec.Command("kubectl", "wait", "--for=condition=available", "deployment/kubero-operator-controller-manager", "-n", "kubero-operator-system", "--timeout=300s").Output()
	if olmWaitErr != nil {
		kuberoSpinner.Error("Failed to wait for Kubero UI to become ready")
		log.Fatal(olmWaitErr)
	}
	kuberoSpinner.Success("Kubero Operator installed successfully")

}

func createNamespace(namespace string) {

	kuberoNSInstalled, _ := exec.Command("kubectl", "get", "ns", namespace).Output()
	if len(kuberoNSInstalled) > 0 {
		_, _ = cfmt.Printf("{{✓ Namespace %s exists}}::lightGreen\n", namespace)
	} else {
		_, kuberoNSErr := exec.Command("kubectl", "create", "namespace", namespace).Output()
		if kuberoNSErr != nil {
			fmt.Println("Failed to run command to create the namespace. Try running this command manually: kubectl create namespace " + namespace)
			log.Fatal(kuberoNSErr)
		} else {
			_, _ = cfmt.Printf("{{✓ Namespace %s created}}::lightGreen\n", namespace)
		}
	}
}

func installKuberoUi() {

	ingressInstall := promptLine("9) Install Kubero UI", "[y,n]", "y")
	if ingressInstall != "y" {
		return
	}

	createNamespace("kubero")

	kuberoSecretInstalled, _ := exec.Command("kubectl", "get", "secret", "kubero-secrets", "-n", "kubero").Output()
	if len(kuberoSecretInstalled) > 0 {
		_, _ = cfmt.Println("{{✓ Kubero Secret exists}}::lightGreen")
	} else {

		webhookSecret := promptLine("Random string for your webhook secret", "", generateRandomString(20, ""))

		sessionKey := promptLine("Random string for your session key", "", generateRandomString(20, ""))

		if argAdminUser == "" {
			argAdminUser = promptLine("Admin User", "", "admin")
		}

		if argAdminPassword == "" {
			argAdminPassword = promptLine("Admin Password", "", generateRandomString(12, ""))
		}

		if argApiToken == "" {
			argApiToken = promptLine("Random string for admin API token", "", generateRandomString(20, ""))
		}

		var userDB []User
		userDB = append(userDB, User{Username: argAdminUser, Password: argAdminPassword, Insecure: true, ApiToken: argApiToken})
		userDBjson, _ := json.Marshal(userDB)
		userDBencoded := base64.StdEncoding.EncodeToString(userDBjson)

		createSecretCommand := exec.Command("kubectl", "create", "secret", "generic", "kubero-secrets",
			"--from-literal=KUBERO_WEBHOOK_SECRET="+webhookSecret,
			"--from-literal=KUBERO_SESSION_KEY="+sessionKey,
			"--from-literal=KUBERO_USERS="+userDBencoded,
		)

		githubConfigure := promptLine("Configure Github", "[y,n]", "y")
		githubPersonalAccessToken := ""
		if githubConfigure == "y" {
			githubPersonalAccessToken = promptLine("Github personal access token", "", "")
			createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITHUB_PERSONAL_ACCESS_TOKEN="+githubPersonalAccessToken)
		}

		giteaConfigure := promptLine("Configure Gitea", "[y,n]", "n")
		giteaPersonalAccessToken := ""
		giteaBaseUrl := ""
		if giteaConfigure == "y" {
			giteaPersonalAccessToken = promptLine("Gitea personal access token", "", "")
			giteaBaseUrl = promptLine("Gitea URL", "http://localhost:3000", "")
			createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITEA_PERSONAL_ACCESS_TOKEN="+giteaPersonalAccessToken)
			createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITEA_BASEURL="+giteaBaseUrl)
		}

		gogsConfigure := promptLine("Configure Gogs", "[y,n]", "n")
		gogsPersonalAccessToken := ""
		gogsBaseUrl := ""
		if gogsConfigure == "y" {
			gogsPersonalAccessToken = promptLine("Gogs personal access token", "", "")
			gogsBaseUrl = promptLine("Gogs URL", "http://localhost:3000", "")
			createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GOGS_PERSONAL_ACCESS_TOKEN="+gogsPersonalAccessToken)
			createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GOGS_BASEURL="+gogsBaseUrl)
		}

		gitlabConfigure := promptLine("Configure Gitlab", "[y,n]", "n")
		gitlabPersonalAccessToken := ""
		gitlabBaseUrl := ""
		if gitlabConfigure == "y" {
			gitlabPersonalAccessToken = promptLine("Gitlab personal access token", "", "")
			gitlabBaseUrl = promptLine("Gitlab URL", "http://localhost:3080", "")
			createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITLAB_PERSONAL_ACCESS_TOKEN="+gitlabPersonalAccessToken)
			createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=GITLAB_BASEURL="+gitlabBaseUrl)
		}

		bitbucketConfigure := promptLine("Configure Bitbucket", "[y,n]", "n")
		bitbucketUsername := ""
		bitbucketAppPassword := ""
		if bitbucketConfigure == "y" {
			bitbucketUsername = promptLine("Bitbucket Username", "", "")
			bitbucketAppPassword = promptLine("Bitbucket App Password", "", "")
			createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=BITBUCKET_USERNAME="+bitbucketUsername)
			createSecretCommand.Args = append(createSecretCommand.Args, "--from-literal=BITBUCKET_APP_PASSWORD="+bitbucketAppPassword)
		}

		createSecretCommand.Args = append(createSecretCommand.Args, "-n", "kubero")

		_, kuberoErr := createSecretCommand.Output()

		if kuberoErr != nil {
			_, _ = cfmt.Println("{{✗ Failed to run command to create the secrets.}}::red")
			log.Fatal(kuberoErr)
		} else {
			_, _ = cfmt.Println("{{✓ Kubero Secret created}}::lightGreen")
		}
	}

	kuberoUIInstalled, _ := exec.Command("kubectl", "get", "kuberoes.application.kubero.dev", "-n", "kubero").Output()
	if len(kuberoUIInstalled) > 0 {
		_, _ = cfmt.Println("{{✓ Kubero UI already installed}}::lightGreen")
	} else {
		installer := resty.New()

		installer.SetBaseURL("https://raw.githubusercontent.com")
		kf, _ := installer.R().Get("kubero-dev/kubero-operator/main/config/samples/application_v1alpha1_kubero.yaml")

		var kuberoUIConfig KuberoUIConfig
		_ = yaml.Unmarshal(kf.Body(), &kuberoUIConfig)

		if argDomain == "" {
			argDomain = promptLine("Kubero UI Domain", "", "kubero.localhost")
		}
		kuberoUIConfig.Spec.Ingress.Hosts[0].Host = argDomain

		// Warn if domain contains kubero.net

		webhookDomain := argDomain
		if strings.Contains(argDomain, "kubero.localhost") {
			_, _ = cfmt.Println("{{⚠ kubero.localhost might not be reachable won't get any Webhooks. GitHub will fail to connect your pipeline. }}::yellow")
			webhookDomain = "webhook.local.kubero.net"
		}

		webhookURL := promptLine("URL to which the webhooks should be sent (localhost fails with GitHub)", "", "https://"+webhookDomain+"/api/repo/webhooks")
		kuberoUIConfig.Spec.Kubero.WebhookURL = webhookURL

		kuberoUISsl := promptLine("Enable SSL for the Kubero UI", "[y/n]", "y")
		if kuberoUISsl == "y" {

			clusterIssuer := promptLine("Kubero UI ClusterIssuer", "", "letsencrypt-prod")
			kuberoUIConfig.Spec.Ingress.Annotations.KubernetesIoIngressClass = clusterIssuer
			kuberoUIConfig.Spec.Ingress.Annotations.KubernetesIoTlsAcme = "true"

			kuberoUIConfig.Spec.Ingress.TLS = []KuberoUITls{
				{
					Hosts:      []string{argDomain},
					SecretName: "kubero-tls",
				},
			}
		}

		kuberoUIRegistry := promptLine("Enable BuildPipeline for Kubero (BETA)", "[y/n]", "n")
		if kuberoUIRegistry == "y" {
			kuberoUIConfig.Spec.Registry.Enabled = true

			kuberoUICreateRegistry := promptLine("Create a local Registry for Kubero", "[y/n]", "n")
			if kuberoUICreateRegistry == "y" {
				kuberoUIConfig.Spec.Registry.Create = true

				kuberoUIRegistryStorage := promptLine("Registry storage size", "", "10Gi")
				kuberoUIConfig.Spec.Registry.Storage = kuberoUIRegistryStorage

				storageClassList := getAvailableStorageClasses()

				kuberoUIRegistryStorageClassName := selectFromList("Registry storage class", storageClassList, "")
				kuberoUIConfig.Spec.Registry.StorageClassName = kuberoUIRegistryStorageClassName
			}

			kuberoUIRegistryHost := promptLine("Registry", "[registry.kubero.mydomain.com]", "")
			kuberoUIConfig.Spec.Registry.Host = kuberoUIRegistryHost

			kuberoUIRegistrySubPath := promptLine("SubPath (optional) ", "[example/foo/bar]", "")
			kuberoUIConfig.Spec.Registry.SubPath = kuberoUIRegistrySubPath

			kuberoUIConfig.Spec.Registry.Port = 443

			kuberoUIRegistryUsername := promptLine("Registry username", "", "admin")
			kuberoUIConfig.Spec.Registry.Account.Username = kuberoUIRegistryUsername

			kuberoUIRegistryPassword := promptLine("Registry password", "", generateRandomString(12, ""))
			kuberoUIConfig.Spec.Registry.Account.Password = kuberoUIRegistryPassword

			kuberoUIRegistryPasswordBytes, _ := bcrypt.GenerateFromPassword([]byte(kuberoUIRegistryPassword), 14)
			kuberoUIConfig.Spec.Registry.Account.Hash = string(kuberoUIRegistryPasswordBytes)
		}

		kuberoUIAudit := promptLine("Enable Audit Logging", "[y/n]", "n")
		if kuberoUIAudit == "y" {
			kuberoUIConfig.Spec.Kubero.AuditLogs.Enabled = true

			storageClassList := getAvailableStorageClasses()

			kuberoUIRegistryStorageClassName := selectFromList("AuditLogs storage class", storageClassList, "")
			kuberoUIConfig.Spec.Kubero.AuditLogs.StorageClassName = kuberoUIRegistryStorageClassName

		}

		if monitoringInstalled {
			kuberoUIConfig.Spec.Prometheus.Enabled = true
			kuberoUIConfig.Spec.Prometheus.Endpoint = promptLine("Prometheus URL", "", "http://kubero-prometheus-server")
		} else {
			kuberoUIConfig.Spec.Prometheus.Enabled = false
		}

		kuberoUIConsole := promptLine("Enable Console Access to running containers", "[y/n]", "y")

		if kuberoUIConsole == "y" {
			kuberoUIConfig.Spec.Kubero.Config.Kubero.Console.Enabled = true
		}

		//kuberoUIConfig.Spec.Image.Tag = "v2.0.0-rc.8"

		if clusterType == "" {
			clusterType = selectFromList("Which cluster type have you installed?", clusterTypeList, "")
		}

		if clusterType == "linode" ||
			clusterType == "digitalocean" ||
			clusterType == "scaleway" ||
			clusterType == "gke" {
			kuberoUIConfig.Spec.Ingress.ClassName = "nginx"
		}

		kuberoUIYaml, _ := yaml.Marshal(kuberoUIConfig)
		kuberoUIErr := os.WriteFile("kuberoUI.yaml", kuberoUIYaml, 0644)

		if kuberoUIErr != nil {
			fmt.Println(kuberoUIErr)
			return
		}

		_, olmInstallErr := exec.Command("kubectl", "apply", "-f", "kuberoUI.yaml", "-n", "kubero").Output()
		if olmInstallErr != nil {
			_, _ = cfmt.Println("{{✗ Failed to run command to install Kubero UI. Try running this command manually: kubectl apply -f kuberoUI.yaml -n kubero}}::red")
			return
		} else {
			e := os.Remove("kuberoUI.yaml")
			if e != nil {
				log.Fatal(e)
			}
			_, _ = cfmt.Println("{{✓ Kubero UI installed}}::lightGreen")
		}

		kuberoUISpinner := spinner.New("Wait for Kubero UI to be created")
		kuberoUISpinner.Start("Wait for Kubero UI to be created")

		var kuberoWait []byte
		for len(kuberoWait) == 0 {
			// kubectl get --ignore-not-found deployment kubero
			kuberoWait, _ = exec.Command("kubectl", "get", "--ignore-not-found", "deployment", "kubero", "-n", "kubero").Output()
			kuberoUISpinner.UpdateMessage("Waiting for Kubero UI to be created")
			time.Sleep(1 * time.Second)
		}

		kuberoUISpinner.UpdateMessage("Waiting for Kubero UI to be ready")

		time.Sleep(5 * time.Second)
		// kubectl wait --for=condition=available deployment/kubero -n kubero --timeout=180s
		_, olmWaitErr := exec.Command("kubectl", "wait", "--for=condition=available", "deployment/kubero", "-n", "kubero", "--timeout=300s").Output()
		if olmWaitErr != nil {
			kuberoUISpinner.Error("Failed to wait for Kubero UI to become ready")
			log.Fatal(olmWaitErr)
		}
		kuberoUISpinner.Success("Kubero UI is ready")
	}

}

func installMonitoring() {

	if promptLine("7) Enable long-term metrics", "[y/n]", "y") == "y" {
		monitoringInstalled = true
	} else {
		monitoringInstalled = false
		return
	}

	createNamespace("kubero")

	spinnerObj := spinner.New("enable metrics")
	if promptLine("7.1) Create local Prometheus instance", "[y/n]", "y") == "y" {
		URL := "https://raw.githubusercontent.com/kubero-dev/kubero-operator/main/config/samples/application_v1alpha1_kuberoprometheus.yaml"
		_, _ = cfmt.Println("  run command : kubectl apply -n kubero -f " + URL)
		spinnerObj.Start("Installing Prometheus")
		_, ingressErr := exec.Command("kubectl", "apply", "-n", "kubero", "-f", URL).Output()
		if ingressErr != nil {
			spinnerObj.Error("Failed to run command. Try running this command manually: kubectl apply -f " + URL)
			log.Fatal(ingressErr)
		}
		/*
			spinner.UpdateMessage("Waiting for Prometheus to be ready")

			time.Sleep(5 * time.Second)
			// kubectl wait --for=condition=available deployment/kubero -n kubero --timeout=180s
			_, olmWaitErr := exec.Command("kubectl", "wait", "--for=condition=available", "deployment/kubero-prometheus-server", "-n", "kubero", "--timeout=300s").Output()
			if olmWaitErr != nil {
				spinner.Error("Failed to wait for Prometheus to become ready")
				log.Fatal(olmWaitErr)
			}
		*/
		spinnerObj.Success("Prometheus installed successfully")
	}

	if promptLine("7.2) Enable KubeMetrics", "[y/n]", "y") == "y" {
		_, _ = cfmt.Println("  run command : kubectl patch kuberoes kubero -n kubero --type=merge")
		spinnerObj.Start("Enabling Metrics")

		patch := `{
			"spec": {
				"prometheus": {
					"kube-state-metrics": {
						"enabled": true
					}
				}
			}
		}`

		_, patchResult := exec.Command("kubectl", "patch", "kuberoprometheuses", "kubero-prometheus", "-n", "kubero", "--type=merge", "-p", patch).Output()
		if patchResult != nil {
			spinnerObj.Error("Failed to patch the kubero prometheus CRD to enable kube metrics", patchResult.Error(), patch)
		}
		spinnerObj.Success("Metrics enabled successfully")

	}

	patch := `{
		"spec": {
		  "template": {
			"metadata": {
			  "annotations": {
				"prometheus.io/port": "10254",
				"prometheus.io/scrape": "true"
			  }
			},
			"spec": {
			  "containers": [
				{
				  "name": "controller",
				  "ports": [
					{
					  "containerPort": 10254,
					  "name": "prometheus",
					  "protocol": "TCP"
					}
				  ],
				  "args": [
					"/nginx-ingress-controller",
					"--election-id=ingress-nginx-leader",
					"--controller-class=k8s.io/ingress-nginx",
					"--ingress-class=nginx",
					"--configmap=$(POD_NAMESPACE)/ingress-nginx-controller",
					"--validating-webhook=:8443",
					"--validating-webhook-certificate=/usr/local/certificates/cert",
					"--validating-webhook-key=/usr/local/certificates/key",
					"--watch-ingress-without-class=true",
					"--enable-metrics=true",
					"--publish-status-address=localhost"
				  ]
				}
			  ]
			}
		  }
		}
	  }`
	_, ingressPatch := exec.Command("kubectl", "patch", "deployments.apps", "ingress-nginx-controller", "-n", "ingress-nginx", "-p", patch).Output()
	if ingressPatch != nil {
		_, _ = cfmt.Println("{{✗ Failed to patch the ingress controller. }}::red\nHere is a detailed information how to do it manually: https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/monitoring.md")
		//log.Fatal(ingressPatch)
	}

	patch = `{
		"spec": {
			"ports": [
				{
					"name": "prometheus",
					"nodePort": 31280,
					"port": 10254,
					"protocol": "TCP",
					"targetPort": "prometheus"
				}
			]
		}
	}`

	_, ingressPatch = exec.Command("kubectl", "patch", "svc", "ingress-nginx-controller", "-n", "ingress-nginx", "-p", patch).Output()
	if ingressPatch != nil {
		_, _ = cfmt.Println("{{✗ Failed to patch the ingress controller service. }}::red\nHere is a detailed information how to do it manually: https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/monitoring.md")
		//log.Fatal(ingressPatch)
	}

}

func installCertManager() {

	install := promptLine("6) Install SSL CertManager", "[y,n]", "y")
	if install != "y" {
		return
	}

	if installOlm {
		installOLMCertManager()
	} else {
		installCertManagerSlim()
	}
}

func installCertManagerSlim() {

	kuberoUIInstalled, _ := exec.Command("kubectl", "get", "crd", "certificates.cert-manager.io").Output()
	if len(kuberoUIInstalled) > 0 {
		_, _ = cfmt.Println("{{✓ CertManager already installed}}::lightGreen")
		return
	}

	certManagerSpinner := spinner.New("Install Cert Manager")
	//certManagerUrl := "https://github.com/cert-manager/cert-manager/releases/download/v1.11.0/cert-manager.yaml"
	certManagerUrl := "https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml"
	_, _ = cfmt.Println("  run command : kubectl create -f " + certManagerUrl)
	certManagerSpinner.Start("Installing Cert Manager")
	_, certManagerErr := exec.Command("kubectl", "create", "-f", certManagerUrl).Output()
	if certManagerErr != nil {
		certManagerSpinner.Error("Failed to run command. Try running this command manually: kubectl create -f " + certManagerUrl)
		log.Fatal(certManagerErr)
	}

	certManagerSpinner.UpdateMessage("Waiting for Cert Manager to be ready")
	time.Sleep(10 * time.Second)
	_, certManagerWaitErr := exec.Command("kubectl", "wait", "--for=condition=available", "deployment/cert-manager-webhook", "-n", "cert-manager", "--timeout=180s", "-n", "cert-manager").Output()
	if certManagerWaitErr != nil {
		certManagerSpinner.Error("Failed to run command. Try running it manually: kubectl wait --for=condition=available deployment/cert-manager-webhook -n cert-manager --timeout=180s -n cert-manager")
		log.Fatal(certManagerWaitErr)
	}
	certManagerSpinner.Success("Cert Manager installed")

	installCertManagerClusterIssuer("cert-manager")

}

func installCertManagerClusterIssuer(namespace string) {

	installer := resty.New()

	installer.SetBaseURL("https://raw.githubusercontent.com")
	kf, _ := installer.R().Get("kubero-dev/kubero-cli/main/templates/certManagerClusterIssuer.prod.yaml")

	var certManagerClusterIssuer CertManagerClusterIssuer
	_ = yaml.Unmarshal(kf.Body(), &certManagerClusterIssuer)

	argCertManagerContact := promptLine("6.1) Letsencrypt ACME contact email", "", "noreply@yourdomain.com")
	certManagerClusterIssuer.Spec.Acme.Email = argCertManagerContact

	clusterIssuer := promptLine("6.2) ClusterIssuer Name", "", "letsencrypt-prod")
	certManagerClusterIssuer.Metadata.Name = clusterIssuer

	certManagerClusterIssuerYaml, _ := yaml.Marshal(certManagerClusterIssuer)
	certManagerClusterIssuerYamlErr := os.WriteFile("kuberoCertManagerClusterIssuer.yaml", certManagerClusterIssuerYaml, 0644)
	if certManagerClusterIssuerYamlErr != nil {
		fmt.Println(certManagerClusterIssuerYamlErr)
		return
	}

	_, certManagerClusterIssuerErr := exec.Command("kubectl", "apply", "-f", "kuberoCertManagerClusterIssuer.yaml", "-n", namespace).Output()
	if certManagerClusterIssuerErr != nil {
		_, _ = cfmt.Println("{{✗ Failed to create CertManager ClusterIssuer. Try running this command manually: kubectl apply -f kuberoCertManagerClusterIssuer.yaml -n cert-manager}}::red")
		return
	} else {
		e := os.Remove("kuberoCertManagerClusterIssuer.yaml")
		if e != nil {
			log.Fatal(e)
		}
		_, _ = cfmt.Println("{{✓ Cert Manager Cluster Issuer created}}::lightGreen")
	}
}

func installOLMCertManager() {

	certManagerInstalled, _ := exec.Command("kubectl", "get", "deployment", "cert-manager-webhook", "-n", "operators").Output()
	if len(certManagerInstalled) > 0 {
		_, _ = cfmt.Println("{{✓ Cert Manager already installed}}::lightGreen")
		return
	}

	certManagerSpinner := spinner.New("Install Cert Manager")
	_, _ = cfmt.Println("  run command : kubectl create -f https://operatorhub.io/install/cert-manager.yaml")
	certManagerSpinner.Start("Installing Cert Manager")
	_, certManagerErr := exec.Command("kubectl", "create", "-f", "https://operatorhub.io/install/cert-manager.yaml").Output()
	if certManagerErr != nil {
		certManagerSpinner.Error("Failed to run command. Try running this command manually: kubectl create -f https://operatorhub.io/install/cert-manager.yaml")
		log.Fatal(certManagerErr)
	}
	certManagerSpinner.Success("Cert Manager installed")

	certManagerSpinner = spinner.New("Wait for Cert Manager to be ready")
	certManagerSpinner.Start("installing Cert Manager")

	_, _ = cfmt.Println("\r  run command : kubectl wait --for=condition=available deployment/cert-manager-webhook -n cert-manager --timeout=180s -n operators")
	_, _ = cfmt.Println("\r  This might take a while. Time enough for a joke:")
	for i := 0; i < 4; i++ {
		tellAChucknorrisJoke()
		time.Sleep(15 * time.Second)
	}
	_, certManagerWaitErr := exec.Command("kubectl", "wait", "--for=condition=available", "deployment/cert-manager-webhook", "-n", "cert-manager", "--timeout=180s", "-n", "operators").Output()
	if certManagerWaitErr != nil {
		certManagerSpinner.Error("Failed to run command. Try running it manually: kubectl wait --for=condition=available deployment/cert-manager-webhook -n cert-manager --timeout=180s -n operators")
		log.Fatal(certManagerWaitErr)
	}
	certManagerSpinner.Success("Cert Manager is ready")

	installCertManagerClusterIssuer("default")
}

func writeCLIConfig() {

	ingressInstall := promptLine("10) Write the Kubero CLI config", "[y,n]", "n")
	if ingressInstall != "y" {
		return
	}

	//TODO consider using SSL here.
	url := promptLine("Kubero Host address", "", "http://"+argDomain+":"+argPort)
	viper.Set("api.url", url)

	token := promptLine("Kubero Token", "", argApiToken)
	viper.Set("api.token", token)

	var config Config
	if err := viper.Unmarshal(&config); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("%+v\n", config)

	_ = viper.WriteConfig()
}

func printDNSinfo() {

	ingressInstalled, err := exec.Command("kubectl", "get", "ingress", "-n", "kubero", "-o", "json").Output()
	if err != nil {
		_, _ = cfmt.Println("{{✗ Failed to fetch DNS information}}::red")
		return
	}
	var kuberoIngress KuberoIngress
	_ = json.Unmarshal(ingressInstalled, &kuberoIngress)

	_, _ = cfmt.Println("{{⚠ make sure your DNS is pointing to your Kubernetes cluster}}::yellow")

	//TODO this should be replaces by the default review app domain
	if len(kuberoIngress.Items) > 0 &&
		len(kuberoIngress.Items[0].Spec.Rules[0].Host) > 0 &&
		len(kuberoIngress.Items[0].Status.LoadBalancer.Ingress) > 0 &&
		len(kuberoIngress.Items[0].Status.LoadBalancer.Ingress[0].IP) > 0 {
		_, _ = cfmt.Printf("{{  %s.		IN		A		%s}}::lightBlue\n", kuberoIngress.Items[0].Spec.Rules[0].Host, kuberoIngress.Items[0].Status.LoadBalancer.Ingress[0].IP)
		_, _ = cfmt.Printf("{{  *.review.example.com.			IN		A		%s}}::lightBlue", kuberoIngress.Items[0].Status.LoadBalancer.Ingress[0].IP)
	}

}

func finalMessage() {
	_, _ = cfmt.Println(`

    ,--. ,--.        ,--.
    |  .'   /,--.,--.|  |-.  ,---. ,--.--. ,---.
    |  .   ' |  ||  || .-. '| .-. :|  .--'| .-. |
    |  |\   \'  ''  '| '-' |\   --.|  |   ' '-' '
    '--' '--' '----'  '---'  '----''--'    '---'

    Documentation:
    https://docs.kubero.dev
    `)

	protocol := "https"
	if argPort == "80" {
		protocol = "http"
	}
	_, _ = cfmt.Println(`
    Your Kubero UI :{{
    URL : ` + protocol + "://" + argDomain + ":" + argPort + `
    User: ` + argAdminUser + `
    Pass: ` + argAdminPassword + `}}::lightBlue
	`)

	_, _ = cfmt.Println("\n\n    {{Done - you can now login to your Kubero UI}}::lightGreen\n\n ")

}

func generateRandomString(length int, chars string) string {
	if chars == "" {
		chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!+?._-%"
	}
	var letterRunes = []rune(chars)

	b := make([]rune, length)
	rand.New(rand.NewSource(time.Now().UnixNano()))

	for i := range b {
		b[i] = letterRunes[rand.Intn(len(letterRunes))]
	}
	return string(b)
}

func getAvailableStorageClasses() []string {
	var storageClasses []string
	storageClassesRaw, _ := exec.Command("kubectl", "get", "storageClasses", "-o", "json").Output()
	var storageClassesList StorageClassesList
	_ = json.Unmarshal(storageClassesRaw, &storageClassesList)
	for _, storageClass := range storageClassesList.Items {
		storageClasses = append(storageClasses, storageClass.Metadata.Name)
	}
	return storageClasses
}

func getGithubVersionList(repository string, limit int) []string {

	githubapi := resty.New().
		SetHeader("Accept", "application/json").
		SetHeader("Content-Type", "application/json").
		SetHeader("User-Agent", "kubero-cli/"+kuberoCliVersion).
		SetBaseURL("https://api.github.com/repos/")

	tags, _ := githubapi.R().Get(repository + "/tags")
	var versions []GithubVersion
	_ = json.Unmarshal(tags.Body(), &versions)

	var versionList []string
	versionList = make([]string, 0)

	for _, version := range versions {
		if limit == 0 || len(versionList) < limit {
			versionList = append(versionList, version.Name)
		}
	}

	return versionList
}

type StorageClassesList struct {
	APIVersion string `json:"apiVersion"`
	Items      []struct {
		APIVersion string `json:"apiVersion"`
		Kind       string `json:"kind"`
		Metadata   struct {
			Annotations struct {
				KubectlKubernetesIoLastAppliedConfiguration string `json:"kubectl.kubernetes.io/last-applied-configuration"`
				StorageClassKubernetesIoIsDefaultClass      string `json:"storageClass.kubernetes.io/is-default-class"`
			} `json:"annotations"`
			CreationTimestamp time.Time `json:"creationTimestamp"`
			Name              string    `json:"name"`
			ResourceVersion   string    `json:"resourceVersion"`
			UID               string    `json:"uid"`
		} `json:"metadata"`
		Provisioner       string `json:"provisioner"`
		ReclaimPolicy     string `json:"reclaimPolicy"`
		VolumeBindingMode string `json:"volumeBindingMode"`
	} `json:"items"`
	Kind     string `json:"kind"`
	Metadata struct {
		ResourceVersion string `json:"resourceVersion"`
		SelfLink        string `json:"selfLink"`
	} `json:"metadata"`
}
0707010000001C000081A400000000000000000000000167BEEE0500006222000000000000000000000000000000000000003000000000kubero-cli-2.4.7/cmd/kuberoCli/install.types.gopackage kuberoCli

import (
	"time"

	"gorm.io/gorm"
)

type DigitalOceanKubernetesConfig struct {
	Name      string `json:"name" gorm:"column:name"`
	Region    string `json:"region" gorm:"column:region"`
	Version   string `json:"version" gorm:"column:version"`
	NodePools []struct {
		Size  string `json:"size" gorm:"column:size"`
		Count int    `json:"count" gorm:"column:count"`
		Name  string `json:"name" gorm:"column:name"`
	} `json:"node_pools" gorm:"embedded"`
}

type DigitalOcean struct {
	KubernetesCluster struct {
		ID            string   `json:"id" gorm:"column:id"`
		Name          string   `json:"name" gorm:"column:name"`
		Region        string   `json:"region" gorm:"column:region"`
		Version       string   `json:"version" gorm:"column:version"`
		ClusterSubnet string   `json:"cluster_subnet" gorm:"column:cluster_subnet"`
		ServiceSubnet string   `json:"service_subnet" gorm:"column:service_subnet"`
		VpcUUID       string   `json:"vpc_uuid" gorm:"column:vpc_uuid"`
		Ipv4          string   `json:"ipv4" gorm:"column:ipv4"`
		Endpoint      string   `json:"endpoint" gorm:"column:endpoint"`
		Tags          []string `json:"tags" gorm:"column:tags"`
		NodePools     []struct {
			ID        string        `json:"id" gorm:"column:id"`
			Name      string        `json:"name" gorm:"column:name"`
			Size      string        `json:"size" gorm:"column:size"`
			Count     int           `json:"count" gorm:"column:count"`
			Tags      []string      `json:"tags" gorm:"column:tags"`
			Labels    interface{}   `json:"labels" gorm:"column:labels"`
			Taints    []interface{} `json:"taints" gorm:"column:taints"`
			AutoScale bool          `json:"auto_scale" gorm:"column:auto_scale"`
			MinNodes  int           `json:"min_nodes" gorm:"column:min_nodes"`
			MaxNodes  int           `json:"max_nodes" gorm:"column:max_nodes"`
			Nodes     []struct {
				ID     string `json:"id" gorm:"column:id"`
				Name   string `json:"name" gorm:"column:name"`
				Status struct {
					State string `json:"state" gorm:"column:state"`
				} `json:"status" gorm:"embedded"`
				DropletID string    `json:"droplet_id" gorm:"column:droplet_id"`
				CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
				UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
			} `json:"nodes" gorm:"embedded"`
		} `json:"node_pools" gorm:"embedded"`
		MaintenancePolicy struct {
			StartTime string `json:"start_time" gorm:"column:start_time"`
			Duration  string `json:"duration" gorm:"column:duration"`
			Day       string `json:"day" gorm:"column:day"`
		} `json:"maintenance_policy" gorm:"embedded"`
		AutoUpgrade bool `json:"auto_upgrade" gorm:"column:auto_upgrade"`
		Status      struct {
			State   string `json:"state" gorm:"column:state"`
			Message string `json:"message" gorm:"column:message"`
		} `json:"status" gorm:"embedded"`
		CreatedAt         time.Time `json:"created_at" gorm:"column:created_at"`
		UpdatedAt         time.Time `json:"updated_at" gorm:"column:updated_at"`
		SurgeUpgrade      bool      `json:"surge_upgrade" gorm:"column:surge_upgrade"`
		RegistryEnabled   bool      `json:"registry_enabled" gorm:"column:registry_enabled"`
		Ha                bool      `json:"ha" gorm:"column:ha"`
		SupportedFeatures []string  `json:"supported_features" gorm:"column:supported_features"`
	} `json:"kubernetes_cluster" gorm:"embedded"`
}

type DigitaloceanOptions struct {
	Options struct {
		Regions []struct {
			Name string `json:"name" gorm:"column:name"`
			Slug string `json:"slug" gorm:"column:slug"`
		} `json:"regions" gorm:"embedded"`
		Versions []struct {
			Slug              string   `json:"slug" gorm:"column:slug"`
			KubernetesVersion string   `json:"kubernetes_version" gorm:"column:kubernetes_version"`
			SupportedFeatures []string `json:"supported_features" gorm:"column:supported_features"`
		} `json:"versions" gorm:"embedded"`
		Sizes []struct {
			Name string `json:"name" gorm:"column:name"`
			Slug string `json:"slug" gorm:"column:slug"`
		} `json:"sizes" gorm:"embedded"`
	} `json:"options" gorm:"embedded"`
}

type User struct {
	gorm.Model
	Method   string `json:"method" gorm:"column:method"`
	Username string `json:"username" gorm:"column:username"`
	Password string `json:"password" gorm:"column:password"`
	Insecure bool   `json:"insecure" gorm:"column:insecure"`
	ApiToken string `json:"apiToken,omitempty" gorm:"column:apiToken"`
}

type KindConfig struct {
	Kind       string `yaml:"kind" gorm:"column:kind"`
	APIVersion string `yaml:"apiVersion" gorm:"column:apiVersion"`
	Name       string `yaml:"name" gorm:"column:name"`
	Networking struct {
		IPFamily         string `yaml:"ipFamily" gorm:"column:ipFamily"`
		APIServerAddress string `yaml:"apiServerAddress" gorm:"column:apiServerAddress"`
	} `yaml:"networking" gorm:"embedded"`
	Nodes []struct {
		Role                 string   `yaml:"role" gorm:"column:role"`
		Image                string   `yaml:"image,omitempty" gorm:"column:image"`
		KubeadmConfigPatches []string `yaml:"kubeadmConfigPatches" gorm:"column:kubeadmConfigPatches"`
		ExtraPortMappings    []struct {
			ContainerPort int    `yaml:"containerPort" gorm:"column:containerPort"`
			HostPort      int    `yaml:"hostPort" gorm:"column:hostPort"`
			Protocol      string `yaml:"protocol" gorm:"column:protocol"`
		} `yaml:"extraPortMappings" gorm:"embedded"`
	} `yaml:"nodes" gorm:"embedded"`
	ContainerdConfigPatches []string `yaml:"containerdConfigPatches" gorm:"column:containerdConfigPatches"`
}

type KuberoUIConfig struct {
	APIVersion string `yaml:"apiVersion" gorm:"column:apiVersion"`
	Kind       string `yaml:"kind" gorm:"column:kind"`
	Metadata   struct {
		Name string `yaml:"name" gorm:"column:name"`
	} `yaml:"metadata" gorm:"embedded"`
	Spec struct {
		FullnameOverride string `yaml:"fullnameOverride" gorm:"column:fullnameOverride"`
		Image            struct {
			PullPolicy string `yaml:"pullPolicy" gorm:"column:pullPolicy"`
			Repository string `yaml:"repository" gorm:"column:repository"`
			Tag        string `yaml:"tag" gorm:"column:tag"`
		} `yaml:"image" gorm:"embedded"`
		ImagePullSecrets []interface{} `yaml:"imagePullSecrets" gorm:"-"`
		Ingress          struct {
			Annotations struct {
				KubernetesIoIngressClass string `yaml:"cert-manager.io/cluster-issuer,omitempty" gorm:"column:cert_manager_io_cluster_issuer"`
				KubernetesIoTlsAcme      string `yaml:"kubernetes.io/tls-acme,omitempty" gorm:"column:kubernetes_io_tls_acme"`
			} `yaml:"annotations" gorm:"embedded"`
			ClassName string `yaml:"className" gorm:"column:className"`
			Enabled   bool   `yaml:"enabled" gorm:"column:enabled"`
			Hosts     []struct {
				Host  string `yaml:"host" gorm:"column:host"`
				Paths []struct {
					Path     string `yaml:"path" gorm:"column:path"`
					PathType string `yaml:"pathType" gorm:"column:pathType"`
				} `yaml:"paths" gorm:"embedded"`
			} `yaml:"hosts" gorm:"embedded"`
			TLS []KuberoUITls `yaml:"tls" gorm:"-"`
		} `yaml:"ingress" gorm:"embedded"`
		NameOverride string `yaml:"nameOverride" gorm:"column:nameOverride"`
		NodeSelector struct {
		} `yaml:"nodeSelector" gorm:"-"`
		PodAnnotations struct {
		} `yaml:"podAnnotations" gorm:"-"`
		PodSecurityContext struct {
		} `yaml:"podSecurityContext" gorm:"-"`
		Prometheus struct {
			Enabled  bool   `yaml:"enabled" gorm:"column:enabled"`
			Endpoint string `yaml:"endpoint" gorm:"column:endpoint"`
		} `yaml:"prometheus,omitempty" gorm:"embedded"`
		Registry struct {
			Enabled bool   `yaml:"enabled" gorm:"column:enabled"`
			Create  bool   `yaml:"create" gorm:"column:create"`
			Host    string `yaml:"host" gorm:"column:host"`
			SubPath string `yaml:"subPath" gorm:"column:subPath"`
			Account struct {
				Username string `yaml:"username" gorm:"column:username"`
				Password string `yaml:"password" gorm:"column:password"`
				Hash     string `yaml:"hash" gorm:"column:hash"`
			} `yaml:"account" gorm:"embedded"`
			Port             int         `yaml:"port" gorm:"column:port"`
			Storage          string      `yaml:"storage" gorm:"column:storage"`
			StorageClassName interface{} `yaml:"storageClassName" gorm:"column:storageClassName"`
		} `yaml:"registry" gorm:"embedded"`
		ReplicaCount int `yaml:"replicaCount" gorm:"column:replicaCount"`
		Resources    struct {
		} `yaml:"resources" gorm:"-"`
		SecurityContext struct {
		} `yaml:"securityContext" gorm:"-"`
		Service struct {
			Port int    `yaml:"port" gorm:"column:port"`
			Type string `yaml:"type" gorm:"column:type"`
		} `yaml:"service" gorm:"embedded"`
		ServiceAccount struct {
			Annotations struct {
			} `yaml:"annotations" gorm:"-"`
			Create bool   `yaml:"create" gorm:"column:create"`
			Name   string `yaml:"name" gorm:"column:name"`
		} `yaml:"serviceAccount" gorm:"embedded"`
		Tolerations []interface{} `yaml:"tolerations" gorm:"-"`
		Kubero      struct {
			Debug      string `yaml:"debug" gorm:"column:debug"`
			Namespace  string `yaml:"namespace" gorm:"column:namespace"`
			Context    string `yaml:"context" gorm:"column:context"`
			WebhookURL string `yaml:"webhook_url" gorm:"column:webhook_url"`
			SessionKey string `yaml:"sessionKey" gorm:"column:sessionKey"`
			Auth       struct {
				Github struct {
					Enabled     bool   `yaml:"enabled" gorm:"column:enabled"`
					ID          string `yaml:"id,omitempty" gorm:"column:id"`
					Secret      string `yaml:"secret,omitempty" gorm:"column:secret"`
					CallbackURL string `yaml:"callbackUrl,omitempty" gorm:"column:callbackUrl"`
					Org         string `yaml:"org,omitempty" gorm:"column:org"`
				} `yaml:"github" gorm:"embedded"`
				Oauth2 struct {
					Enabled     bool   `yaml:"enabled" gorm:"column:enabled"`
					Name        string `yaml:"name,omitempty" gorm:"column:name"`
					ID          string `yaml:"id,omitempty" gorm:"column:id"`
					AuthURL     string `yaml:"authUrl,omitempty" gorm:"column:authUrl"`
					TokenURL    string `yaml:"tokenUrl,omitempty" gorm:"column:tokenUrl"`
					Secret      string `yaml:"secret,omitempty" gorm:"column:secret"`
					CallbackURL string `yaml:"callbackUrl,omitempty" gorm:"column:callbackUrl"`
				} `yaml:"oauth2" gorm:"embedded"`
			} `yaml:"auth" gorm:"embedded"`
			AuditLogs struct {
				Enabled          bool     `yaml:"enabled" gorm:"column:enabled"`
				StorageClassName string   `yaml:"storageClassName" gorm:"column:storageClassName"`
				Size             string   `yaml:"size" gorm:"column:size"`
				AccessModes      []string `yaml:"accessModes" gorm:"column:accessModes"`
				Limit            string   `yaml:"limit" gorm:"column:limit"`
			} `yaml:"auditLogs" gorm:"embedded"`
			Config KuberoConfigfile `yaml:"config,omitempty" gorm:"embedded"`
		} `yaml:"kubero" gorm:"embedded"`
	} `yaml:"spec" gorm:"embedded"`
}

type NotificationsConfig struct {
	Enabled   bool     `yaml:"enabled" gorm:"column:enabled"`
	Name      string   `yaml:"name" gorm:"column:name"`
	Type      string   `yaml:"type" gorm:"column:type"`
	Pipelines []string `yaml:"pipelines" gorm:"column:pipelines"`
	Events    []string `yaml:"events" gorm:"column:events"`
	Config    struct {
		Url     string `yaml:"url" gorm:"column:url"`
		Channel string `yaml:"channel,omitempty" gorm:"column:channel"`
		Secret  string `yaml:"secret,omitempty" gorm:"column:secret"`
	} `yaml:"config" gorm:"embedded"`
}

type KuberoConfigfile struct {
	Kubero struct {
		Readonly bool `yaml:"readonly" gorm:"column:readonly"`
		Admin    struct {
			Disabled bool `yaml:"disabled" gorm:"column:enabled"`
		} `yaml:"admin" gorm:"embedded"`
		Console struct {
			Enabled bool `yaml:"enabled" gorm:"column:enabled"`
		} `yaml:"console" gorm:"embedded"`
		Banner struct {
			Show      bool   `yaml:"show" gorm:"column:show"`
			Message   string `yaml:"message" gorm:"column:message"`
			BgColor   string `yaml:"bgColor" gorm:"column:bgColor"`
			Fontcolor string `yaml:"fontcolor" gorm:"column:fontcolor"`
		} `yaml:"banner" gorm:"embedded"`
	} `yaml:"kubero" gorm:"embedded"`
	Notifications []NotificationsConfig `yaml:"notifications" gorm:"embedded"`
	ClusterIssuer string                `yaml:"clusterIssuer" gorm:"column:clusterIssuer"`
	Templates     struct {
		Enabled  bool `yaml:"enabled" gorm:"column:enabled"`
		Catalogs []struct {
			Name             string `yaml:"name" gorm:"column:name"`
			Description      string `yaml:"description" gorm:"column:description"`
			TemplateBasePath string `yaml:"templateBasePath" gorm:"column:templateBasePath"`
			Index            struct {
				URL    string `yaml:"url" gorm:"column:url"`
				Format string `yaml:"format" gorm:"column:format"`
			} `yaml:"index" gorm:"embedded"`
		} `yaml:"catalogs" gorm:"embedded"`
	} `yaml:"templates" gorm:"embedded"`
	BuildPacks []struct {
		Name     string `yaml:"name" gorm:"column:name"`
		Language string `yaml:"language" gorm:"column:language"`
		Fetch    struct {
			Repository      string `yaml:"repository" gorm:"column:repository"`
			Tag             string `yaml:"tag" gorm:"column:tag"`
			SecurityContext struct {
				RunAsUser int `yaml:"runAsUser" gorm:"column:runAsUser"`
			} `yaml:"securityContext" gorm:"embedded"`
		} `yaml:"fetch" gorm:"embedded"`
		Build struct {
			Repository      string `yaml:"repository" gorm:"column:repository"`
			Tag             string `yaml:"tag" gorm:"column:tag"`
			Command         string `yaml:"command" gorm:"column:command"`
			SecurityContext struct {
				RunAsUser int `yaml:"runAsUser" gorm:"column:runAsUser"`
			} `yaml:"securityContext" gorm:"embedded"`
		} `yaml:"build,omitempty" gorm:"embedded"`
		Run struct {
			Repository         string `yaml:"repository" gorm:"column:repository"`
			Tag                string `yaml:"tag" gorm:"column:tag"`
			ReadOnlyAppStorage bool   `yaml:"readOnlyAppStorage" gorm:"column:readOnlyAppStorage"`
			SecurityContext    struct {
				AllowPrivilegeEscalation bool `yaml:"allowPrivilegeEscalation" gorm:"column:allowPrivilegeEscalation"`
				ReadOnlyRootFilesystem   bool `yaml:"readOnlyRootFilesystem" gorm:"column:readOnlyRootFilesystem"`
			} `yaml:"securityContext" gorm:"embedded"`
			Command string `yaml:"command" gorm:"column:command"`
		} `yaml:"run,omitempty" gorm:"embedded"`
	} `yaml:"buildPacks" gorm:"embedded"`
	PodSizeList []struct {
		Active      bool   `yaml:"active,omitempty" gorm:"column:active"`
		Name        string `yaml:"name" gorm:"column:name"`
		Description string `yaml:"description" gorm:"column:description"`
		Default     bool   `yaml:"default,omitempty" gorm:"column:default"`
		Resources   struct {
			Requests struct {
				Memory string `yaml:"memory" gorm:"column:memory"`
				CPU    string `yaml:"cpu" gorm:"column:cpu"`
			} `yaml:"requests" gorm:"embedded"`
			Limits struct {
				Memory string `yaml:"memory" gorm:"column:memory"`
				CPU    string `yaml:"cpu" gorm:"column:cpu"`
			} `yaml:"limits" gorm:"embedded"`
		} `yaml:"resources,omitempty" gorm:"embedded"`
	} `yaml:"podSizeList" gorm:"embedded"`
}

type KuberoUITls struct {
	SecretName string   `yaml:"secretName" gorm:"column:secretName"`
	Hosts      []string `yaml:"hosts" gorm:"column:hosts"`
}

// ScalewayCreate https://developers.scaleway.com/en/products/k8s/api/#post-612200
type ScalewayCreate struct {
	OrganizationID string             `json:"organization_id,omitempty" gorm:"column:organization_id"`
	ProjectID      string             `json:"project_id,omitempty" gorm:"column:project_id"`
	Type           string             `json:"type" gorm:"column:type"`
	Name           string             `json:"name" gorm:"column:name"`
	Description    string             `json:"description" gorm:"column:description"`
	Tags           []string           `json:"tags" gorm:"column:tags"`
	Version        string             `json:"version" gorm:"column:version"`
	Cni            string             `json:"cni" gorm:"column:cni"`
	Pools          []ScalewayNodePool `json:"pools" gorm:"embedded"`
	AutoUpgrade    struct {
		Enable            bool `json:"enable" gorm:"column:enable"`
		MaintenanceWindow struct {
			StartHour int    `json:"start_hour" gorm:"column:start_hour"`
			Day       string `json:"day" gorm:"column:day"`
		} `json:"maintenance_window" gorm:"embedded"`
	} `json:"auto_upgrade" gorm:"embedded"`
	FeatureGates      []string `json:"feature_gates" gorm:"column:feature_gates"`
	AdmissionPlugins  []string `json:"admission_plugins" gorm:"column:admission_plugins"`
	ApiServerCertSans []string `json:"apiServer_cert_sans" gorm:"column:apiServer_cert_sans"`
	Ingress           string   `json:"ingress" gorm:"column:ingress"`
}

type ScalewayNodePool struct {
	Name             string   `json:"name" gorm:"column:name"`
	NodeType         string   `json:"node_type" gorm:"column:node_type"`
	PlacementGroupID string   `json:"placement_group_id,omitempty" gorm:"column:placement_group_id"`
	Autoscaling      bool     `json:"autoscaling" gorm:"column:autoscaling"`
	Size             int      `json:"size" gorm:"column:size"`
	MinSize          int      `json:"min_size" gorm:"column:min_size"`
	MaxSize          int      `json:"max_size" gorm:"column:max_size"`
	ContainerRuntime string   `json:"container_runtime" gorm:"column:container_runtime"`
	AutoHealing      bool     `json:"autoHealing" gorm:"column:autoHealing"`
	Tags             []string `json:"tags" gorm:"column:tags"`
	Zone             string   `json:"zone" gorm:"column:zone"`
	RootVolumeType   string   `json:"root_volume_type" gorm:"column:root_volume_type"`
	RootVolumeSize   int      `json:"root_volume_size" gorm:"column:root_volume_size"`
}

type ScalewayVersionsResponse struct {
	Versions []struct {
		Name                       string   `json:"name" gorm:"column:name"`
		Label                      string   `json:"label" gorm:"column:label"`
		Region                     string   `json:"region" gorm:"column:region"`
		AvailableCnis              []string `json:"available_cnis" gorm:"column:available_cnis"`
		AvailableIngresses         []string `json:"available_ingresses" gorm:"column:available_ingresses"`
		AvailableContainerRuntimes []string `json:"available_container_runtimes" gorm:"column:available_container_runtimes"`
		AvailableFeatureGates      []string `json:"available_feature_gates" gorm:"column:available_feature_gates"`
		AvailableAdmissionPlugins  []string `json:"available_admission_plugins" gorm:"column:available_admission_plugins"`
		AvailableKubeletArgs       struct {
			AvailableKubeletArgKey string `json:"<available_kubelet_argKey>" gorm:"column:available_kubelet_arg_key"`
		} `json:"available_kubelet_args" gorm:"embedded"`
	} `json:"versions" gorm:"embedded"`
}

type ScalewayCreateResponse struct {
	ID               string    `json:"id" gorm:"column:id"`
	Type             string    `json:"type" gorm:"column:type"`
	Name             string    `json:"name" gorm:"column:name"`
	Status           string    `json:"status" gorm:"column:status"`
	Version          string    `json:"version" gorm:"column:version"`
	Region           string    `json:"region" gorm:"column:region"`
	OrganizationID   string    `json:"organization_id" gorm:"column:organization_id"`
	ProjectID        string    `json:"project_id" gorm:"column:project_id"`
	Tags             []string  `json:"tags" gorm:"column:tags"`
	Cni              string    `json:"cni" gorm:"column:cni"`
	Description      string    `json:"description" gorm:"column:description"`
	ClusterURL       string    `json:"cluster_url" gorm:"column:cluster_url"`
	DNSWildcard      string    `json:"dns_wildcard" gorm:"column:dns_wildcard"`
	CreatedAt        time.Time `json:"created_at" gorm:"column:created_at"`
	UpdatedAt        time.Time `json:"updated_at" gorm:"column:updated_at"`
	AutoscalerConfig struct {
		ScaleDownDisabled             bool    `json:"scale_down_disabled" gorm:"column:scale_down_disabled"`
		ScaleDownDelayAfterAdd        string  `json:"scale_down_delay_after_add" gorm:"column:scale_down_delay_after_add"`
		Estimator                     string  `json:"estimator" gorm:"column:estimator"`
		Expander                      string  `json:"expander" gorm:"column:expander"`
		IgnoreDaemonSetsUtilization   bool    `json:"ignore_daemonSets_utilization" gorm:"column:ignore_daemonSets_utilization"`
		BalanceSimilarNodeGroups      bool    `json:"balance_similar_node_groups" gorm:"column:balance_similar_node_groups"`
		ExpendablePodsPriorityCutoff  int     `json:"expendable_pods_priority_cutoff" gorm:"column:expendable_pods_priority_cutoff"`
		ScaleDownUnneededTime         int     `json:"scale_down_unneeded_time" gorm:"column:scale_down_unneeded_time"`
		ScaleDownUtilizationThreshold float32 `json:"scale_down_utilization_threshold" gorm:"column:scale_down_utilization_threshold"`
		MaxGracefulTerminationSec     int     `json:"max_graceful_termination_sec" gorm:"column:max_graceful_termination_sec"`
	} `json:"autoscaler_config" gorm:"embedded"`
	DashboardEnabled bool   `json:"dashboard_enabled" gorm:"column:dashboard_enabled"`
	Ingress          string `json:"ingress" gorm:"column:ingress"`
	AutoUpgrade      struct {
		Enabled           bool `json:"enabled" gorm:"column:enabled"`
		MaintenanceWindow struct {
			StartHour int    `json:"start_hour" gorm:"column:start_hour"`
			Day       string `json:"day" gorm:"column:day"`
		} `json:"maintenance_window" gorm:"embedded"`
	} `json:"auto_upgrade" gorm:"embedded"`
	UpgradeAvailable    string   `json:"upgrade_available" gorm:"column:upgrade_available"`
	FeatureGates        []string `json:"feature_gates" gorm:"column:feature_gates"`
	AdmissionPlugins    []string `json:"admission_plugins" gorm:"column:admission_plugins"`
	OpenIDConnectConfig struct {
		IssuerURL      string   `json:"issuer_url" gorm:"column:issuer_url"`
		ClientID       string   `json:"client_id" gorm:"column:client_id"`
		UsernameClaim  string   `json:"username_claim" gorm:"column:username_claim"`
		UsernamePrefix string   `json:"username_prefix" gorm:"column:username_prefix"`
		GroupsClaim    []string `json:"groups_claim" gorm:"column:groups_claim"`
		GroupsPrefix   string   `json:"groups_prefix" gorm:"column:groups_prefix"`
		RequiredClaim  []string `json:"required_claim" gorm:"column:required_claim"`
	} `json:"open_id_connect_config" gorm:"embedded"`
	ApiServerCertSans []string `json:"apiServer_cert_sans" gorm:"column:apiServer_cert_sans"`
}

type ScalewayKubeconfigResponse struct {
	Name        string `json:"name" gorm:"column:name"`
	ContentType string `json:"content_type" gorm:"column:content_type"`
	Content     string `json:"content" gorm:"column:content"`
}

type JokeResponse struct {
	Categories []string `json:"categories"`
	CreatedAt  string   `json:"created_at"`
	IconURL    string   `json:"icon_url"`
	ID         string   `json:"id"`
	UpdatedAt  string   `json:"updated_at"`
	URL        string   `json:"url"`
	Value      string   `json:"value"`
}

type KuberoIngress struct {
	Items []struct {
		Kind string `json:"kind"`
		Spec struct {
			IngressClassName string `json:"ingressClassName"`
			Rules            []struct {
				Host string `json:"host"`
				HTTP struct {
					Paths []struct {
						Backend struct {
							Service struct {
								Name string `json:"name"`
								Port struct {
									Number int `json:"number"`
								} `json:"port"`
							} `json:"service"`
						} `json:"backend"`
						Path string `json:"path"`
					} `json:"paths"`
				} `json:"http"`
			} `json:"rules"`
		} `json:"spec"`
		Status struct {
			LoadBalancer struct {
				Ingress []struct {
					Hostname string `json:"hostname"`
					IP       string `json:"ip"`
				} `json:"ingress"`
			} `json:"loadBalancer"`
		} `json:"status"`
	} `json:"items"`
}

type LinodeCreateClusterRequest struct {
	Label        string   `json:"label"`
	Region       string   `json:"region"`
	K8SVersion   string   `json:"k8s_version"`
	Tags         []string `json:"tags"`
	ControlPlane struct {
		HighAvailability bool `json:"high_availability"`
	} `json:"control_plane"`
	NodePools []LinodeNodepool `json:"node_pools"`
}

type LinodeCreateClusterResponse struct {
	ControlPlane struct {
		HighAvailability bool `json:"high_availability"`
	} `json:"control_plane"`
	Created    time.Time `json:"created"`
	ID         int       `json:"id"`
	K8SVersion string    `json:"k8s_version"`
	Label      string    `json:"label"`
	Region     string    `json:"region"`
	Tags       []string  `json:"tags"`
	Updated    time.Time `json:"updated"`
}

type LinodeNodepool struct {
	Type       string `json:"type"`
	Count      int    `json:"count"`
	Autoscaler struct {
		Enabled bool `json:"enabled"`
		Max     int  `json:"max,omitempty"`
		Min     int  `json:"min,omitempty"`
	} `json:"autoscaler,omitempty"`
}

type CertManagerClusterIssuer struct {
	APIVersion string `yaml:"apiVersion"`
	Kind       string `yaml:"kind"`
	Metadata   struct {
		Name string `yaml:"name"`
	} `yaml:"metadata"`
	Spec struct {
		Acme struct {
			Server              string `yaml:"server"`
			Email               string `yaml:"email"`
			PrivateKeySecretRef struct {
				Name string `yaml:"name"`
			} `yaml:"privateKeySecretRef"`
			Solvers []struct {
				HTTP01 struct {
					Ingress struct {
						Class string `yaml:"class"`
					} `yaml:"ingress"`
				} `yaml:"http01"`
			} `yaml:"solvers"`
		} `yaml:"acme"`
	} `yaml:"spec"`
}
0707010000001D000081A400000000000000000000000167BEEE0500001156000000000000000000000000000000000000003700000000kubero-cli-2.4.7/cmd/kuberoCli/install_digitalocean.gopackage kuberoCli

import (
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"os"
	"strconv"
	"time"

	"github.com/go-resty/resty/v2"
	"github.com/i582/cfmt/cmd/cfmt"
	"github.com/leaanthony/spinner"
)

func installDigitalOcean() {
	// https://docs.digitalocean.com/reference/api/api-reference/#operation/kubernetes_create_cluster

	_, _ = cfmt.Println("{{⚠ Installing Kubernetes on Digital Ocean is currently beta state in kubero-cli}}::yellow")
	_, _ = cfmt.Println("{{  Please report if you run into errors}}::yellow")

	token := os.Getenv("DIGITALOCEAN_ACCESS_TOKEN")
	if token == "" {
		_, _ = cfmt.Println("{{✗ DIGITALOCEAN_ACCESS_TOKEN is not set}}::red")
		log.Fatal("missing DIGITALOCEAN_ACCESS_TOKEN")
	}

	doApi := resty.New().
		SetAuthScheme("Bearer").
		SetAuthToken(token).
		SetHeader("Accept", "application/json").
		SetHeader("Content-Type", "application/json").
		SetHeader("User-Agent", "kubero-cli/"+kuberoCliVersion).
		SetBaseURL("https://api.digitalocean.com")

	var doConfig DigitalOceanKubernetesConfig
	doConfig.NodePools = []struct {
		Size  string `json:"size" gorm:"column:size"`
		Count int    `json:"count" gorm:"column:count"`
		Name  string `json:"name" gorm:"column:name"`
	}([]struct {
		Size  string `json:"size"`
		Count int    `json:"count"`
		Name  string `json:"name"`
	}{
		{
			Size:  "s-1vcpu-2gb",
			Count: 1,
			Name:  "worker-pool",
		},
	})

	kubernetesVersions, regions, sizes := getDigitaloceanOptions(doApi)

	doConfig.Name = promptLine("Kubernetes Cluster Name", "", "kubero-"+strconv.Itoa(rand.Intn(1000)))
	doConfig.Region = selectFromList("Cluster Region", regions, "")
	doConfig.Version = selectFromList("Cluster Version", kubernetesVersions, "")

	doConfig.NodePools[0].Size = selectFromList("Cluster Node Size", sizes, "")
	doConfig.NodePools[0].Count, _ = strconv.Atoi(promptLine("Cluster Node Count", "", "3"))

	kf, _ := doApi.R().
		SetBody(doConfig).
		Post("/v2/kubernetes/clusters")

	if kf.StatusCode() > 299 {
		fmt.Println(kf.String())
		_, _ = cfmt.Println("{{✗ failed to create digital ocean cluster}}::red")
		os.Exit(1)
	} else {
		_, _ = cfmt.Println("{{✓ digital ocean cluster created}}::lightGreen")
	}

	var doCluster DigitalOcean
	jsonUnmarshalErr := json.Unmarshal(kf.Body(), &doCluster)
	if jsonUnmarshalErr != nil {
		fmt.Println(jsonUnmarshalErr)
		return
	}

	doSpinner := spinner.New("Starting a kubernetes cluster on digital ocean")
	doSpinner.Start("Waiting for digital ocean cluster to be ready. This may take a few minutes. Time enough to get a coffee ☕")
	clusterID := doCluster.KubernetesCluster.ID

	for {
		time.Sleep(2 * time.Second)
		doWait, _ := doApi.R().
			Get("/v2/kubernetes/clusters/" + clusterID)

		if doWait.StatusCode() > 299 {
			fmt.Println(doWait.String())
			doSpinner.Error("Failed to create digital ocean cluster")
			continue
		} else {
			var doCluster DigitalOcean
			jsonUnmarshalBErr := json.Unmarshal(doWait.Body(), &doCluster)
			if jsonUnmarshalBErr != nil {
				fmt.Println(jsonUnmarshalBErr)
				return
			}
			if doCluster.KubernetesCluster.Status.State == "running" {
				doSpinner.Success("digital ocean cluster created")
				break
			}
		}
	}

	kubectl, _ := doApi.R().
		Get("v2/kubernetes/clusters/" + clusterID + "/kubeconfig")
	mergeKubeconfigErr := mergeKubeconfig(kubectl.Body())
	if mergeKubeconfigErr != nil {
		fmt.Println(mergeKubeconfigErr)
		return
	}

}

func getDigitaloceanOptions(api *resty.Client) ([]string, []string, []string) {
	token := os.Getenv("DIGITALOCEAN_ACCESS_TOKEN")
	if token == "" {
		_, _ = cfmt.Println("{{✗ DIGITALOCEAN_ACCESS_TOKEN is not set}}::red")
		log.Fatal("missing DIGITALOCEAN_ACCESS_TOKEN")
	}

	optionsResponse, err := api.R().Get("/v2/kubernetes/options")
	if err != nil {
		_, _ = cfmt.Println("{{✗ failed to get digitalocean options}}::red")
		log.Fatal(err)
	}

	var versionsResponse DigitaloceanOptions
	jsonUnmarshalErr := json.Unmarshal(optionsResponse.Body(), &versionsResponse)
	if jsonUnmarshalErr != nil {
		fmt.Println(jsonUnmarshalErr)
		return nil, nil, nil
	}

	var versions []string
	for _, v := range versionsResponse.Options.Versions {
		versions = append(versions, v.Slug)
	}

	var regions []string
	for _, v := range versionsResponse.Options.Regions {
		regions = append(regions, v.Slug)
	}

	var sizes []string
	for _, v := range versionsResponse.Options.Sizes {
		sizes = append(sizes, v.Slug)
	}

	return versions, regions, sizes
}
0707010000001E000081A400000000000000000000000167BEEE0500000858000000000000000000000000000000000000002E00000000kubero-cli-2.4.7/cmd/kuberoCli/install_gke.gopackage kuberoCli

import (
	"fmt"
	"log"
	"math/rand"
	"os/exec"
	"strconv"

	"github.com/leaanthony/spinner"
)

func installGKE() {
	// implememted with gcloud, since it is required for the download of the kubeconfig anyway

	// gcloud config list
	// gcloud config get project
	// gcloud container clusters create kubero-cluster-4 --region=us-central1-c
	// gcloud container clusters get-credentials kubero-cluster-4 --region=us-central1-c

	// https://cloud.google.com/kubernetes-engine/docs/reference/libraries#client-libraries-install-go
	// https://github.com/googleapis/google-cloud-go

	gcloudName := promptLine("Kubernetes Cluster Name", "", "kubero-"+strconv.Itoa(rand.Intn(1000)))
	gcloudRegion := promptLine("Region", "[https://cloud.google.com/compute/docs/regions-zones]", "us-central1-c")
	gcloudClusterVersion := promptLine("Cluster Version", "[https://cloud.google.com/kubernetes-engine/docs/release-notes-regular]", "1.23.8-gke.1900")

	spinner := spinner.New("Spin up a GKE cluster")
	spinner.Start("run command : gcloud container clusters create " + gcloudName + " --region=" + gcloudRegion + " --cluster-version=" + gcloudClusterVersion)
	_, err := exec.Command("gcloud", "container", "clusters", "create", gcloudName,
		"--region="+gcloudRegion,
		"--cluster-version="+gcloudClusterVersion).Output()
	if err != nil {
		fmt.Println()
		spinner.Error("Failed to run command. Try runnig this command manually and skip this step: 'gcloud container clusters create " + gcloudName + " --region=" + gcloudRegion + " --cluster-version=" + gcloudClusterVersion + "'")
		log.Fatal(err)
	}
	spinner.Success("GKE cluster started sucessfully")

	spinner.Start("Get credentials for the GKE cluster")
	_, err = exec.Command("gcloud", "container", "clusters", "get-credentials", gcloudName, "--region="+gcloudRegion).Output()
	if err != nil {
		fmt.Println()
		spinner.Error("Failed to run command. Try runnig this command manually and skip this step: 'gcloud container clusters get-credentials " + gcloudName + " --region=" + gcloudRegion + "'")
		log.Fatal(err)
	} else {
		spinner.Success("GKE cluster credentials set")
	}

}
0707010000001F000081A400000000000000000000000167BEEE05000009A6000000000000000000000000000000000000002F00000000kubero-cli-2.4.7/cmd/kuberoCli/install_kind.gopackage kuberoCli

import (
	"fmt"
	"log"
	"math/rand"
	"os"
	"os/exec"
	"strconv"

	"github.com/go-resty/resty/v2"
	"github.com/i582/cfmt/cmd/cfmt"
	"github.com/leaanthony/spinner"
	"gopkg.in/yaml.v3"
)

func installKind() {

	if !checkBinary("kind") {
		log.Fatal("kind binary is not installed")
	}

	installer := resty.New()

	// get kind.yaml
	installer.SetBaseURL("https://raw.githubusercontent.com")
	kf, _ := installer.R().Get("/kubero-dev/kubero/main/kind.yaml")

	var kindConfig KindConfig
	yamlUnmarshalErr := yaml.Unmarshal(kf.Body(), &kindConfig)
	if yamlUnmarshalErr != nil {
		fmt.Println(yamlUnmarshalErr)
		return
	}

	// set cluster name
	kindConfig.Name = promptLine("Kind Cluster Name", "", "kubero-"+strconv.Itoa(rand.Intn(1000)))

	// select Kubernetes version
	kv, _ := installer.R().Get("/kubero-dev/kubero-cli/main/templates/kindVersions.yaml")
	var kindDefaults struct {
		AvailableKubernetesVersions []string `yaml:"availableKubernetesVersions"`
	}
	yamlUnmarshalBErr := yaml.Unmarshal(kv.Body(), &kindDefaults)
	if yamlUnmarshalBErr != nil {
		fmt.Println(yamlUnmarshalBErr)
		return
	}
	version := selectFromList("Kubernetes Version", kindDefaults.AvailableKubernetesVersions, "")

	kindConfig.Nodes[0].Image = "kindest/node:" + version

	if argPort == "" {
		argPort = promptLine("Local HTTP Port", "", "80")
	}
	kindConfig.Nodes[0].ExtraPortMappings[0].HostPort, _ = strconv.Atoi(argPort)

	if argPortSecure == "" {
		argPortSecure = promptLine("Local HTTPS Port", "", "443")
	}
	kindConfig.Nodes[0].ExtraPortMappings[1].HostPort, _ = strconv.Atoi(argPortSecure)

	kindConfigYaml, _ := yaml.Marshal(kindConfig)
	//fmt.Println("-------------- kind.yaml ---------------")
	//fmt.Println(string(kindConfigYaml))
	//fmt.Println("----------------------------------------")

	kindConfigErr := os.WriteFile("kind.yaml", kindConfigYaml, 0644)
	if kindConfigErr != nil {
		fmt.Println(kindConfigErr)
		return
	}

	kindSpinner := spinner.New("Spin up a local Kind cluster")
	_, _ = cfmt.Println("run command : kind create cluster --config kind.yaml")
	kindSpinner.Start("Creating Kind cluster")
	out, err := exec.Command("kind", "create", "cluster", "--config", "kind.yaml").Output()
	if err != nil {
		kindSpinner.Error("Failed to run command. Try running this command manually and skip this step : 'kind create cluster --config kind.yaml'")
		log.Fatal(err)
	}
	kindSpinner.Success("Kind cluster started successfully")

	fmt.Println(string(out))
}
07070100000020000081A400000000000000000000000167BEEE0500000E9C000000000000000000000000000000000000003100000000kubero-cli-2.4.7/cmd/kuberoCli/install_linode.gopackage kuberoCli

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"os"
	"strconv"
	"time"

	"github.com/go-resty/resty/v2"
	"github.com/i582/cfmt/cmd/cfmt"
	"github.com/leaanthony/spinner"
)

func installLinode() {
	// https://www.linode.com/docs/api/linode-kubernetes-engine-lke/#kubernetes-cluster-create
	// https://www.linode.com/docs/api/linode-kubernetes-engine-lke/#kubernetes-cluster-view
	// https://www.linode.com/docs/api/linode-kubernetes-engine-lke/#kubeconfig-view

	_, _ = cfmt.Println("{{⚠ Installing Kubernetes on Linode is currently beta state in kubero-cli}}::yellow")
	_, _ = cfmt.Println("{{  Please report if you run into errors}}::yellow")

	token := os.Getenv("LINODE_ACCESS_TOKEN")
	if token == "" {
		_, _ = cfmt.Println("{{✗ LINODE_ACCESS_TOKEN is not set}}::red")
		log.Fatal("missing LINODE_ACCESS_TOKEN")
	}

	api := resty.New().
		SetAuthScheme("Bearer").
		SetAuthToken(token).
		SetHeader("Accept", "application/json").
		SetHeader("Content-Type", "application/json").
		SetHeader("User-Agent", "kubero-cli/"+kuberoCliVersion).
		SetBaseURL("https://api.linode.com/v4/lke/clusters")

	var clusterConfig LinodeCreateClusterRequest
	clusterConfig.Label = promptLine("Cluster Name", "", "kubero-"+strconv.Itoa(rand.Intn(1000)))
	clusterConfig.Region = promptLine("Region", "[https://www.linode.com/global-infrastructure/]", "us-central") // TODO load the list of regions or point to e better document

	workerNodesCount, _ := strconv.Atoi(promptLine("Worker Nodes Count", "", "3"))
	workerNodesType := promptLine("Worker Nodes Type", "[https://www.linode.com/pricing/]", "g6-standard-2") // TODO load the list of types or point to e better document

	clusterConfig.K8SVersion = promptLine("Kubernetes Version", "[1.25]", "1.25")
	clusterConfig.Tags = []string{"kubero"}
	clusterConfig.NodePools = []LinodeNodepool{
		{
			Type:  workerNodesType,
			Count: workerNodesCount,
		},
	}

	spinnerObj := spinner.New("Spin up a Linode Kubernetes Cluster")

	spinnerObj.Start("Create Linode Kubernetes Cluster")
	clusterResponse, _ := api.R().SetBody(clusterConfig).Post("")
	if clusterResponse.StatusCode() > 299 {
		fmt.Println()
		spinnerObj.Error("Failed to create Linode Kubernetes Cluster")
		log.Fatal(clusterResponse.String())
	}
	spinnerObj.Success("Linode Kubernetes Cluster created")

	var cluster LinodeCreateClusterResponse
	jsonUnmarshalErr := json.Unmarshal(clusterResponse.Body(), &cluster)
	if jsonUnmarshalErr != nil {
		log.Fatal(jsonUnmarshalErr)
		return
	}

	// According to the docs, the cluster is ready after 2-5 minutes.
	_, _ = cfmt.Println("{{  Wait for Linode Kubernetes Cluster to be ready}}::lightBlue")
	_, _ = cfmt.Println("{{  According to the docs this may take up to 7 minutes}}::lightBlue")
	_, _ = cfmt.Println("{{  Time for a coffee break and some Chuck Norris jokes.}}::lightBlue")
	spinnerObj.Start("Wait for Linode Kubernetes Cluster to be ready")

	var LinodeKubeconfig struct {
		Kubeconfig string `json:"kubeconfig"`
	}

	for i := 0; true; i++ {
		time.Sleep(15 * time.Second)
		r, _ := api.R().SetResult(&LinodeKubeconfig).Get("/" + strconv.Itoa(cluster.ID) + "/kubeconfig")
		if r.StatusCode() > 299 {
			tellAChucknorrisJoke()
		}
		if LinodeKubeconfig.Kubeconfig != "" {
			spinnerObj.Success("Linode Kubernetes Cluster is ready")
			break
		}
	}
	kubeconfig, err := base64.StdEncoding.DecodeString(LinodeKubeconfig.Kubeconfig)

	if err != nil {
		fmt.Println()
		spinnerObj.Error("Failed to decode kubeconfig")
		log.Fatal(err)
	}

	err = mergeKubeconfig(kubeconfig)
	if err != nil {
		fmt.Println()
		spinnerObj.Error("Failed to merge kubeconfig")
		log.Fatal(err)
	}

	spinnerObj.Success("Linode Kubernetes Cluster credentials set")

}
07070100000021000081A400000000000000000000000167BEEE0500001557000000000000000000000000000000000000003300000000kubero-cli-2.4.7/cmd/kuberoCli/install_scaleway.gopackage kuberoCli

import (
	"encoding/base64"
	"encoding/json"
	"log"
	"math/rand"
	"os"
	"strconv"
	"time"

	"github.com/go-resty/resty/v2"
	"github.com/i582/cfmt/cmd/cfmt"
	"github.com/leaanthony/spinner"
)

func installScaleway() {

	// create the cluster
	// https://api.scaleway.com/k8s/v1/regions/{region}/clusters/{cluster_id}/available-versions

	// check state of cluster
	// https://api.scaleway.com/k8s/v1/regions/{region}/clusters

	// get the kubeconfig
	// https://api.scaleway.com/k8s/v1/regions/{region}/clusters/{cluster_id}/kubeconfig

	// _, _ = cfmt.Println("{{⚠ Installing Kubernetes on Scaleway is currently beta state in kubero-cli}}::yellow")
	// _, _ = cfmt.Println("{{  Please report if you run into errors}}::yellow")

	var cluster ScalewayCreate
	/*
		cluster.ProjectID = os.Getenv("SCALEWAY_PROJECTID")
		if cluster.ProjectID == "" {
			_, _ = cfmt.Println("{{✗ SCALEWAY_PROJECTID is not set}}::red")
			log.Fatal("missing SCALEWAY_PROJECTID")
		}
	*/
	cluster.OrganizationID = os.Getenv("SCALEWAY_PROJECTID")

	token := os.Getenv("SCALEWAY_ACCESS_TOKEN")
	if token == "" {
		_, _ = cfmt.Println("{{✗ SCALEWAY_ACCESS_TOKEN is not set}}::red")
		log.Fatal("missing SCALEWAY_ACCESS_TOKEN")
	}

	api := resty.New().
		SetHeader("X-Auth-Token", token).
		SetHeader("Accept", "application/json").
		SetHeader("Content-Type", "application/json").
		SetHeader("User-Agent", "kubero-cli/"+kuberoCliVersion).
		SetBaseURL("https://api.scaleway.com/k8s/v1/regions")

	cluster.Name = promptLine("Kubernetes Cluster Name", "", "kubero-"+strconv.Itoa(rand.Intn(1000)))

	regionsList := []string{"fr-par", "nl-ams", "pl-waw"}
	region := selectFromList("Cluster Region", regionsList, "")

	versions := getScalewayVersions(api, region)
	cluster.Version = selectFromList("Kubernetes Version", versions, "")

	// TODO lets make this configurable if needed in the future
	cluster.Cni = "unknown_cni"
	cluster.Ingress = "unknown_ingress" // is marked as deprecated in the api but required for now
	/*
		TODO : not implemented yet, but prepare for it in the future
		cluster.AutoscalerConfig.Estimator = "unknown_estimator"
		cluster.AutoscalerConfig.Expander = "unknown_expander"
		cluster.AutoscalerConfig.ScaleDownUtilizationThreshold = 0.5
		cluster.AutoscalerConfig.MaxGracefulTerminationSec = 60
	*/
	cluster.AutoUpgrade.Enable = false
	cluster.AutoUpgrade.MaintenanceWindow.StartHour = 3
	cluster.AutoUpgrade.MaintenanceWindow.Day = "any"

	// TODO load the options from the api
	nodeType := promptLine("Node Types", "[DEV1-M,DEV1-XL,START1-M]", "DEV1-M")

	clusterSize, _ := strconv.Atoi(promptLine("Cluster Size", "[at least 3]", "3"))

	cluster.Pools = append(cluster.Pools, ScalewayNodePool{
		Name:             "default",
		NodeType:         nodeType,
		Autoscaling:      false,
		Size:             clusterSize,
		ContainerRuntime: "unknown_runtime",
		RootVolumeType:   "default_volume_type",
		//RootVolumeSize:   50,
	})

	//fmt.Printf("%+v\n", cluster)
	newCluster, _ := api.R().SetBody(cluster).Post(region + "/clusters")

	var clusterResponse ScalewayCreateResponse
	if newCluster.StatusCode() < 299 {
		jsonUnmarshalErr := json.Unmarshal(newCluster.Body(), &clusterResponse)
		if jsonUnmarshalErr != nil {
			_, _ = cfmt.Println("{{✗ Failed to create Scaleway Kubernetes cluster}}::red")
			return
		}
		_, _ = cfmt.Println("{{✓ Scaleway Kubernetes cluster created}}::lightGreen")
	} else {
		_, _ = cfmt.Println("{{✗ Scaleway Kubernetes Cluster creation failed}}::red")
		log.Fatal(string(newCluster.Body()))
	}

	spinnerObj := spinner.New()
	spinnerObj.Start("Waiting for cluster to be ready")
	for {
		clusterStatus, _ := api.R().Get(region + "/clusters/" + clusterResponse.ID)
		var clusterStatusResponse ScalewayCreateResponse
		jsonUnmarshalErr := json.Unmarshal(clusterStatus.Body(), &clusterStatusResponse)
		if jsonUnmarshalErr != nil {
			_, _ = cfmt.Println("{{✗ Failed to get Scaleway Kubernetes cluster status}}::red")
			return
		}
		if clusterStatusResponse.Status == "ready" {
			spinnerObj.Success("Scaleway Kubernetes Cluster is ready")
			break
		}
		time.Sleep(2 * time.Second)
	}

	k, _ := api.R().Get(region + "/clusters/" + clusterResponse.ID + "/kubeconfig")

	var scalewayKubeconfigResponse ScalewayKubeconfigResponse
	jsonUnmarshalErr := json.Unmarshal(k.Body(), &scalewayKubeconfigResponse)
	if jsonUnmarshalErr != nil {
		_, _ = cfmt.Println("{{✗ Failed to download kubeconfig}}::red")
		return
	}
	kubeconfig, _ := base64.StdEncoding.DecodeString(scalewayKubeconfigResponse.Content)

	err := mergeKubeconfig(kubeconfig)
	if err != nil {
		_, _ = cfmt.Println("{{✗ Failed to download kubeconfig}}::red")
		log.Fatal(err)
	} else {
		_, _ = cfmt.Println("{{✓ Kubeconfig downloaded}}::lightGreen")
	}

}

func getScalewayVersions(api *resty.Client, region string) []string {
	token := os.Getenv("SCALEWAY_ACCESS_TOKEN")
	if token == "" {
		_, _ = cfmt.Println("{{✗ SCALEWAY_ACCESS_TOKEN is not set}}::red")
		log.Fatal("missing SCALEWAY_ACCESS_TOKEN")
	}

	versionsR, _ := api.R().Get(region + "/versions")

	var versionsResponse ScalewayVersionsResponse
	jsonUnmarshalErr := json.Unmarshal(versionsR.Body(), &versionsResponse)
	if jsonUnmarshalErr != nil {
		_, _ = cfmt.Println("{{✗ Failed to get Scaleway Kubernetes versions}}::red")
		return nil
	}

	var versions []string
	for _, v := range versionsResponse.Versions {
		versions = append(versions, v.Name)
	}

	return versions
}
07070100000022000081A400000000000000000000000167BEEE0500000C53000000000000000000000000000000000000002B00000000kubero-cli-2.4.7/cmd/kuberoCli/instance.gopackage kuberoCli

/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/

import (
	"fmt"
	"os"

	"github.com/i582/cfmt/cmd/cfmt"
	"github.com/olekukonko/tablewriter"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

// instanceCmd represents the instance command
var instanceCmd = &cobra.Command{
	Use:     "instance",
	Aliases: []string{"i"},
	Short:   "List available instances",
	Long:    `Print a list of available instances.`,
	Run: func(cmd *cobra.Command, args []string) {
		/*
			fmt.Println("current instance : " + currentInstanceName)
			fmt.Println(instanceList)
			if currentInstance.ApiUrl == "" {
				fmt.Println("No current instance api URL")
			} else {
				fmt.Println("Current instance api URL : " + currentInstance.ApiUrl)
			}
		*/

		printInstanceList()
	},
}

func init() {
	rootCmd.AddCommand(instanceCmd)
}

func printInstanceList() {
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Active", "Token", "Name", "API URL", "Path", "IAC Base Dir"})
	table.SetAutoWrapText(false)
	table.SetAutoFormatHeaders(true)
	//table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
	//table.SetAlignment(tablewriter.ALIGN_LEFT)
	table.SetRowLine(true)
	table.SetCenterSeparator("")
	//table.SetColumnSeparator("")
	table.SetRowSeparator("")
	table.SetBorder(false)
	table.SetTablePadding("\t")
	table.SetNoWhiteSpace(true)
	for _, instanceName := range instanceNameList {
		active := ""
		if instanceName == currentInstanceName {
			active = cfmt.Sprintf("   {{✔}}::green")
		}

		token := ""

		// check if instanceName is in credentialsConfig
		if credentialsConfig.GetString(instanceName) != "" {
			token = cfmt.Sprintf("  {{✔}}::green")
		}

		table.Append([]string{
			active,
			token,
			instanceName,
			instanceList[instanceName].ApiUrl,
			instanceList[instanceName].ConfigPath,
			instanceList[instanceName].IacBaseDir,
		})
	}
	table.Render()
}

func createInstanceForm() {
	fmt.Println("Create a new instance")

	instanceName := promptLine("Enter the name of the instance", "", "")
	instanceApiurl := promptLine("Enter the API URL of the instance", "", "http://localhost:80")
	instancePath := viper.ConfigFileUsed()

	personalInstanceList := viper.GetStringMap("instances")

	personalInstanceList[instanceName] = Instance{
		Name:       instanceName,
		ApiUrl:     instanceApiurl,
		ConfigPath: instancePath,
	}

	viper.Set("instances", personalInstanceList)

	instanceNameList = append(instanceNameList, instanceName)

	setCurrentInstance(instanceName)

}

func setCurrentInstance(instanceName string) {
	currentInstanceName = instanceName
	currentInstance = instanceList[instanceName]
	viper.Set("currentInstance", instanceName)
	writeConfigErr := viper.WriteConfig()
	if writeConfigErr != nil {
		fmt.Println("Failed to save configuration:", writeConfigErr)
		return
	}
}

func deleteInstanceForm() {
	instanceName := selectFromList("Select an instance to delete", instanceNameList, "")

	delete(instanceList, instanceName)
	viper.Set("instances", instanceList)
	writeConfigErr := viper.WriteConfig()
	if writeConfigErr != nil {
		fmt.Println("Failed to save configuration:", writeConfigErr)
		return
	}

}
07070100000023000081A400000000000000000000000167BEEE0500000205000000000000000000000000000000000000003100000000kubero-cli-2.4.7/cmd/kuberoCli/instanceCreate.gopackage kuberoCli

/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/

import (
	"fmt"

	"github.com/spf13/cobra"
)

// instanceCreateCmd represents the instanceCreate command
var instanceCreateCmd = &cobra.Command{
	Use:     "create",
	Aliases: []string{"new", "cr"},
	Short:   "Create an new instance",
	Long:    `Create an new instance.`,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println("instanceCreate called")
		createInstanceForm()
	},
}

func init() {
	instanceCmd.AddCommand(instanceCreateCmd)
}
07070100000024000081A400000000000000000000000167BEEE05000001FD000000000000000000000000000000000000003100000000kubero-cli-2.4.7/cmd/kuberoCli/instanceDelete.gopackage kuberoCli

/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/

import (
	"fmt"

	"github.com/spf13/cobra"
)

// instanceDeleteCmd represents the instanceDelete command
var instanceDeleteCmd = &cobra.Command{
	Use:     "delete",
	Aliases: []string{"del", "rm"},
	Short:   "Delete an instance",
	Long:    `Delete an instance.`,
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println("instanceDelete called")
		deleteInstanceForm()
	},
}

func init() {
	instanceCmd.AddCommand(instanceDeleteCmd)
}
07070100000025000081A400000000000000000000000167BEEE050000022E000000000000000000000000000000000000003100000000kubero-cli-2.4.7/cmd/kuberoCli/instanceSelect.gopackage kuberoCli

/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/

import (
	"github.com/spf13/cobra"
)

// instanceSelectCmd represents the instanceSelect command
var instanceSelectCmd = &cobra.Command{
	Use:     "select",
	Aliases: []string{"use"},
	Short:   "Select an instance",
	Long:    `Select an instance to use.`,
	Run: func(cmd *cobra.Command, args []string) {
		newInstanceName := selectFromList("Select an instance", instanceNameList, "")
		setCurrentInstance(newInstanceName)
	},
}

func init() {
	instanceCmd.AddCommand(instanceSelectCmd)
}
07070100000026000081A400000000000000000000000167BEEE05000004A8000000000000000000000000000000000000002700000000kubero-cli-2.4.7/cmd/kuberoCli/list.gopackage kuberoCli

/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/

import (
	"fmt"
	"os"

	"github.com/i582/cfmt/cmd/cfmt"
	"github.com/spf13/cobra"
)

// listCmd represents the list command
var listCmd = &cobra.Command{
	Use:     "list",
	Aliases: []string{"ls"},
	Short:   "List pipelines and apps",
	Long:    `List pipelines and apps`,
	Run: func(cmd *cobra.Command, args []string) {

		if pipelineName != "" {
			// get a single pipeline
			pipelineResp, err := client.Get("/api/cli/pipelines/" + pipelineName)
			if pipelineResp.StatusCode() == 404 {
				_, _ = cfmt.Println("{{  Pipeline not found}}::red")
				os.Exit(1)
			}

			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}

			printPipeline(pipelineResp)
			appsList()
		} else {
			// get the pipelines
			pipelineListResp, err := client.Get("/api/cli/pipelines")
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			printPipelinesList(pipelineListResp)

		}
	},
}

func init() {
	rootCmd.AddCommand(listCmd)
	listCmd.Flags().StringVarP(&pipelineName, "pipeline", "p", "", "name of the pipeline")
	listCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", "table", "output format [table, json]")
}
07070100000027000081A400000000000000000000000167BEEE0500000409000000000000000000000000000000000000002800000000kubero-cli-2.4.7/cmd/kuberoCli/login.gopackage kuberoCli

import (
	"fmt"
	"github.com/spf13/cobra"
)

// loginCmd represents the login command
var loginCmd = &cobra.Command{
	Use:   "login",
	Short: "Login to your Kubero instance",
	Long:  `Use the login subcommand to login to your Kubero instance.`,
	Run: func(cmd *cobra.Command, args []string) {
		ensureIntanceOrCreate()
		setKuberoCredentials("")
	},
}

func init() {
	rootCmd.AddCommand(loginCmd)
}

func ensureIntanceOrCreate() {

	instanceNameList = append(instanceNameList, "<create new>")

	instanceName := selectFromList("Select an instance", instanceNameList, currentInstanceName)
	if instanceName == "<create new>" {
		createInstanceForm()
	} else {
		setCurrentInstance(instanceName)
	}

}

func setKuberoCredentials(token string) {

	if token == "" {
		token = promptLine("Kubero Token", "", "")
	}

	credentialsConfig.Set(currentInstanceName, token)
	writeConfigErr := credentialsConfig.WriteConfig()
	if writeConfigErr != nil {
		fmt.Println("Error writing config file: ", writeConfigErr)
		return
	}
}
07070100000028000081A400000000000000000000000167BEEE0500001344000000000000000000000000000000000000002B00000000kubero-cli-2.4.7/cmd/kuberoCli/pipeline.gopackage kuberoCli

import (
	"encoding/json"
	"fmt"
	"kubero/pkg/kuberoApi"
	"log"
	"os"
	"strconv"

	"github.com/go-resty/resty/v2"
	"github.com/i582/cfmt/cmd/cfmt"
	"github.com/olekukonko/tablewriter"
	"github.com/spf13/viper"
)

func loadAllLocalPipelines() pipelinesConfigsList {
	pipelines := getAllLocalPipelines()

	//var pipelinesConfigsList pipelinesConfigsList
	pipelinesConfigsList := make(pipelinesConfigsList)

	for _, pipeline := range pipelines {
		pipelinesConfigsList[pipeline] = loadLocalPipeline(pipeline)
	}

	return pipelinesConfigsList

}

func printPipeline(r *resty.Response) {
	//fmt.Println(r)

	var pipeline Pipeline
	unmarshalErr := json.Unmarshal(r.Body(), &pipeline)
	if unmarshalErr != nil {
		fmt.Println("Error: ", "Unable to decode response")
		return
	}

	_, _ = cfmt.Printf("{{Name:}}::lightWhite %v \n", pipeline.Name)
	_, _ = cfmt.Printf("{{BuildPack:}}::lightWhite %v\n", pipeline.BuildPack.Name)
	_, _ = cfmt.Printf("{{Language:}}::lightWhite %v\n", pipeline.BuildPack.Language)
	if pipeline.DockerImage != "" {
		fmt.Printf("{{Docker Image:}}::lightWhite %v \n", pipeline.DockerImage)
	}
	_, _ = cfmt.Printf("{{Deployment Strategy:}}::lightWhite %v \n", pipeline.DeploymentStrategy)
	_, _ = cfmt.Printf("{{Git:}}::lightWhite %v:%v \n", pipeline.Git.Repository.SshUrl, pipeline.Git.Repository.DefaultBranch)
}

// print the response as a table
func printPipelinesList(r *resty.Response) {

	table := tablewriter.NewWriter(os.Stdout)
	//table.SetAutoFormatHeaders(true)
	//table.SetBorder(false)
	//table.SetAlignment(tablewriter.ALIGN_CENTER)
	table.SetHeader([]string{
		"Name",
		"Repository",
		//"Branch",
		"BuildPack",
		"reviewapps",
		"test",
		"staging",
		"production",
		//"Docker Image",
		//"Deployment Strategy",
		//"Review Apps"
	})

	var pipelinesList PipelinesList
	unmarshalErr := json.Unmarshal(r.Body(), &pipelinesList)
	if unmarshalErr != nil {
		fmt.Println("Error: ", "Unable to decode response")
		return
	}

	for _, pipeline := range pipelinesList.Items {
		table.Append([]string{
			pipeline.Name,
			pipeline.Git.Repository.SshUrl,
			//pipeline.Git.Repository.DefaultBranch,
			pipeline.BuildPack.Name,
			//pipeline.DockerImage,
			//pipeline.DeploymentStrategy,
			//fmt.Sprintf("%t", pipeline.ReviewApps)
			boolToEmoji(pipeline.Phases[0].Enabled),
			boolToEmoji(pipeline.Phases[1].Enabled),
			boolToEmoji(pipeline.Phases[2].Enabled),
			boolToEmoji(pipeline.Phases[3].Enabled),
		})
	}

	printCLI(table, r)
}

func getAllRemotePipelines() []string {
	var pipelinesList PipelinesList

	res, err := api.GetPipelines()
	if err != nil {
		fmt.Println("Error: ", "Unable to load pipelines")
		fmt.Println(err)
		os.Exit(1)
	}

	unmarshalErr := json.Unmarshal(res.Body(), &pipelinesList)
	if unmarshalErr != nil {
		fmt.Println("Error: ", "Unable to decode response")
		return nil
	}

	var pipelines []string
	pipelines = make([]string, 0)

	for _, pipeline := range pipelinesList.Items {
		pipelines = append(pipelines, pipeline.Name)
	}

	return pipelines
}

func getAllLocalPipelines() []string {

	baseDir := getIACBaseDir()
	dir := baseDir + "/" + pipelineName

	var pipelineNames []string
	pipelineNames = make([]string, 0)

	files, err := os.ReadDir(dir)
	if err != nil {
		log.Fatal(err)
	}

	for _, f := range files {
		if f.IsDir() {
			if _, err := os.Stat(dir + "/" + f.Name() + "/pipeline.yaml"); err == nil {
				pipelineNames = append(pipelineNames, f.Name())
			}
		}
	}

	return pipelineNames
}

func getPipelinePhases(pipelineConfig *viper.Viper) []string {
	var phases []string

	//pipelineConfig := getPipelineConfig(pipelineName)

	phasesList := pipelineConfig.GetStringSlice("spec.phases")

	for p := range phasesList {
		enabled := pipelineConfig.GetBool("spec.phases." + strconv.Itoa(p) + ".enabled")
		if enabled {
			phases = append(phases, pipelineConfig.GetString("spec.phases."+strconv.Itoa(p)+".name"))
		}
	}
	return phases
}

func loadPipelineConfig(pipelineName string) *viper.Viper {

	baseDir := getIACBaseDir()
	dir := baseDir + "/" + pipelineName

	pipelineConfig := viper.New()
	pipelineConfig.SetConfigName("pipeline") // name of config file (without extension)
	pipelineConfig.SetConfigType("yaml")     // REQUIRED if the config file does not have the extension in the name
	pipelineConfig.AddConfigPath(dir)        // path to look for the config file in
	readInConfigErr := pipelineConfig.ReadInConfig()
	if readInConfigErr != nil {
		fmt.Println("Error: ", "Unable to read config file")
		return nil
	}

	return pipelineConfig
}

func loadLocalPipeline(pipelineName string) kuberoApi.PipelineCRD {

	pipelineConfig := loadPipelineConfig(pipelineName)

	var pipelineCRD kuberoApi.PipelineCRD

	pipelineConfigUnmarshalErr := pipelineConfig.Unmarshal(&pipelineCRD)
	if pipelineConfigUnmarshalErr != nil {
		fmt.Println("Error: ", "Unable to unmarshal config file")
		return kuberoApi.PipelineCRD{}
	}

	return pipelineCRD
}
07070100000029000081A400000000000000000000000167BEEE050000292B000000000000000000000000000000000000002700000000kubero-cli-2.4.7/cmd/kuberoCli/root.gopackage kuberoCli

import (
	"bufio"
	_ "embed"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"os"
	"reflect"
	"strings"

	"kubero/pkg/kuberoApi"

	"github.com/AlecAivazis/survey/v2"
	"github.com/go-git/go-billy/v5/osfs"
	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing/cache"
	"github.com/go-git/go-git/v5/storage/filesystem"
	"github.com/go-resty/resty/v2"
	"github.com/i582/cfmt/cmd/cfmt"
	"github.com/olekukonko/tablewriter"
	"github.com/spf13/cobra"
	_ "github.com/spf13/pflag"
	"github.com/spf13/viper"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

var (
	outputFormat        string
	force               bool
	repoSimpleList      []string
	client              *resty.Request
	api                 *kuberoApi.KuberoClient
	contextSimpleList   []string
	currentInstanceName string
	instanceList        map[string]Instance
	instanceNameList    []string
	currentInstance     Instance
	kuberoCliVersion    string
	pipelineConfig      *viper.Viper
	credentialsConfig   *viper.Viper
	db                  *gorm.DB
)

var rootCmd = &cobra.Command{
	Use:   "kubero",
	Short: "Kubero is a platform as a service (PaaS) that enables developers to build, run, and operate applications on Kubernetes.",
	Long: `
	,--. ,--.        ,--.
	|  .'   /,--.,--.|  |-.  ,---. ,--.--. ,---.
	|  .   ' |  ||  || .-. '| .-. :|  .--'| .-. |
	|  |\   \'  ''  '| '-' |\   --.|  |   ' '-' '
	'--' '--' '----'  '---'  '----''--'    '---'
Documentation:
  https://docs.kubero.dev
`,
	Example: `kubero install`,
	Aliases: []string{"kbr"},
}

func Execute() {
	rootCmd.CompletionOptions.HiddenDefaultCmd = false

	SetUsageDefinition(rootCmd)

	loadCLIConfig()
	loadCredentials()
	api = new(kuberoApi.KuberoClient)
	client = api.Init(currentInstance.ApiUrl, credentialsConfig.GetString(currentInstanceName))
	err := rootCmd.Execute()
	if err != nil {
		os.Exit(1)
	}
}

func init() {
	rootCmd.CompletionOptions.HiddenDefaultCmd = true
	initDB()
}

func initDB() {
	var err error
	db, err = gorm.Open(sqlite.Open("kubero.db"), &gorm.Config{})
	if err != nil {
		log.Fatal("Failed to connect to database:", err)
	}
	autoMigrateErr := db.AutoMigrate(&Instance{})
	if autoMigrateErr != nil {
		log.Fatal("Failed to migrate database:", autoMigrateErr)
		return
	}
}

func printCLI(table *tablewriter.Table, r *resty.Response) {
	if outputFormat == "json" {
		fmt.Println(r)
	} else {
		table.Render()
	}
}

func promptWarning(msg string) {
	_, _ = cfmt.Println("{{\n⚠️   " + msg + ".\n}}::yellow")
}

//func promptBanner(msg string) {
//	_, _ = cfmt.Printf(`
//    {{                                                                            }}::bgRed
//    {{  %-72s  }}::bgRed|#ffffff
//    {{                                                                            }}::bgRed
//	`, msg)
//}

func promptLine(question, options, def string) string {
	if def != "" && force {
		_, _ = cfmt.Printf("\n{{?}}::green %s %s : {{%s}}::cyan\n", question, options, def)
		return def
	}
	reader := bufio.NewReader(os.Stdin)
	_, _ = cfmt.Printf("\n{{?}}::green|bold {{%s %s}}::bold {{%s}}::cyan : ", question, options, def)
	text, _ := reader.ReadString('\n')
	text = strings.TrimSpace(text)
	if text == "" {
		text = def
	}
	return text
}

func selectFromList(question string, options []string, def string) string {
	_, _ = cfmt.Println("")
	if def != "" && force {
		_, _ = cfmt.Printf("\n{{?}}::green %s : {{%s}}::cyan\n", question, def)
		return def
	}
	prompt := &survey.Select{
		Message: question,
		Options: options,
	}
	askOneErr := survey.AskOne(prompt, &def)
	if askOneErr != nil {
		fmt.Println("Error while selecting:", askOneErr)
		return ""
	}
	return def
}

func confirmationLine(question, def string) bool {
	confirmation := promptLine(question, "[y,n]", def)
	if confirmation != "y" {
		_, _ = cfmt.Println("{{\n✗ Aborted\n}}::red")
		os.Exit(0)
		return false
	}
	return true
}

func loadRepositories() {
	res, err := api.GetRepositories()
	if res == nil {
		fmt.Println("Error: Can't reach Kubero API. Make sure, you are logged in.")
		os.Exit(1)
	}
	if res.StatusCode() != 200 {
		fmt.Println("Error:", res.StatusCode(), "Can't reach Kubero API. Make sure, you are logged in.")
		os.Exit(1)
	}
	if err != nil {
		fmt.Println("Error: Unable to load repositories")
		fmt.Println(err)
		os.Exit(1)
	}
	var availRep Repositories
	jsonUnmarshalErr := json.Unmarshal(res.Body(), &availRep)
	if jsonUnmarshalErr != nil {
		fmt.Println("Error: Unable to load repositories")
		return
	}
	t := reflect.TypeOf(availRep)
	repoSimpleList = make([]string, t.NumField())
	for i := range repoSimpleList {
		if reflect.ValueOf(availRep).Field(i).Bool() {
			repoSimpleList[i] = t.Field(i).Name
		}
	}
}

func loadContexts() {
	cont, _ := api.GetContexts()
	var contexts Contexts
	jsonUnmarshalErr := json.Unmarshal(cont.Body(), &contexts)
	if jsonUnmarshalErr != nil {
		fmt.Println("Error: Unable to load contexts")
		return
	}
	for _, context := range contexts {
		contextSimpleList = append(contextSimpleList, context.Name)
	}
}

func getGitRemote() string {
	gitdir := getGitdir() + "/.git"
	fs := osfs.New(gitdir)
	s := filesystem.NewStorageWithOptions(fs, cache.NewObjectLRUDefault(), filesystem.Options{KeepDescriptors: true})
	r, err := git.Open(s, fs)
	if err == nil {
		remotes, _ := r.Remotes()
		return remotes[0].Config().URLs[0]
	}
	return ""
}

func getGitdir() string {
	wd, _ := os.Getwd()
	path := strings.Split(wd, "/")
	for i := len(path); i >= 0; i-- {
		subPath := strings.Join(path[:i], "/")
		fileInfo, err := os.Stat(subPath + "/.git")
		if err == nil && fileInfo.IsDir() {
			return subPath
		}
	}
	return ""
}

func getIACBaseDir() string {
	basePath := "."
	if currentInstance.IacBaseDir == "" {
		currentInstance.IacBaseDir = ".kubero"
		basePath += "/" + currentInstance.IacBaseDir
	}
	gitdir := getGitdir()
	if gitdir != "" {
		basePath = gitdir + "/" + currentInstance.IacBaseDir
	}
	if _, err := os.Stat(basePath); os.IsNotExist(err) {
		_, _ = cfmt.Println("{{Creating directory}}::yellow " + basePath)
		mkDirAllErr := os.MkdirAll(basePath, 0755)
		if mkDirAllErr != nil {
			fmt.Println("Error while creating directory:", mkDirAllErr)
			return ""
		}
	}
	return basePath
}

//func loadConfigs(basePath, pipelineName string) {
//	baseDir := getIACBaseDir()
//	dir := baseDir + "/" + pipelineName
//	pipelineConfig = viper.New()
//	pipelineConfig.SetConfigName("pipeline")
//	pipelineConfig.SetConfigType("yaml")
//	pipelineConfig.AddConfigPath(dir)
//	readInConfigErr := pipelineConfig.ReadInConfig()
//	if readInConfigErr != nil {
//		fmt.Println("Error while loading pipeline config file:", readInConfigErr)
//		return
//	}
//}

func loadConfigs(pipelineName string) {
	baseDir := getIACBaseDir()
	dir := baseDir + "/" + pipelineName
	pipelineConfig = viper.New()
	pipelineConfig.SetConfigName("pipeline")
	pipelineConfig.SetConfigType("yaml")
	pipelineConfig.AddConfigPath(dir)
	readInConfigErr := pipelineConfig.ReadInConfig()
	if readInConfigErr != nil {
		fmt.Println("Error while loading pipeline config file:", readInConfigErr)
		return
	}
}

func loadCLIConfig() {
	dir := getGitdir()
	repoConfig := viper.New()
	repoConfig.SetConfigName("kubero")
	repoConfig.SetConfigType("yaml")
	repoConfig.AddConfigPath(dir)
	repoConfig.ConfigFileUsed()
	errCred := repoConfig.ReadInConfig()

	viper.SetDefault("api.url", "http://default:2000")
	viper.SetConfigName("kubero")
	viper.SetConfigType("yaml")
	viper.AddConfigPath("/etc/kubero/")
	viper.AddConfigPath("$HOME/.kubero/")
	err := viper.ReadInConfig()

	if err != nil && errCred != nil {
		var configFileNotFoundError viper.ConfigFileNotFoundError
		if errors.As(err, &configFileNotFoundError) {
			fmt.Println("No config file found; using defaults")
		} else {
			fmt.Println("Error while loading config file:", err)
			return
		}
	}

	viperUnmarshalErr := viper.UnmarshalKey("instances", &instanceList)
	if viperUnmarshalErr != nil {
		fmt.Println("Error while unmarshalling instances:", viperUnmarshalErr)
		return
	}
	for instanceName, instance := range instanceList {
		instance.Name = instanceName
		instance.ConfigPath = viper.ConfigFileUsed()
		instanceList[instanceName] = instance
	}

	var repoInstancesList map[string]Instance
	unmarshalKeyErr := repoConfig.UnmarshalKey("instances", &repoInstancesList)
	if unmarshalKeyErr != nil {
		fmt.Println("Error while unmarshalling instances:", unmarshalKeyErr)
		return
	}
	for instanceName, repoInstance := range repoInstancesList {
		repoInstance.Name = instanceName
		repoInstance.ConfigPath = repoConfig.ConfigFileUsed()
		instanceList[instanceName] = repoInstance
	}

	currentInstanceName = viper.GetString("currentInstance")
	for instanceName, instance := range instanceList {
		instance.Name = instanceName
		instanceNameList = append(instanceNameList, instanceName)
		if instanceName == currentInstanceName {
			currentInstance = instance
		}
	}
}

func loadCredentials() {
	credentialsConfig = viper.New()
	credentialsConfig.SetConfigName("credentials")
	credentialsConfig.SetConfigType("yaml")
	credentialsConfig.AddConfigPath("/etc/kubero/")
	credentialsConfig.AddConfigPath("$HOME/.kubero/")
	err := credentialsConfig.ReadInConfig()
	if err != nil {
		fmt.Println("Error while loading credentialsConfig file:", err)
	}
}

func boolToEmoji(b bool) string {
	if b {
		return "✅"
	}
	return "❌"
}

func ensurePipelineIsSet(pipelinesList []string) {
	if pipelineName == "" {
		fmt.Println("")
		prompt := &survey.Select{
			Message: "Select a pipeline",
			Options: pipelinesList,
		}
		askOneErr := survey.AskOne(prompt, &pipelineName)
		if askOneErr != nil {
			fmt.Println("Error while selecting pipeline:", askOneErr)
			return
		}
	}
}

func ensureAppNameIsSet() {
	if appName == "" {
		appName = promptLine("Define a app name", "", appName)
	}
}

func ensureStageNameIsSet() {
	if stageName == "" {
		fmt.Println("")
		pipelineConfig := loadPipelineConfig(pipelineName)
		availablePhases := getPipelinePhases(pipelineConfig)
		prompt := &survey.Select{
			Message: "Select a stage",
			Options: availablePhases,
		}
		askOneErr := survey.AskOne(prompt, &stageName)
		if askOneErr != nil {
			fmt.Println("Error while selecting stage:", askOneErr)
			return
		}
	}
}

func ensureAppNameIsSelected(availableApps []string) {
	if appName == "" {
		fmt.Println("")
		prompt := &survey.Select{
			Message: "Select an app",
			Options: availableApps,
		}
		askOneErr := survey.AskOne(prompt, &appName)
		if askOneErr != nil {
			fmt.Println("Error while selecting app:", askOneErr)
			return
		}
	}
}
0707010000002A000081A400000000000000000000000167BEEE0500000E8A000000000000000000000000000000000000002900000000kubero-cli-2.4.7/cmd/kuberoCli/tunnel.gopackage kuberoCli

/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/

import (
	"fmt"
	"os"
	"regexp"
	"strconv"
	"time"

	"github.com/go-resty/resty/v2"
	"github.com/i582/cfmt/cmd/cfmt"
	"github.com/jonasfj/go-localtunnel"
	"github.com/leaanthony/spinner"

	"github.com/spf13/cobra"
)

var tunnelHost string
var tunnelPort int
var tunnelSubdomain string
var tunnelDuration string

// tunnelCmd represents the tunnel command
var tunnelCmd = &cobra.Command{
	Use:   "tunnel",
	Short: cfmt.Sprint("Create a tunnel to the cluster in NATed infrastructures {{[BETA]}}::cyan "),
	Long:  `Use the tunnel subcommand to create a tunnel to the cluster in NATed infrastructures.`,
	Run: func(cmd *cobra.Command, args []string) {
		startTunnel()
	},
}

func init() {
	rootCmd.AddCommand(tunnelCmd)
	tunnelCmd.Flags().StringVarP(&tunnelHost, "host", "H", "localhost", "Hostname")
	tunnelCmd.Flags().IntVarP(&tunnelPort, "port", "p", 80, "Port to use")
	tunnelCmd.Flags().StringVarP(&tunnelDuration, "timeout", "t", "1h", "Timeout for the tunnel")

	tunnelCmd.Flags().StringVarP(&tunnelSubdomain, "subdomain", "s", "", "Subdomain to use ('-' to generate a random one)")
}

func startTunnel() {

	promptWarning("WARNING: your traffic will routed thru localtunnel.me")

	if tunnelSubdomain == "" {
		tunnelSubdomainSugestion := "kubero-" + generateRandomString(10, "abcdefghijklmnopqrstuvwxyz0123456789")

		if currentInstance.Tunnel.Subdomain != "" {
			tunnelSubdomainSugestion = currentInstance.Tunnel.Subdomain
		} else if currentInstance.Name != "" {
			tunnelSubdomainSugestion = "kubero-" + currentInstance.Name
		}

		tunnelSubdomain = promptLine("Subdomain", "", tunnelSubdomainSugestion)
	}

	// Check if subdomain is valid
	// localtunnel.me allows only lowercasae letters, numbers and dashes
	if !regexp.MustCompile(`^[a-z0-9-]+$`).MatchString(tunnelSubdomain) {
		_, _ = cfmt.Println("{{✖}}::red Subdomain {{" + tunnelSubdomain + "}}::yellow can only contain lowercase letters, numbers and dashes")
		os.Exit(1)
	}

	// genereate a subdomain if the user entered "-"
	if tunnelSubdomain == "-" {
		tunnelSubdomain = ""
	}

	ipclient := resty.New().R()
	ipres, err := ipclient.Get("https://api.ipify.org")
	if err != nil {
		_, _ = cfmt.Println("{{✖}}::red Error getting your IP")
		os.Exit(1)
	}
	_, _ = cfmt.Println()
	_, _ = cfmt.Println("  Endpoint IP (Tunnel Password) : {{" + ipres.String() + "}}::cyan")
	_, _ = cfmt.Println("  Destination Host              : {{" + tunnelHost + "}}::cyan")
	_, _ = cfmt.Println("  Destination Port              : {{" + strconv.Itoa(tunnelPort) + "}}::cyan\n\n")

	spinnerObj := spinner.New()
	spinnerObj.Start("Waiting for tunnel to be ready")

	tunnel, err := localtunnel.New(
		tunnelPort,
		tunnelHost,
		localtunnel.Options{
			Subdomain: tunnelSubdomain,
			//BaseURL:        "https://localtunnel.me",
			MaxConnections: 5,
		},
	)
	if err != nil {
		spinnerObj.Error("Error creating tunnel : " + err.Error())
		//cfmt.Println("{{✖}}::red Error creating tunnel : " + err.Error() + "")
		os.Exit(1)
	}
	defer func(tunnel *localtunnel.LocalTunnel) {
		localTunnelErr := tunnel.Close()
		if localTunnelErr != nil {
			fmt.Println("Error closing tunnel : " + localTunnelErr.Error())
		}
	}(tunnel)

	spinnerObj.UpdateMessage(cfmt.Sprint("Tunnel active at {{" + tunnel.URL() + "}}::cyan with an expiration of {{" + tunnelDuration + "}}::cyan"))
	//cfmt.Println("{{✔}}::green Tunnel created at {{" + tunnel.URL() + "}}::cyan with an expiration of {{" + tunnelDuration + "}}::cyan")

	tunnelTimeout, err := time.ParseDuration(tunnelDuration)
	if err != nil {
		_, _ = cfmt.Println("{{✗}}::red Error parsing timeout")
		os.Exit(1)
	}

	time.Sleep(tunnelTimeout * time.Second)

}
0707010000002B000081A400000000000000000000000167BEEE0500003385000000000000000000000000000000000000002800000000kubero-cli-2.4.7/cmd/kuberoCli/types.gopackage kuberoCli

import (
	"gorm.io/gorm"
	"kubero/pkg/kuberoApi"
	"time"
)

type Pipeline struct {
	gorm.Model
	BuildPack struct {
		Build struct {
			Command    string `json:"command" gorm:"column:command"`
			Repository string `json:"repository" gorm:"column:repository"`
			Tag        string `json:"tag" gorm:"column:tag"`
		} `json:"build" gorm:"embedded"`
		Fetch struct {
			Repository string `json:"repository" gorm:"column:repository"`
			Tag        string `json:"tag" gorm:"column:tag"`
		} `json:"fetch" gorm:"embedded"`
		Language string `json:"language" gorm:"column:language"`
		Name     string `json:"name" gorm:"column:name"`
		Run      struct {
			Command    string `json:"command" gorm:"column:command"`
			Repository string `json:"repository" gorm:"column:repository"`
			Tag        string `json:"tag" gorm:"column:tag"`
		} `json:"run" gorm:"embedded"`
	} `json:"buildpack" gorm:"embedded"`
	DeploymentStrategy string `json:"deploymentstrategy" gorm:"column:deploymentstrategy"`
	DockerImage        string `json:"dockerimage" gorm:"column:dockerimage"`
	Git                struct {
		Keys struct {
			ReadOnly bool   `json:"read_only" gorm:"column:read_only"`
			Title    string `json:"title" gorm:"column:title"`
			URL      string `json:"url" gorm:"column:url"`
			Verified bool   `json:"verified" gorm:"column:verified"`
		} `json:"keys" gorm:"embedded"`
		Repository struct {
			Admin         bool   `json:"admin" gorm:"column:admin"`
			CloneURL      string `json:"clone_url" gorm:"column:clone_url"`
			DefaultBranch string `json:"default_branch" gorm:"column:default_branch"`
			Description   string `json:"description" gorm:"column:description"`
			Homepage      string `json:"homepage" gorm:"column:homepage"`
			ID            int    `json:"id" gorm:"column:id"`
			Language      string `json:"language" gorm:"column:language"`
			Name          string `json:"name" gorm:"column:name"`
			NodeID        string `json:"node_id" gorm:"column:node_id"`
			Owner         string `json:"owner" gorm:"column:owner"`
			Private       bool   `json:"private" gorm:"column:private"`
			Push          bool   `json:"push" gorm:"column:push"`
			SshUrl        string `json:"ssh_url" gorm:"column:ssh_url"`
			Visibility    string `json:"visibility" gorm:"column:visibility"`
		} `json:"repository" gorm:"embedded"`
		Webhook struct {
			Active    bool      `json:"active" gorm:"column:active"`
			CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
			Events    []string  `json:"events" gorm:"column:events"`
			Insecure  string    `json:"insecure" gorm:"column:insecure"`
			URL       string    `json:"url" gorm:"column:url"`
		} `json:"webhook" gorm:"embedded"`
	} `json:"git" gorm:"embedded"`
	Name       string  `json:"name" gorm:"column:name"`
	Phases     []Phase `json:"phases" gorm:"foreignKey:PipelineID"`
	ReviewApps bool    `json:"reviewapps" gorm:"column:reviewapps"`
}

type Phase struct {
	gorm.Model
	PipelineID uint   `gorm:"column:pipeline_id"`
	Context    string `json:"context" gorm:"column:context"`
	Enabled    bool   `json:"enabled" gorm:"column:enabled"`
	Name       string `json:"name" gorm:"column:name"`
	Apps       []App  `json:"apps" gorm:"foreignKey:PhaseID"`
}

type PipelinesList struct {
	Items []Pipeline `json:"items"`
}

type Contexts []struct {
	Cluster string `json:"cluster"`
	Name    string `json:"name"`
	User    string `json:"user"`
}

type Repositories struct {
	Github    bool `json:"github"`
	Gitea     bool `json:"gitea"`
	Gitlab    bool `json:"gitlab"`
	Bitbucket bool `json:"bitbucket"`
	Docker    bool `json:"docker"`
}

type App struct {
	gorm.Model
	PhaseID     uint          `gorm:"column:phase_id"`
	Addons      []interface{} `json:"addons" gorm:"-"`
	Autodeploy  bool          `json:"autodeploy" gorm:"column:autodeploy"`
	Autoscale   bool          `json:"autoscale" gorm:"column:autoscale"`
	Autoscaling struct {
		Enabled bool `json:"enabled" gorm:"column:enabled"`
	} `json:"autoscaling" gorm:"embedded"`
	Branch             string        `json:"branch" gorm:"column:branch"`
	CronJobs           []interface{} `json:"cronjobs" gorm:"-"`
	DeploymentStrategy string        `json:"deploymentstrategy" gorm:"column:deploymentstrategy"`
	Domain             string        `json:"domain" gorm:"column:domain"`
	EnvVars            []interface{} `json:"envVars" gorm:"-"`
	FullnameOverride   string        `json:"fullnameOverride" gorm:"column:fullnameOverride"`
	Gitrepo            struct {
		Admin         bool   `json:"admin" gorm:"column:admin"`
		CloneURL      string `json:"clone_url" gorm:"column:clone_url"`
		DefaultBranch string `json:"default_branch" gorm:"column:default_branch"`
		Description   string `json:"description" gorm:"column:description"`
		Homepage      string `json:"homepage" gorm:"column:homepage"`
		ID            int    `json:"id" gorm:"column:id"`
		Language      string `json:"language" gorm:"column:language"`
		Name          string `json:"name" gorm:"column:name"`
		NodeID        string `json:"node_id" gorm:"column:node_id"`
		Owner         string `json:"owner" gorm:"column:owner"`
		Private       bool   `json:"private" gorm:"column:private"`
		Push          bool   `json:"push" gorm:"column:push"`
		SshUrl        string `json:"ssh_url" gorm:"column:ssh_url"`
		Visibility    string `json:"visibility" gorm:"column:visibility"`
	} `json:"gitrepo" gorm:"embedded"`
	Image struct {
		Build struct {
			Command    string `json:"command" gorm:"column:command"`
			Repository string `json:"repository" gorm:"column:repository"`
			Tag        string `json:"tag" gorm:"column:tag"`
		} `json:"build" gorm:"embedded"`
		ContainerPort int `json:"containerPort" gorm:"column:containerPort"`
		Fetch         struct {
			Repository string `json:"repository" gorm:"column:repository"`
			Tag        string `json:"tag" gorm:"column:tag"`
		} `json:"fetch" gorm:"embedded"`
		PullPolicy string `json:"pullPolicy" gorm:"column:pullPolicy"`
		Repository string `json:"repository" gorm:"column:repository"`
		Run        struct {
			Command    string `json:"command" gorm:"column:command"`
			Repository string `json:"repository" gorm:"column:repository"`
			Tag        string `json:"tag" gorm:"column:tag"`
		} `json:"run" gorm:"embedded"`
		Tag string `json:"tag" gorm:"column:tag"`
	} `json:"image" gorm:"embedded"`
	ImagePullSecrets []interface{} `json:"imagePullSecrets" gorm:"-"`
	Ingress          struct {
		Annotations struct {
		} `json:"annotations" gorm:"-"`
		ClassName string `json:"className" gorm:"column:className"`
		Enabled   bool   `json:"enabled" gorm:"column:enabled"`
		Hosts     []struct {
			Host  string `json:"host" gorm:"column:host"`
			Paths []struct {
				Path     string `json:"path" gorm:"column:path"`
				PathType string `json:"pathType" gorm:"column:pathType"`
			} `json:"paths" gorm:"-"`
		} `json:"hosts" gorm:"-"`
		TLS []interface{} `json:"tls" gorm:"-"`
	} `json:"ingress" gorm:"embedded"`
	Name         string `json:"name" gorm:"column:name"`
	NameOverride string `json:"nameOverride" gorm:"column:nameOverride"`
	NodeSelector struct {
	} `json:"nodeSelector" gorm:"-"`
	Phase          string `json:"phase" gorm:"column:phase"`
	Pipeline       string `json:"pipeline" gorm:"column:pipeline"`
	PodAnnotations struct {
	} `json:"podAnnotations" gorm:"-"`
	PodSecurityContext struct {
	} `json:"podSecurityContext" gorm:"-"`
	PodSize      string `json:"podsize" gorm:"column:podsize"`
	ReplicaCount int    `json:"replicaCount" gorm:"column:replicaCount"`
	Service      struct {
		Port int    `json:"port" gorm:"column:port"`
		Type string `json:"type" gorm:"column:type"`
	} `json:"service" gorm:"embedded"`
	ServiceAccount struct {
		Annotations struct {
		} `json:"annotations" gorm:"-"`
		Create bool   `json:"create" gorm:"column:create"`
		Name   string `json:"name" gorm:"column:name"`
	} `json:"serviceAccount" gorm:"embedded"`
	Tolerations []interface{} `json:"tolerations" gorm:"-"`
	Web         Web           `json:"web" gorm:"embedded"`
	Worker      Worker        `json:"worker" gorm:"embedded"`
}

type Web struct {
	Autoscaling struct {
		MaxReplicas                       int `json:"maxReplicas" gorm:"column:maxReplicas"`
		MinReplicas                       int `json:"minReplicas" gorm:"column:minReplicas"`
		TargetCPUUtilizationPercentage    int `json:"targetCPUUtilizationPercentage" gorm:"column:targetCPUUtilizationPercentage"`
		TargetMemoryUtilizationPercentage int `json:"targetMemoryUtilizationPercentage" gorm:"column:targetMemoryUtilizationPercentage"`
	} `json:"autoscaling" gorm:"embedded"`
	ReplicaCount int `json:"replicaCount" gorm:"column:replicaCount"`
}

type Worker struct {
	Autoscaling struct {
		MaxReplicas                       int `json:"maxReplicas" gorm:"column:maxReplicas"`
		MinReplicas                       int `json:"minReplicas" gorm:"column:minReplicas"`
		TargetCPUUtilizationPercentage    int `json:"targetCPUUtilizationPercentage" gorm:"column:targetCPUUtilizationPercentage"`
		TargetMemoryUtilizationPercentage int `json:"targetMemoryUtilizationPercentage" gorm:"column:targetMemoryUtilizationPercentage"`
	} `json:"autoscaling" gorm:"embedded"`
	ReplicaCount int `json:"replicaCount" gorm:"column:replicaCount"`
}

type Addon struct {
	gorm.Model
	ID      string `json:"id" gorm:"column:id"`
	Enabled bool   `json:"enabled" gorm:"column:enabled"`
	Version struct {
		Latest    string `json:"latest" gorm:"column:latest"`
		Installed string `json:"installed" gorm:"column:installed"`
	} `json:"version,omitempty" gorm:"embedded"`
	Description string `json:"description,omitempty" gorm:"column:description"`
	Readme      string `json:"readme,omitempty" gorm:"column:readme"`
	ArtifactURL string `json:"artifact_url" gorm:"column:artifact_url"`
	Kind        string `json:"kind" gorm:"column:kind"`
	Install     string `json:"install" gorm:"column:install"`
	Beta        bool   `json:"beta" gorm:"column:beta"`
}

type buildPacks []struct {
	Name     string `json:"name" gorm:"column:name"`
	Language string `json:"language" gorm:"column:language"`
	Fetch    struct {
		Repository string `json:"repository" gorm:"column:repository"`
		Tag        string `json:"tag" gorm:"column:tag"`
	} `json:"fetch" gorm:"embedded"`
	Build struct {
		Repository string `json:"repository" gorm:"column:repository"`
		Tag        string `json:"tag" gorm:"column:tag"`
		Command    string `json:"command" gorm:"column:command"`
	} `json:"build" gorm:"embedded"`
	Run struct {
		Repository         string `json:"repository" gorm:"column:repository"`
		Tag                string `json:"tag" gorm:"column:tag"`
		ReadOnlyAppStorage bool   `json:"readOnlyAppStorage" gorm:"column:readOnlyAppStorage"`
		SecurityContext    *struct {
			AllowPrivilegeEscalation *bool `json:"allowPrivilegeEscalation" gorm:"column:allowPrivilegeEscalation"`
			ReadOnlyRootFilesystem   *bool `json:"readOnlyRootFilesystem" gorm:"column:readOnlyRootFilesystem"`
		} `json:"securityContext" gorm:"embedded"`
		Command string `json:"command" gorm:"column:command"`
	} `json:"run,omitempty" gorm:"embedded"`
}

type PodSize struct {
	Name        string `json:"name" gorm:"column:name"`
	Description string `json:"description" gorm:"column:description"`
	Default     bool   `json:"default,omitempty" gorm:"column:default"`
	Resources   struct {
		Requests struct {
			Memory string `json:"memory" gorm:"column:memory"`
			CPU    string `json:"cpu" gorm:"column:cpu"`
		} `json:"requests" gorm:"embedded"`
		Limits struct {
			Memory string `json:"memory" gorm:"column:memory"`
			CPU    string `json:"cpu" gorm:"column:cpu"`
		} `json:"limits,omitempty" gorm:"embedded"`
	} `json:"resources,omitempty" gorm:"embedded"`
	Active bool `json:"active,omitempty" gorm:"column:active"`
}

type pipelinesConfigsList map[string]kuberoApi.PipelineCRD

type appShort struct {
	Name     string `json:"name" gorm:"column:name"`
	Phase    string `json:"phase" gorm:"column:phase"`
	Pipeline string `json:"pipeline" gorm:"column:pipeline"`
}

type Instance struct {
	gorm.Model
	Name       string `json:"-" yaml:"-"`
	ApiUrl     string `json:"apiurl" yaml:"apiurl" gorm:"column:apiurl"`
	IacBaseDir string `json:"iacBaseDir,omitempty" yaml:"iacBaseDir,omitempty" gorm:"column:iacBaseDir"`
	ConfigPath string `json:"-" yaml:"-"`
	Tunnel     struct {
		Subdomain string `json:"subdomain" yaml:"subdomain" gorm:"column:subdomain"`
		Port      int    `json:"port" yaml:"port" gorm:"column:port"`
		Host      string `json:"host" yaml:"host" gorm:"column:host"`
	} `json:"tunnel,omitempty" yaml:"tunnel,omitempty" gorm:"embedded"`
}

type Config struct {
	gorm.Model
	Api struct {
		Url   string `json:"url" yaml:"url" gorm:"column:url"`
		Token string `json:"token" yaml:"token" gorm:"column:token"`
	} `json:"api" yaml:"api" gorm:"embedded"`
}

type GithubVersion struct {
	gorm.Model
	Name       string `json:"name" gorm:"column:name"`
	ZipballUrl string `json:"zipball_url" gorm:"column:zipball_url"`
	TarballURL string `json:"tarball_url" gorm:"column:tarball_url"`
	Commit     struct {
		Sha string `json:"sha" gorm:"column:sha"`
		URL string `json:"url" gorm:"column:url"`
	} `json:"commit" gorm:"embedded"`
	NodeID string `json:"node_id" gorm:"column:node_id"`
}
0707010000002C000081A400000000000000000000000167BEEE050000096F000000000000000000000000000000000000002500000000kubero-cli-2.4.7/cmd/kuberoCli/up.gopackage kuberoCli

import (
	"github.com/i582/cfmt/cmd/cfmt"
	"github.com/spf13/cobra"
)

// upCmd represents the up command
var upCmd = &cobra.Command{
	Use:     "up",
	Aliases: []string{"deploy", "dp"},
	Short:   "Deploy your pipelines and apps to the cluster",
	Long:    ``,
	Run: func(cmd *cobra.Command, args []string) {
		if pipelineName != "" && appName == "" {

			pipelinesList := getAllLocalPipelines()
			ensurePipelineIsSet(pipelinesList)
			upPipeline()
		} else if appName != "" {

			pipelinesList := getAllLocalPipelines()
			ensurePipelineIsSet(pipelinesList)
			upApp()
		} else {
			upAllPipelines()
		}
	},
}

func init() {
	rootCmd.AddCommand(upCmd)
	upCmd.Flags().StringVarP(&pipelineName, "pipeline", "p", "", "name of the pipeline")
	upCmd.Flags().StringVarP(&stageName, "stage", "s", "", "Name of the stage [test|stage|production]")
	upCmd.Flags().StringVarP(&appName, "app", "a", "", "name of the app")
	upCmd.PersistentFlags().BoolVarP(&force, "force", "f", false, "Skip asking for confirmation")
}

func upPipeline() {
	confirmationLine("Are you sure you want to deploy the pipeline '"+pipelineName+"'?", "y")

	pipeline := loadLocalPipeline(pipelineName)
	_, deployPipelineErr := api.DeployPipeline(pipeline)
	if deployPipelineErr != nil {
		_, _ = cfmt.Println("{{Error deploying pipeline}}::red", deployPipelineErr)
		return
	}
}

func upApp() {
	confirmationLine("Are you sure you want to deploy the app "+appName+" to "+pipelineName+"?", "y")
	app := loadLocalApp(pipelineName, stageName, appName)
	app.Spec.Pipeline = pipelineName             // ensure pipeline is set
	app.Spec.Phase = stageName                   // ensure stage is set
	app.Spec.Security.VulnerabilityScans = false // TODO: ask for this
	_, DeployAppErr := api.DeployApp(app)
	if DeployAppErr != nil {
		_, _ = cfmt.Println("{{Error deploying app}}::red", DeployAppErr)
		return
	}
}

func upAllPipelines() {

	confirmationLine("Are you sure you want to deploy all pipelines?", "y")
	pipelinesConfigs := loadAllLocalPipelines()

	_, _ = cfmt.Println("{{Deploying all pipelines}}::yellow")

	for _, pipelineCRD := range pipelinesConfigs {
		_, _ = cfmt.Println("{{Deploying pipeline}}::yellow " + pipelineCRD.Spec.Name + "")
		_, deployPipelineErr := api.DeployPipeline(pipelineCRD)
		if deployPipelineErr != nil {
			_, _ = cfmt.Println("{{Error deploying pipeline}}::red", deployPipelineErr)
			return
		}
	}
}
0707010000002D000081A400000000000000000000000167BEEE0500000405000000000000000000000000000000000000002800000000kubero-cli-2.4.7/cmd/kuberoCli/upApp.gopackage kuberoCli

/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/

import (
	"os"

	"github.com/i582/cfmt/cmd/cfmt"
	"github.com/spf13/cobra"
)

// appCmd represents the app command
var upAppCmd = &cobra.Command{
	Use:   "app",
	Short: "Deploy an apps to the cluster",
	Long:  `Use the app subcommand to deploy your apps to the cluster`,
	Run: func(cmd *cobra.Command, args []string) {

		pipelinesList := getAllLocalPipelines()
		ensurePipelineIsSet(pipelinesList)

		ensureStageNameIsSet()

		appsList := getAllLocalApps()
		if len(appsList) == 0 {
			_, _ = cfmt.Println("\n{{ERROR:}}::red No apps found in pipeline '" + pipelineName + "'")
			os.Exit(1)
		}
		ensureAppNameIsSelected(appsList)
		upApp()
	},
}

func init() {
	upCmd.AddCommand(upAppCmd)
	upAppCmd.Flags().StringVarP(&pipelineName, "pipeline", "p", "", "name of the pipeline")
	upAppCmd.Flags().StringVarP(&stageName, "stage", "s", "", "Name of the stage [test|stage|production]")
	upAppCmd.Flags().StringVarP(&appName, "app", "a", "", "name of the app")
}
0707010000002E000081A400000000000000000000000167BEEE0500000293000000000000000000000000000000000000002D00000000kubero-cli-2.4.7/cmd/kuberoCli/upPipeline.gopackage kuberoCli

/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/

import (
	"github.com/spf13/cobra"
)

// pipelineCmd represents the pipeline command
var upPipelineCmd = &cobra.Command{
	Use:     "pipeline",
	Aliases: []string{"pl"},
	Short:   "Deploy a pipeline to the cluster",
	Long:    `Use the pipeline subcommand to deploy your pipelines to the cluster`,
	Run: func(cmd *cobra.Command, args []string) {

		pipelinesList := getAllLocalPipelines()
		ensurePipelineIsSet(pipelinesList)
		upPipeline()
	},
}

func init() {
	upCmd.AddCommand(upPipelineCmd)
	upPipelineCmd.Flags().StringVarP(&pipelineName, "pipeline", "p", "", "name of the pipeline")
}
0707010000002F000081A400000000000000000000000167BEEE05000009AC000000000000000000000000000000000000002800000000kubero-cli-2.4.7/cmd/kuberoCli/usage.gopackage kuberoCli

import (
	"github.com/fatih/color"
	"github.com/spf13/cobra"
)

func colorYellow(s string) string {
	return color.New(color.FgYellow).SprintFunc()(s)
}

func colorGreen(s string) string {
	return color.New(color.FgGreen).SprintFunc()(s)
}

func colorBlue(s string) string {
	return color.New(color.FgBlue).SprintFunc()(s)
}

func colorRed(s string) string {
	return color.New(color.FgRed).SprintFunc()(s)
}

func colorHelp(s string) string {
	return color.New(color.FgCyan).SprintFunc()(s)
}

func hasServiceCommands(cmds []*cobra.Command) bool {
	for _, cmd := range cmds {
		if cmd.Annotations["service"] == "true" {
			return true
		}
	}
	return false
}

func hasModuleCommands(cmds []*cobra.Command) bool {
	for _, cmd := range cmds {
		if cmd.Annotations["service"] != "true" {
			return true
		}
	}
	return false
}

var cliUsageTemplate = `{{colorYellow "Usage:"}}{{if .Runnable}}
  {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
  {{.CommandPath}} [command] [args]{{end}}{{if gt (len .Aliases) 0}}

{{colorYellow "Aliases:"}}
  {{.NameAndAliases}}{{end}}{{if .HasExample}}

{{colorYellow "Example:"}}
  {{.Example}}{{end}}{{if .HasAvailableSubCommands}}

{{colorYellow "Available Commands:"}}
{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
  {{colorGreen (rpad .Name .NamePadding) }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}

{{colorYellow "Flags:"}}
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces | colorHelp}}{{end}}{{if .HasAvailableInheritedFlags}}

{{colorYellow "Global Options:"}}
  {{.InheritedFlags.FlagUsages | trimTrailingWhitespaces | colorHelp}}{{end}}{{if .HasHelpSubCommands}}

{{colorYellow "Additional help topics:"}}
{{range .Commands}}{{if .IsHelpCommand}}
  {{colorGreen (rpad .CommandPath .CommandPathPadding) }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasSubCommands}}

{{colorYellow (printf "Use \"%s [command] --help\" for more information about a command." .CommandPath)}}{{end}}
`

func SetUsageDefinition(cmd *cobra.Command) {
	cobra.AddTemplateFunc("colorYellow", colorYellow)
	cobra.AddTemplateFunc("colorGreen", colorGreen)
	cobra.AddTemplateFunc("colorRed", colorRed)
	cobra.AddTemplateFunc("colorBlue", colorBlue)
	cobra.AddTemplateFunc("colorHelp", colorHelp)
	cobra.AddTemplateFunc("hasServiceCommands", hasServiceCommands)
	cobra.AddTemplateFunc("hasModuleCommands", hasModuleCommands)

	// Altera o template de uso do cobra
	cmd.SetUsageTemplate(cliUsageTemplate)
}
07070100000030000081A400000000000000000000000167BEEE05000000C4000000000000000000000000000000000000001D00000000kubero-cli-2.4.7/cmd/main.go/*
Copyright © 2022 NAME HERE <EMAIL ADDRESS>
*/
package main

import "kubero/cmd/kuberoCli"

func main() {

	kuberoCli.Execute()
}

// var configPath = os.Getenv("HOME") + "/.config/kubero-cli"
07070100000031000081A400000000000000000000000167BEEE0500001050000000000000000000000000000000000000001800000000kubero-cli-2.4.7/go.modmodule kubero

go 1.23.0

toolchain go1.23.5

require (
	github.com/go-git/go-billy/v5 v5.6.2
	github.com/go-git/go-git/v5 v5.13.2
	github.com/go-resty/resty/v2 v2.16.5
	github.com/i582/cfmt v1.4.0
	github.com/jonasfj/go-localtunnel v0.0.0-20170326223115-8a804488f275
	github.com/leaanthony/spinner v0.5.4
	github.com/olekukonko/tablewriter v0.0.5
	github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
	github.com/spf13/cobra v1.8.1
	github.com/spf13/viper v1.19.0
	gopkg.in/yaml.v3 v3.0.1
	gorm.io/driver/sqlite v1.5.7
	gorm.io/gorm v1.25.12
)

require google.golang.org/protobuf v1.36.4 // indirect

require (
	dario.cat/mergo v1.0.1 // indirect
	github.com/AlecAivazis/survey/v2 v2.3.7
	github.com/Microsoft/go-winio v0.6.2 // indirect
	github.com/ProtonMail/go-crypto v1.1.5 // indirect
	github.com/cloudflare/circl v1.5.0 // indirect
	github.com/cyphar/filepath-securejoin v0.4.0 // indirect
	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
	github.com/emirpasic/gods v1.18.1 // indirect
	github.com/fatih/color v1.18.0
	github.com/fsnotify/fsnotify v1.8.0 // indirect
	github.com/fxamacker/cbor/v2 v2.7.0 // indirect
	github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
	github.com/go-logr/logr v1.4.2 // indirect
	github.com/gogo/protobuf v1.3.2 // indirect
	github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
	github.com/google/gofuzz v1.2.0 // indirect
	github.com/gookit/color v1.5.4 // indirect
	github.com/hashicorp/hcl v1.0.0 // indirect
	github.com/inconshreveable/mousetrap v1.1.0 // indirect
	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
	github.com/jinzhu/inflection v1.0.0 // indirect
	github.com/jinzhu/now v1.1.5 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
	github.com/kevinburke/ssh_config v1.2.0 // indirect
	github.com/leaanthony/synx v0.1.0 // indirect
	github.com/leaanthony/wincursor v0.1.0 // indirect
	github.com/magiconair/properties v1.8.9 // indirect
	github.com/mattn/go-colorable v0.1.14 // indirect
	github.com/mattn/go-isatty v0.0.20 // indirect
	github.com/mattn/go-runewidth v0.0.16 // indirect
	github.com/mattn/go-sqlite3 v1.14.24 // indirect
	github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
	github.com/mitchellh/mapstructure v1.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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
	github.com/pelletier/go-toml/v2 v2.2.3 // indirect
	github.com/pjbgf/sha1cd v0.3.2 // indirect
	github.com/rivo/uniseg v0.4.7 // indirect
	github.com/sagikazarmark/locafero v0.7.0 // indirect
	github.com/sagikazarmark/slog-shim v0.1.0 // indirect
	github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
	github.com/skeema/knownhosts v1.3.0 // indirect
	github.com/sourcegraph/conc v0.3.0 // indirect
	github.com/spf13/afero v1.12.0 // indirect
	github.com/spf13/cast v1.7.1 // indirect
	github.com/spf13/pflag v1.0.6
	github.com/subosito/gotenv v1.6.0 // indirect
	github.com/x448/float16 v0.8.4 // indirect
	github.com/xanzy/ssh-agent v0.3.3 // indirect
	github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
	go.uber.org/multierr v1.11.0 // indirect
	golang.org/x/crypto v0.34.0
	golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
	golang.org/x/net v0.34.0 // indirect
	golang.org/x/oauth2 v0.25.0 // indirect
	golang.org/x/sys v0.30.0 // indirect
	golang.org/x/term v0.29.0 // indirect
	golang.org/x/text v0.22.0 // indirect
	golang.org/x/time v0.9.0 // indirect
	gopkg.in/inf.v0 v0.9.1 // indirect
	gopkg.in/ini.v1 v1.67.0 // indirect
	gopkg.in/warnings.v0 v0.1.2 // indirect
	k8s.io/apimachinery v0.32.2 // indirect
	k8s.io/client-go v0.32.2
	k8s.io/klog/v2 v2.130.1 // indirect
	k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
	sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
	sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
	sigs.k8s.io/yaml v1.4.0 // indirect
)
07070100000032000081A400000000000000000000000167BEEE05000072A3000000000000000000000000000000000000001800000000kubero-cli-2.4.7/go.sumdario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
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/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyphar/filepath-securejoin v0.4.0 h1:PioTG9TBRSApBpYGnDU8HC+miIsX8vitBH9LGNNMoLQ=
github.com/cyphar/filepath-securejoin v0.4.0/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
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/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM=
github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ=
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/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
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/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0=
github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A=
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-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.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
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-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
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.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/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.3.2/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
github.com/i582/cfmt v1.4.0 h1:DNugs+dvy3xjJSUk9Oita0udy1YVQh2vDP6cWYhDCIQ=
github.com/i582/cfmt v1.4.0/go.mod h1:tpHWAxhE4Y7yy7sliaNe0pnnEs1SZe67KLljyOlEYI8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jonasfj/go-localtunnel v0.0.0-20170326223115-8a804488f275 h1:CHhhnqQGmniRCgMx34aIcqasiKme7gsscUYO/2rMFnk=
github.com/jonasfj/go-localtunnel v0.0.0-20170326223115-8a804488f275/go.mod h1:2jVFHClLG6ANLzONRisiL5vkbUOd82KNdQhdW4vykgY=
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.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
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/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/leaanthony/spinner v0.5.4 h1:XA2ElQgqwCg3gkTR6bJD+amKZ/VU0Ou94Vhv3W+GQug=
github.com/leaanthony/spinner v0.5.4/go.mod h1:oHlrvWicr++CVV7ALWYi+qHk/XNA91D9IJ48IqmpVUo=
github.com/leaanthony/synx v0.1.0 h1:R0lmg2w6VMb8XcotOwAe5DLyzwjLrskNkwU7LLWsyL8=
github.com/leaanthony/synx v0.1.0/go.mod h1:Iz7eybeeG8bdq640iR+CwYb8p+9EOsgMWghkSRyZcqs=
github.com/leaanthony/wincursor v0.1.0 h1:Dsyp68QcF5cCs65AMBmxoYNEm0n8K7mMchG6a8fYxf8=
github.com/leaanthony/wincursor v0.1.0/go.mod h1:7TVwwrzSH/2Y9gLOGH+VhA+bZhoWXBRgbGNTMk+yimE=
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
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.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
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-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
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.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
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 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
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/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
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/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
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.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/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/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/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
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/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
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/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=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
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.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.34.0 h1:+/C6tk6rf/+t5DhUketUbD1aNGqiSX3j15Z6xuIDlBA=
golang.org/x/crypto v0.34.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
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/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
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.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
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/sys v0.0.0-20181228144115-9a3f9b0469bb/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-20190222072716-a9d3bda3a223/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-20191026070338-33540a1f6037/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-20210423082822-04245dca01da/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-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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
golang.org/x/time v0.9.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-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/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/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
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/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw=
k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y=
k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ=
k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
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/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/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0=
k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk=
sigs.k8s.io/structured-merge-diff/v4 v4.5.0/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=
07070100000033000081ED00000000000000000000000167BEEE0500000A7A000000000000000000000000000000000000001C00000000kubero-cli-2.4.7/install.sh#!/usr/bin/env bash

################################################################################
# This Script is used to install kubero-cli binaries.                          #
#                                                                              #
# Supported OS: Linux, macOS ---> Windows(not supported)                       #
# Supported Architecture: amd64, arm64                                         #
# Source: https://github.com/kubero-dev/kubero-cli                             #
# Binary Release: https://github.com/kubero-dev/kubero-cli/releases/latest     #
# License: Apache License 2.0                                                  #
# Usage:                                                                       #
#   curl -fsSL get.kubero.dev | bash                                           #
#   curl -fsSL get.kubero.dev | bash -s -- v1.10.0                             #
#   bash <(curl -fsSL get.kubero.dev) v1.9.2                                   #
################################################################################

set -eo pipefail
[[ $TRACE ]] && set -x

get_os() {
    case "$(uname -s)" in
        Linux*) echo "linux" ;;
        Darwin*) echo "darwin" ;;
        *) echo "unsupported" ;;
    esac
}

get_arch() {
    case "$(uname -m)" in
        x86_64) echo "amd64" ;;
        arm*|aarch64) echo "arm64" ;;
        *) echo "unsupported" ;;
    esac
}

os=$(get_os)
arch=$(get_arch)
version=${1:-latest}

if [[ "$os" == "unsupported" || "$arch" == "unsupported" ]]; then
    echo "Unsupported OS or architecture."
    exit 1
fi

if [[ -f "/usr/local/bin/kubero" ]]; then
    read -r -p "Do you want to replace it? [y/n] " replaceBinary
    [[ "$replaceBinary" != "y" && "$replaceBinary" != "" ]] && echo "Aborting installation." && exit 1
fi

release_url="https://github.com/kubero-dev/kubero-cli/releases/${version}/download/kubero-cli_${os}_${arch}.tar.gz"
temp_dir=$(mktemp -d)

echo "Downloading ${release_url} ..."
curl -L -s -o "${temp_dir}/kubero-cli.tar.gz" "$release_url" || { echo "Failed to download the binary."; rm -rf "$temp_dir"; exit 1; }

echo "Unpacking the binary..."
tar -xzvf "${temp_dir}/kubero-cli.tar.gz" -C "$temp_dir" || { echo "Failed to unpack the binary."; rm -rf "$temp_dir"; exit 1; }

[[ ! -f "${temp_dir}/kubero" ]] && { echo "Failed to unpack the binary."; rm -rf "$temp_dir"; exit 1; }

echo "Installing kubero in /usr/local/bin ..."
sudo mv "${temp_dir}/kubero" "/usr/local/bin/kubero" || { echo "Failed to install kubero."; rm -rf "$temp_dir"; exit 1; }

rm -rf "$temp_dir"
echo "Kubero has been successfully installed."
echo "Run 'kubero install' to create a kubernetes cluster."07070100000034000081A400000000000000000000000167BEEE05000000C0000000000000000000000000000000000000002500000000kubero-cli-2.4.7/kubero.yaml.exampleinstances:
    kubero-examlpe:
      iacBaseDir: .kubero/
      apiurl: http://localhost:80
      tunnel: 
        subdomain: kubero-local-dev-example
        port: 80
        host: localhost
07070100000035000041ED00000000000000000000000267BEEE0500000000000000000000000000000000000000000000001500000000kubero-cli-2.4.7/pkg07070100000036000041ED00000000000000000000000267BEEE0500000000000000000000000000000000000000000000001F00000000kubero-cli-2.4.7/pkg/kuberoApi07070100000037000081A400000000000000000000000167BEEE0500000003000000000000000000000000000000000000002700000000kubero-cli-2.4.7/pkg/kuberoApi/VERSIONdev07070100000038000081A400000000000000000000000167BEEE0500000B87000000000000000000000000000000000000002700000000kubero-cli-2.4.7/pkg/kuberoApi/main.gopackage kuberoApi

import (
	_ "embed"

	"github.com/go-resty/resty/v2"
)

type KuberoClient struct {
	baseURL     string
	bearerToken string
	client      *resty.Request
}

//go:embed VERSION
var version string

func (k *KuberoClient) Init(baseURL string, bearerToken string) *resty.Request {

	client := resty.New().SetBaseURL(baseURL).R().
		EnableTrace().
		SetAuthScheme("Bearer").
		SetAuthToken(bearerToken).
		SetHeader("Accept", "application/json").
		SetHeader("Content-Type", "application/json").
		SetHeader("User-Agent", "kubero-cli/"+version)

	k.baseURL = baseURL
	k.bearerToken = bearerToken
	k.client = client

	return client
}

func (k *KuberoClient) DeployPipeline(pipeline PipelineCRD) (*resty.Response, error) {
	k.client.SetBody(pipeline.Spec)
	res, err := k.client.Post("/api/cli/pipelines/")

	return res, err
}

func (k *KuberoClient) UnDeployPipeline(pipelineName string) (*resty.Response, error) {
	res, err := k.client.Delete("/api/cli/pipelines/" + pipelineName)

	return res, err
}

func (k *KuberoClient) GetPipeline(pipelineName string) (*resty.Response, error) {
	res, err := k.client.Get("/api/cli/pipelines/" + pipelineName)

	return res, err
}

func (k *KuberoClient) UnDeployApp(pipelineName string, stageName string, appName string) (*resty.Response, error) {
	res, err := k.client.Delete("/api/cli/pipelines/" + pipelineName + "/" + stageName + "/" + appName)

	return res, err
}

func (k *KuberoClient) GetApp(pipelineName string, stageName string, appName string) (*resty.Response, error) {
	res, err := k.client.Get("/api/cli/pipelines/" + pipelineName + "/" + stageName + "/" + appName)

	return res, err
}

func (k *KuberoClient) GetApps() (*resty.Response, error) {
	res, err := k.client.Get("/api/cli/apps")

	return res, err
}

func (k *KuberoClient) GetPipelines() (*resty.Response, error) {
	res, err := k.client.Get("/api/cli/pipelines")

	return res, err
}

func (k *KuberoClient) DeployApp(app AppCRD) (*resty.Response, error) {
	k.client.SetBody(app.Spec)
	res, err := k.client.Post("/api/cli/apps")

	return res, err
}

func (k *KuberoClient) GetPipelineApps(pipelineName string) (*resty.Response, error) {
	res, err := k.client.Get("/api/cli/pipelines/" + pipelineName + "/apps")

	return res, err
}

func (k *KuberoClient) GetAddons() (*resty.Response, error) {
	res, err := k.client.Get("/api/cli/addons")

	return res, err
}

func (k *KuberoClient) GetBuildpacks() (*resty.Response, error) {
	res, err := k.client.Get("/api/cli/config/buildpacks")

	return res, err
}

func (k *KuberoClient) GetPodsize() (*resty.Response, error) {
	res, err := k.client.Get("/api/cli/config/podsize")

	return res, err
}

func (k *KuberoClient) GetRepositories() (*resty.Response, error) {
	res, err := k.client.Get("/api/cli/config/repositories")

	return res, err
}

func (k *KuberoClient) GetContexts() (*resty.Response, error) {
	res, err := k.client.Get("/api/cli/config/k8s/context")

	return res, err
}
07070100000039000081A400000000000000000000000167BEEE0500001A32000000000000000000000000000000000000002800000000kubero-cli-2.4.7/pkg/kuberoApi/types.gopackage kuberoApi

import (
	"gorm.io/gorm"
	"time"
)

type Metadata struct {
	gorm.Model
	Name   string      `json:"name,omitempty" yaml:"name,omitempty"`
	Labels interface{} `json:"labels,omitempty" yaml:"labels,omitempty"`
}

type GitKeys struct {
	gorm.Model
	ReadOnly bool   `json:"read_only"`
	Title    string `json:"title"`
	URL      string `json:"url"`
	Verified bool   `json:"verified"`
}

type GitRepository struct {
	gorm.Model
	Provider      string `json:"provider"`
	Admin         bool   `json:"admin"`
	CloneURL      string `json:"clone_url"`
	DefaultBranch string `json:"default_branch"`
	Description   string `json:"description"`
	Homepage      string `json:"homepage"`
	Language      string `json:"language"`
	Name          string `json:"name"`
	NodeID        string `json:"node_id" yaml:"node_id"`
	Owner         string `json:"owner"`
	Private       bool   `json:"private"`
	Push          bool   `json:"push"`
	SshUrl        string `json:"ssh_url"`
	Visibility    string `json:"visibility"`
}

type GitWebhook struct {
	gorm.Model
	Active    bool      `json:"active"`
	CreatedAt time.Time `json:"created_at"`
	Events    []string  `json:"events"`
	Insecure  string    `json:"insecure"`
	URL       string    `json:"url"`
}

type Git struct {
	gorm.Model
	Keys       GitKeys       `json:"keys" gorm:"embedded"`
	Repository GitRepository `json:"repository" gorm:"embedded"`
	Webhook    GitWebhook    `json:"webhook" gorm:"embedded"`
}

type Build struct {
	gorm.Model
	Command    string `json:"command"`
	Repository string `json:"repository"`
	Tag        string `json:"tag"`
}

type Fetch struct {
	gorm.Model
	Repository string `json:"repository"`
	Tag        string `json:"tag"`
}

type Run struct {
	gorm.Model
	Command    string `json:"command"`
	Repository string `json:"repository"`
	Tag        string `json:"tag"`
}

type Buildpack struct {
	gorm.Model
	Build    Build  `json:"build" gorm:"embedded"`
	Fetch    Fetch  `json:"fetch" gorm:"embedded"`
	Language string `json:"language"`
	Name     string `json:"name"`
	Run      Run    `json:"run" gorm:"embedded"`
}

type Phase struct {
	gorm.Model
	Name    string `json:"name"`
	Enabled bool   `json:"enabled"`
	Context string `json:"context"`
}

type PipelineSpec struct {
	gorm.Model
	Buildpack          Buildpack `json:"buildpack" gorm:"embedded"`
	DeploymentStrategy string    `json:"deploymentstrategy"`
	Domain             string    `json:"domain"`
	DockerImage        string    `json:"dockerimage,omitempty" yaml:"dockerimage,omitempty"`
	Git                Git       `json:"git,omitempty" yaml:"git,omitempty" gorm:"embedded"`
	Name               string    `json:"pipelineName" yaml:"pipelineName"`
	Phases             []Phase   `json:"phases" gorm:"foreignKey:PipelineID"`
	ReviewApps         bool      `json:"reviewapps"`
}

type PipelineCRD struct {
	gorm.Model
	APIVersion string       `json:"apiVersion" yaml:"apiVersion"`
	Kind       string       `json:"kind"`
	Metadata   Metadata     `json:"metadata,omitempty" yaml:"metadata,omitempty"`
	Spec       PipelineSpec `json:"spec" gorm:"embedded"`
}

type AppSpec struct {
	gorm.Model
	Autodeploy  bool `json:"autodeploy"`
	Autoscale   bool `json:"autoscale"`
	Autoscaling struct {
		Enabled bool `json:"enabled"`
	} `json:"autoscaling" gorm:"embedded"`
	Branch           string        `json:"branch"`
	Buildpack        string        `json:"buildpack"`
	Domain           string        `json:"domain"`
	FullnameOverride string        `json:"fullnameOverride" yaml:"fullnameOverride"`
	Gitrepo          GitRepository `json:"gitrepo" gorm:"embedded"`
	Image            struct {
		Fetch struct {
			Repository string `json:"repository"`
			Tag        string `json:"tag"`
		} `json:"fetch" gorm:"embedded"`
		Build struct {
			Command    string `json:"command"`
			Repository string `json:"repository"`
			Tag        string `json:"tag"`
		} `json:"build" gorm:"embedded"`
		Run struct {
			Command    string `json:"command"`
			Repository string `json:"repository"`
			Tag        string `json:"tag"`
		} `json:"run" gorm:"embedded"`
		ContainerPort int    `json:"containerPort"`
		PullPolicy    string `json:"pullPolicy" yaml:"pullPolicy"`
		Repository    string `json:"repository"`
		Tag           string `json:"tag"`
	} `json:"image" gorm:"embedded"`
	Ingress struct {
		ClassName string `json:"className"`
		Enabled   bool   `json:"enabled"`
	} `json:"ingress" gorm:"embedded"`
	Name         string `json:"appname" yaml:"appname"`
	NameOverride string `json:"nameOverride" yaml:"nameOverride"`
	Phase        string `json:"phase"`
	Pipeline     string `json:"pipeline"`
	PodSize      string `json:"podsize"`
	ReplicaCount int    `json:"replicaCount" yaml:"replicaCount"`
	Service      struct {
		Port int    `json:"port"`
		Type string `json:"type"`
	} `json:"service" gorm:"embedded"`
	ServiceAccount struct {
		Create bool   `json:"create"`
		Name   string `json:"name"`
	} `json:"serviceAccount" yaml:"serviceAccount" gorm:"embedded"`
	EnvVars  []string `json:"envVars" gorm:"-"`
	Web      Web      `json:"web" gorm:"embedded"`
	Worker   Worker   `json:"worker" gorm:"embedded"`
	Security struct {
		VulnerabilityScans bool `json:"vulnerabilityScans,omitempty" yaml:"vulnerabilityScans,omitempty"`
	} `json:"security,omitempty" yaml:"security,omitempty"`
}

type Web struct {
	Autoscaling struct {
		MaxReplicas                       int `json:"maxReplicas" gorm:"column:maxReplicas"`
		MinReplicas                       int `json:"minReplicas" gorm:"column:minReplicas"`
		TargetCPUUtilizationPercentage    int `json:"targetCPUUtilizationPercentage" gorm:"column:targetCPUUtilizationPercentage"`
		TargetMemoryUtilizationPercentage int `json:"targetMemoryUtilizationPercentage" gorm:"column:targetMemoryUtilizationPercentage"`
	} `json:"autoscaling" gorm:"embedded"`
	ReplicaCount int `json:"replicaCount" gorm:"column:replicaCount"`
}

type Worker struct {
	Autoscaling struct {
		MaxReplicas                       int `json:"maxReplicas" gorm:"column:maxReplicas"`
		MinReplicas                       int `json:"minReplicas" gorm:"column:minReplicas"`
		TargetCPUUtilizationPercentage    int `json:"targetCPUUtilizationPercentage" gorm:"column:targetCPUUtilizationPercentage"`
		TargetMemoryUtilizationPercentage int `json:"targetMemoryUtilizationPercentage" gorm:"column:targetMemoryUtilizationPercentage"`
	} `json:"autoscaling" gorm:"embedded"`
	ReplicaCount int `json:"replicaCount" gorm:"column:replicaCount"`
}

type AppCRD struct {
	gorm.Model
	APIVersion string   `json:"apiVersion" yaml:"apiVersion"`
	Kind       string   `json:"kind"`
	Metadata   Metadata `json:"metadata,omitempty" yaml:"metadata,omitempty"`
	Spec       AppSpec  `json:"spec" gorm:"embedded"`
}
0707010000003A000041ED00000000000000000000000267BEEE0500000000000000000000000000000000000000000000001B00000000kubero-cli-2.4.7/templates0707010000003B000081A400000000000000000000000167BEEE0500000137000000000000000000000000000000000000003E00000000kubero-cli-2.4.7/templates/certmanagerClusterIssuer.prod.yamlapiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: noreply@kubero.dev
    privateKeySecretRef:
      name: letsencrypt
    solvers:
      - http01:
          ingress:
            class: nginx
0707010000003C000081A400000000000000000000000167BEEE050000014A000000000000000000000000000000000000003F00000000kubero-cli-2.4.7/templates/certmanagerClusterIssuer.stage.yamlapiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    email: noreply@kubero.dev
    privateKeySecretRef:
      name: letsencrypt-staging
    solvers:
      - http01:
          ingress:
            class: nginx
0707010000003D000081A400000000000000000000000167BEEE050000005D000000000000000000000000000000000000002D00000000kubero-cli-2.4.7/templates/kindVersions.yamlavailableKubernetesVersions:
- v1.31.4
- v1.30.8
- v1.29.12
- v1.28.15
- v1.27.16
- v1.26.15
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!463 blocks
openSUSE Build Service is sponsored by