fix pre-commit and cleanup

This commit is contained in:
Kjeld Schouten
2024-10-19 15:18:05 +02:00
parent 2acb59c9dc
commit 4ab41d42f2
203 changed files with 8456 additions and 8484 deletions
+92 -92
View File
@@ -1,132 +1,132 @@
package fluxhandler
import (
"context"
"os"
"path/filepath"
"context"
"os"
"path/filepath"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/truecharts/public/clustertool/pkg/helper"
"github.com/truecharts/public/clustertool/pkg/kubectlcmds"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/truecharts/public/clustertool/pkg/helper"
"github.com/truecharts/public/clustertool/pkg/kubectlcmds"
)
func init() {
// Configure zerolog to output to stdout with a timestamp and log level
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout}).With().Timestamp().Logger()
// Configure zerolog to output to stdout with a timestamp and log level
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout}).With().Timestamp().Logger()
}
// FluxBootstrap initializes the FluxCD bootstrapping process if GITHUB_REPOSITORY is set in TalEnv.
func FluxBootstrap(ctx context.Context) {
if helper.TalEnv["GITHUB_REPOSITORY"] != "" {
log.Info().Msg("GITHUB_Repository for Flux configured.")
if helper.GetYesOrNo("Do you want to bootstrap FluxCD as well? (yes/no) [y/n]: ") {
if err := bootstrapFluxCD(ctx); err != nil {
log.Fatal().Err(err).Msg("Error during FluxCD bootstrap")
if helper.GetYesOrNo("Do you want to retry? (yes/no) [y/n]: ") {
if err2 := bootstrapFluxCD(ctx); err2 != nil {
log.Fatal().Err(err2).Msg("Error during FluxCD bootstrap")
}
}
}
log.Info().Msg("FluxCD Bootstrapped successfully")
}
}
if helper.TalEnv["GITHUB_REPOSITORY"] != "" {
log.Info().Msg("GITHUB_Repository for Flux configured.")
if helper.GetYesOrNo("Do you want to bootstrap FluxCD as well? (yes/no) [y/n]: ") {
if err := bootstrapFluxCD(ctx); err != nil {
log.Fatal().Err(err).Msg("Error during FluxCD bootstrap")
if helper.GetYesOrNo("Do you want to retry? (yes/no) [y/n]: ") {
if err2 := bootstrapFluxCD(ctx); err2 != nil {
log.Fatal().Err(err2).Msg("Error during FluxCD bootstrap")
}
}
}
log.Info().Msg("FluxCD Bootstrapped successfully")
}
}
}
// bootstrapFluxCD handles the entire FluxCD bootstrapping process.
func bootstrapFluxCD(ctx context.Context) error {
if err := checkGitRepo(); err != nil {
return err
}
if err := checkGitRepo(); err != nil {
return err
}
fluxPath := filepath.Join(helper.ClusterPath, "kubernetes", "flux-system", "flux")
if err := setupFluxCD(ctx, fluxPath); err != nil {
return err
}
fluxPath := filepath.Join(helper.ClusterPath, "kubernetes", "flux-system", "flux")
if err := setupFluxCD(ctx, fluxPath); err != nil {
return err
}
reposFilePath := "repositories"
if err := setupRepositories(ctx, reposFilePath); err != nil {
return err
}
reposFilePath := "repositories"
if err := setupRepositories(ctx, reposFilePath); err != nil {
return err
}
clusterEntryFile := filepath.Join(helper.ClusterPath, "kubernetes", "flux-entry.yaml")
if err := kubectlcmds.KubectlApply(ctx, clusterEntryFile); err != nil {
log.Error().Err(err).Str("path", clusterEntryFile).Msg("Error applying Kubernetes flux-entry manifest")
return err
}
clusterEntryFile := filepath.Join(helper.ClusterPath, "kubernetes", "flux-entry.yaml")
if err := kubectlcmds.KubectlApply(ctx, clusterEntryFile); err != nil {
log.Error().Err(err).Str("path", clusterEntryFile).Msg("Error applying Kubernetes flux-entry manifest")
return err
}
return nil
return nil
}
// checkGitRepo verifies if the current directory is a valid Git repository.
func checkGitRepo() error {
isRepo, err := helper.IsCurrentDirGitRepo()
if err != nil {
log.Error().Err(err).Msg("Error checking Git repository")
return err
}
if !isRepo {
errMsg := "Bootstrap: ERROR The current directory is not a Git repository. Cannot bootstrap fluxcd"
log.Error().Msg(errMsg)
return err
}
log.Info().Msg("Bootstrap: The current directory is a valid GIT repository, continuing...")
return nil
isRepo, err := helper.IsCurrentDirGitRepo()
if err != nil {
log.Error().Err(err).Msg("Error checking Git repository")
return err
}
if !isRepo {
errMsg := "Bootstrap: ERROR The current directory is not a Git repository. Cannot bootstrap fluxcd"
log.Error().Msg(errMsg)
return err
}
log.Info().Msg("Bootstrap: The current directory is a valid GIT repository, continuing...")
return nil
}
// setupFluxCD handles the setup of FluxCD manifests.
func setupFluxCD(ctx context.Context, fluxPath string) error {
bootstrapFile := "bootstrap.yaml.ct"
kustomFile := "kustomization.yaml"
tmpFile := "placeholder"
bootstrapFile := "bootstrap.yaml.ct"
kustomFile := "kustomization.yaml"
tmpFile := "placeholder"
log.Info().Msg("Bootstrap: Loading fluxcd onto the cluster...")
log.Info().Msg("Bootstrap: Loading fluxcd onto the cluster...")
// Rename files for kustomize application
if err := os.Rename(filepath.Join(fluxPath, kustomFile), filepath.Join(fluxPath, tmpFile)); err != nil {
log.Error().Err(err).Msg("Error renaming kustomization file")
return err
}
if err := os.Rename(filepath.Join(fluxPath, bootstrapFile), filepath.Join(fluxPath, kustomFile)); err != nil {
log.Error().Err(err).Msg("Error renaming bootstrap file")
return err
}
// Rename files for kustomize application
if err := os.Rename(filepath.Join(fluxPath, kustomFile), filepath.Join(fluxPath, tmpFile)); err != nil {
log.Error().Err(err).Msg("Error renaming kustomization file")
return err
}
if err := os.Rename(filepath.Join(fluxPath, bootstrapFile), filepath.Join(fluxPath, kustomFile)); err != nil {
log.Error().Err(err).Msg("Error renaming bootstrap file")
return err
}
if err := kubectlcmds.KubectlApplyKustomize(ctx, fluxPath); err != nil {
log.Error().Err(err).Str("path", fluxPath).Msg("Error applying FluxCD manifest")
return err
}
if err := kubectlcmds.KubectlApplyKustomize(ctx, fluxPath); err != nil {
log.Error().Err(err).Str("path", fluxPath).Msg("Error applying FluxCD manifest")
return err
}
// Revert file renames
if err := os.Rename(filepath.Join(fluxPath, kustomFile), filepath.Join(fluxPath, bootstrapFile)); err != nil {
log.Error().Err(err).Msg("Error renaming kustomization file back")
return err
}
if err := os.Rename(filepath.Join(fluxPath, tmpFile), filepath.Join(fluxPath, kustomFile)); err != nil {
log.Error().Err(err).Msg("Error renaming placeholder file back")
return err
}
// Revert file renames
if err := os.Rename(filepath.Join(fluxPath, kustomFile), filepath.Join(fluxPath, bootstrapFile)); err != nil {
log.Error().Err(err).Msg("Error renaming kustomization file back")
return err
}
if err := os.Rename(filepath.Join(fluxPath, tmpFile), filepath.Join(fluxPath, kustomFile)); err != nil {
log.Error().Err(err).Msg("Error renaming placeholder file back")
return err
}
return nil
return nil
}
// setupRepositories handles the setup of repository manifests.
func setupRepositories(ctx context.Context, reposFilePath string) error {
log.Info().Msg("Bootstrap: Loading git-repo manifests onto the cluster...")
log.Info().Msg("Bootstrap: Loading git-repo manifests onto the cluster...")
gitRepoFile := filepath.Join(reposFilePath, "git", "this-repo.yaml")
if err := kubectlcmds.KubectlApply(ctx, gitRepoFile); err != nil {
log.Error().Err(err).Str("path", reposFilePath).Msg("Error applying repositories manifest")
return err
}
gitRepoFile := filepath.Join(reposFilePath, "git", "this-repo.yaml")
if err := kubectlcmds.KubectlApply(ctx, gitRepoFile); err != nil {
log.Error().Err(err).Str("path", reposFilePath).Msg("Error applying repositories manifest")
return err
}
log.Info().Msg("Bootstrap: Loading repositories flux-entry onto the cluster...")
reposEntryFile := filepath.Join(reposFilePath, "flux-entry.yaml")
if err := kubectlcmds.KubectlApply(ctx, reposEntryFile); err != nil {
log.Error().Err(err).Str("path", reposEntryFile).Msg("Error applying repositories flux-entry manifest")
return err
}
log.Info().Msg("Bootstrap: Loading repositories flux-entry onto the cluster...")
reposEntryFile := filepath.Join(reposFilePath, "flux-entry.yaml")
if err := kubectlcmds.KubectlApply(ctx, reposEntryFile); err != nil {
log.Error().Err(err).Str("path", reposEntryFile).Msg("Error applying repositories flux-entry manifest")
return err
}
return nil
return nil
}
+430 -430
View File
@@ -1,565 +1,565 @@
package fluxhandler
import (
"context"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"time"
"context"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"time"
"github.com/rs/zerolog/log"
"github.com/truecharts/public/clustertool/pkg/helper"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart/loader"
"helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/cli/values"
"helm.sh/helm/v3/pkg/getter"
"helm.sh/helm/v3/pkg/registry"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v3/pkg/repo"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/kustomize/kyaml/yaml"
"github.com/rs/zerolog/log"
"github.com/truecharts/public/clustertool/pkg/helper"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart/loader"
"helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/cli/values"
"helm.sh/helm/v3/pkg/getter"
"helm.sh/helm/v3/pkg/registry"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v3/pkg/repo"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func newDefaultRegistryClient(plainHTTP bool, settings *cli.EnvSettings) (*registry.Client, error) {
opts := []registry.ClientOption{
registry.ClientOptDebug(settings.Debug),
registry.ClientOptEnableCache(true),
registry.ClientOptWriter(os.Stdout),
registry.ClientOptCredentialsFile(settings.RegistryConfig),
}
if plainHTTP {
opts = append(opts, registry.ClientOptPlainHTTP())
}
opts := []registry.ClientOption{
registry.ClientOptDebug(settings.Debug),
registry.ClientOptEnableCache(true),
registry.ClientOptWriter(os.Stdout),
registry.ClientOptCredentialsFile(settings.RegistryConfig),
}
if plainHTTP {
opts = append(opts, registry.ClientOptPlainHTTP())
}
// Create a new registry client
registryClient, err := registry.NewClient(opts...)
if err != nil {
return nil, err
}
return registryClient, nil
// Create a new registry client
registryClient, err := registry.NewClient(opts...)
if err != nil {
return nil, err
}
return registryClient, nil
}
// HelmPull downloads a Helm chart from a repository
func HelmPull(repo string, name string, version string, dest string, silent bool) error {
settings := cli.New()
actionConfig := new(action.Configuration)
settings := cli.New()
actionConfig := new(action.Configuration)
// Define logger based on the silent parameter
var logger func(string, ...interface{})
if silent {
logger = noOpLog
} else {
logger = log.Printf
}
// Define logger based on the silent parameter
var logger func(string, ...interface{})
if silent {
logger = noOpLog
} else {
logger = log.Printf
}
// Initialize actionConfig with the appropriate logger
if err := actionConfig.Init(settings.RESTClientGetter(), "", os.Getenv("HELM_DRIVER"), logger); err != nil {
return fmt.Errorf("failed to initialize Helm action config: %w", err)
}
// Initialize actionConfig with the appropriate logger
if err := actionConfig.Init(settings.RESTClientGetter(), "", os.Getenv("HELM_DRIVER"), logger); err != nil {
return fmt.Errorf("failed to initialize Helm action config: %w", err)
}
registryClient, err := newDefaultRegistryClient(false, settings)
if err != nil {
return err
}
actionConfig.RegistryClient = registryClient
registryClient, err := newDefaultRegistryClient(false, settings)
if err != nil {
return err
}
actionConfig.RegistryClient = registryClient
client := action.NewPullWithOpts(action.WithConfig(actionConfig))
client.Settings = settings
client.RepoURL = repo
client.Version = version
client.DestDir = filepath.Join(helper.HelmCache, dest)
client := action.NewPullWithOpts(action.WithConfig(actionConfig))
client.Settings = settings
client.RepoURL = repo
client.Version = version
client.DestDir = filepath.Join(helper.HelmCache, dest)
// Create cache directory
if err := os.MkdirAll(client.DestDir, os.ModePerm); err != nil {
return fmt.Errorf("❌ Failed to create cache directory: %s", err)
}
// Create cache directory
if err := os.MkdirAll(client.DestDir, os.ModePerm); err != nil {
return fmt.Errorf("❌ Failed to create cache directory: %s", err)
}
switch repo {
case "https://charts.truecharts.org",
"https://library-charts.truecharts.org",
"https://deps.truecharts.org":
client.Keyring = helper.GpgDir + "/pubring.gpg"
client.Verify = true
case "https://charts.jetstack.io":
client.Keyring = helper.GpgDir + "/certman.gpg"
client.Verify = true
default:
// Do nothing for other repositories
}
switch repo {
case "https://charts.truecharts.org",
"https://library-charts.truecharts.org",
"https://deps.truecharts.org":
client.Keyring = helper.GpgDir + "/pubring.gpg"
client.Verify = true
case "https://charts.jetstack.io":
client.Keyring = helper.GpgDir + "/certman.gpg"
client.Verify = true
default:
// Do nothing for other repositories
}
link := ""
if strings.HasPrefix(repo, "http") {
link = name
repoName := cleanRepoURL(repo)
updateHelmRepo(repoName, repo, silent)
repo = repoName
} else {
link = repo + "/" + name
client.RepoURL = ""
}
link := ""
if strings.HasPrefix(repo, "http") {
link = name
repoName := cleanRepoURL(repo)
updateHelmRepo(repoName, repo, silent)
repo = repoName
} else {
link = repo + "/" + name
client.RepoURL = ""
}
output, err := client.Run(link)
output, err := client.Run(link)
if err != nil {
os.Remove(path.Join(dest, fmt.Sprintf("%s-%s.tgz", name, version)))
return err
}
if !silent {
log.Info().Msg("✅ Dependency Downloaded!")
}
if client.Keyring != "" && client.Keyring != "nil" {
if !silent {
log.Info().Msg("✅ Dependency Verified")
}
}
if err != nil {
os.Remove(path.Join(dest, fmt.Sprintf("%s-%s.tgz", name, version)))
return err
}
if !silent {
log.Info().Msg("✅ Dependency Downloaded!")
}
if client.Keyring != "" && client.Keyring != "nil" {
if !silent {
log.Info().Msg("✅ Dependency Verified")
}
}
if output != "" {
log.Info().Msgf("☸ Helm output: %s", output)
}
return nil
if output != "" {
log.Info().Msgf("☸ Helm output: %s", output)
}
return nil
}
func noOpLog(format string, v ...interface{}) {}
// HelmInstall installs a Helm chart with provided parameters
func HelmInstall(repoURL string, chartName string, releaseName string, namespace string, valuesFile string, version string, dryRun bool, wait bool, silent bool) error {
if dryRun {
log.Info().Msg("dryRun not possible...")
return nil
}
if dryRun {
log.Info().Msg("dryRun not possible...")
return nil
}
settings := cli.New()
actionConfig := new(action.Configuration)
settings := cli.New()
actionConfig := new(action.Configuration)
settings.SetNamespace(namespace)
settings.SetNamespace(namespace)
var logger func(string, ...interface{})
if silent {
logger = noOpLog
var logger func(string, ...interface{})
if silent {
logger = noOpLog
} else {
logger = log.Printf
}
} else {
logger = log.Printf
}
if err := actionConfig.Init(settings.RESTClientGetter(), namespace,
os.Getenv("HELM_DRIVER"), logger); err != nil {
return fmt.Errorf("failed to initialize Helm action config: %w", err)
}
if err := actionConfig.Init(settings.RESTClientGetter(), namespace,
os.Getenv("HELM_DRIVER"), logger); err != nil {
return fmt.Errorf("failed to initialize Helm action config: %w", err)
}
// Ensure namespace exists or create it
if err := ensureNamespace(actionConfig, namespace); err != nil {
return fmt.Errorf("failed to ensure namespace exists: %w", err)
}
// Ensure namespace exists or create it
if err := ensureNamespace(actionConfig, namespace); err != nil {
return fmt.Errorf("failed to ensure namespace exists: %w", err)
}
var chartPath string
var err error
var chartPath string
var err error
// Determine chart path based on chartName format
if strings.HasPrefix(repoURL, "http://") || strings.HasPrefix(repoURL, "https://") || strings.HasPrefix(repoURL, "oci://") {
// Handle HTTP or OCI URL
err = HelmPull(repoURL, chartName, version, "", true)
if err != nil {
return fmt.Errorf("failed to pull chart %s: %w", chartName, err)
}
chartPath = path.Join(helper.HelmCache, fmt.Sprintf("%s-%s.tgz", chartName, version))
// Determine chart path based on chartName format
if strings.HasPrefix(repoURL, "http://") || strings.HasPrefix(repoURL, "https://") || strings.HasPrefix(repoURL, "oci://") {
// Handle HTTP or OCI URL
err = HelmPull(repoURL, chartName, version, "", true)
if err != nil {
return fmt.Errorf("failed to pull chart %s: %w", chartName, err)
}
chartPath = path.Join(helper.HelmCache, fmt.Sprintf("%s-%s.tgz", chartName, version))
} else {
// Local chart path
chartPath = repoURL
}
} else {
// Local chart path
chartPath = repoURL
}
// Load the Helm chart using loader.Load
chart, err := loader.Load(chartPath)
if err != nil {
return fmt.Errorf("failed to load chart: %w", err)
}
// Load the Helm chart using loader.Load
chart, err := loader.Load(chartPath)
if err != nil {
return fmt.Errorf("failed to load chart: %w", err)
}
// Set up Helm install action
client := action.NewInstall(actionConfig)
client.Namespace = namespace
client.ReleaseName = releaseName
client.DryRun = dryRun
client.Version = version
// Set up Helm install action
client := action.NewInstall(actionConfig)
client.Namespace = namespace
client.ReleaseName = releaseName
client.DryRun = dryRun
client.Version = version
tempValuesName := releaseName + "tempvalues.yaml"
tempValuesPath := path.Join(helper.HelmCache, tempValuesName)
// Create values.yaml from chart.Values
err = createValuesYAML(chart.Values, tempValuesPath)
if err != nil {
return fmt.Errorf("error creating tempvalues.yaml: %w", err)
}
valueFiles := []string{tempValuesPath}
tempValuesName := releaseName + "tempvalues.yaml"
tempValuesPath := path.Join(helper.HelmCache, tempValuesName)
// Create values.yaml from chart.Values
err = createValuesYAML(chart.Values, tempValuesPath)
if err != nil {
return fmt.Errorf("error creating tempvalues.yaml: %w", err)
}
valueFiles := []string{tempValuesPath}
// Get the directory part of the path
directory := filepath.Dir(valuesFile)
// Get the directory part of the path
directory := filepath.Dir(valuesFile)
helmreleasePath := path.Join(directory, "helm-release.yaml")
helmreleasePath := path.Join(directory, "helm-release.yaml")
helmRelease, err := LoadHelmRelease(helmreleasePath)
if err != nil {
helmRelease, err := LoadHelmRelease(helmreleasePath)
if err != nil {
}
tempHRValuesName := releaseName + "temphrvalues.yaml"
tempHRValuesPath := path.Join(helper.HelmCache, tempHRValuesName)
err = createValuesYAML(helmRelease.Spec.Values, tempHRValuesPath)
if err != nil {
return fmt.Errorf("error creating temphrvalues.yaml: %w", err)
}
helper.EnvSubst(tempHRValuesPath, helper.TalEnv)
valueFiles = append(valueFiles, tempHRValuesPath)
}
tempHRValuesName := releaseName + "temphrvalues.yaml"
tempHRValuesPath := path.Join(helper.HelmCache, tempHRValuesName)
err = createValuesYAML(helmRelease.Spec.Values, tempHRValuesPath)
if err != nil {
return fmt.Errorf("error creating temphrvalues.yaml: %w", err)
}
helper.EnvSubst(tempHRValuesPath, helper.TalEnv)
valueFiles = append(valueFiles, tempHRValuesPath)
if _, err := os.Stat(valuesFile); err == nil {
valueFiles = append(valueFiles, valuesFile)
if _, err := os.Stat(valuesFile); err == nil {
valueFiles = append(valueFiles, valuesFile)
}
}
overrideValuesPath := path.Join(directory, "bootstrap-values.yaml.ct")
overrideValuesPath := path.Join(directory, "bootstrap-values.yaml.ct")
if _, err := os.Stat(overrideValuesPath); err == nil {
valueFiles = append(valueFiles, overrideValuesPath)
}
if _, err := os.Stat(overrideValuesPath); err == nil {
valueFiles = append(valueFiles, overrideValuesPath)
}
// Prepare values for installation
valOpts := &values.Options{
ValueFiles: valueFiles, // Specify value file to merge
}
// Prepare values for installation
valOpts := &values.Options{
ValueFiles: valueFiles, // Specify value file to merge
}
// Create getter to fetch values from file
valProviders := getter.All(settings)
// Create getter to fetch values from file
valProviders := getter.All(settings)
// Merge values with chart's default values
vals, err := valOpts.MergeValues(valProviders)
if err != nil {
return fmt.Errorf("failed to merge values: %w", err)
}
// Merge values with chart's default values
vals, err := valOpts.MergeValues(valProviders)
if err != nil {
return fmt.Errorf("failed to merge values: %w", err)
}
// Install the chart with merged values
release, err := client.Run(chart, vals)
if err != nil {
return fmt.Errorf("failed to install chart: %w", err)
}
// Install the chart with merged values
release, err := client.Run(chart, vals)
if err != nil {
return fmt.Errorf("failed to install chart: %w", err)
}
if wait {
waitForRelease(actionConfig, release.Name, client.Namespace)
}
if wait {
waitForRelease(actionConfig, release.Name, client.Namespace)
}
log.Printf("Installed Chart: %s in namespace: %s\n", release.Name, release.Namespace)
log.Printf("Installed Chart values: %v\n", release.Config)
log.Printf("Installed Chart: %s in namespace: %s\n", release.Name, release.Namespace)
log.Printf("Installed Chart values: %v\n", release.Config)
return nil
return nil
}
func ensureNamespace(actionConfig *action.Configuration, namespace string) error {
// Check if the namespace exists
exists, err := namespaceExists(actionConfig, namespace)
if err != nil {
return fmt.Errorf("failed to check if namespace exists: %w", err)
}
// Check if the namespace exists
exists, err := namespaceExists(actionConfig, namespace)
if err != nil {
return fmt.Errorf("failed to check if namespace exists: %w", err)
}
if !exists {
// Create the namespace if it does not exist
err := createNamespace(actionConfig, namespace)
if err != nil {
return fmt.Errorf("failed to create namespace: %w", err)
}
}
if !exists {
// Create the namespace if it does not exist
err := createNamespace(actionConfig, namespace)
if err != nil {
return fmt.Errorf("failed to create namespace: %w", err)
}
}
return nil
return nil
}
// HelmUpgrade upgrades a Helm release with provided parameters
// HelmUpgrade upgrades a Helm release with provided parameters
func HelmUpgrade(repoURL string, chartName string, releaseName string, namespace string, valuesFile string, version string, wait bool, silent bool) error {
settings := cli.New()
actionConfig := new(action.Configuration)
settings := cli.New()
actionConfig := new(action.Configuration)
settings.SetNamespace(namespace)
settings.SetNamespace(namespace)
var logger func(string, ...interface{})
if silent {
logger = noOpLog
} else {
logger = log.Printf
}
var logger func(string, ...interface{})
if silent {
logger = noOpLog
} else {
logger = log.Printf
}
if err := actionConfig.Init(settings.RESTClientGetter(), namespace,
os.Getenv("HELM_DRIVER"), logger); err != nil {
return fmt.Errorf("failed to initialize Helm action config: %w", err)
}
if err := actionConfig.Init(settings.RESTClientGetter(), namespace,
os.Getenv("HELM_DRIVER"), logger); err != nil {
return fmt.Errorf("failed to initialize Helm action config: %w", err)
}
// Ensure namespace exists or create it
if err := ensureNamespace(actionConfig, namespace); err != nil {
return fmt.Errorf("failed to ensure namespace exists: %w", err)
}
// Ensure namespace exists or create it
if err := ensureNamespace(actionConfig, namespace); err != nil {
return fmt.Errorf("failed to ensure namespace exists: %w", err)
}
var chartPath string
var err error
var chartPath string
var err error
// Determine chart path based on chartName format
if strings.HasPrefix(repoURL, "http://") || strings.HasPrefix(repoURL, "https://") || strings.HasPrefix(repoURL, "oci://") {
// Handle HTTP or OCI URL
err = HelmPull(repoURL, chartName, version, "", true)
if err != nil {
return fmt.Errorf("failed to pull chart %s: %w", chartName, err)
}
chartPath = path.Join(helper.HelmCache, fmt.Sprintf("%s-%s.tgz", chartName, version))
// Determine chart path based on chartName format
if strings.HasPrefix(repoURL, "http://") || strings.HasPrefix(repoURL, "https://") || strings.HasPrefix(repoURL, "oci://") {
// Handle HTTP or OCI URL
err = HelmPull(repoURL, chartName, version, "", true)
if err != nil {
return fmt.Errorf("failed to pull chart %s: %w", chartName, err)
}
chartPath = path.Join(helper.HelmCache, fmt.Sprintf("%s-%s.tgz", chartName, version))
} else {
// Local chart path
chartPath = repoURL
}
} else {
// Local chart path
chartPath = repoURL
}
// Load the Helm chart using loader.Load
chart, err := loader.Load(chartPath)
if err != nil {
return fmt.Errorf("failed to load chart: %w", err)
}
// Load the Helm chart using loader.Load
chart, err := loader.Load(chartPath)
if err != nil {
return fmt.Errorf("failed to load chart: %w", err)
}
// Set up Helm upgrade action
client := action.NewUpgrade(actionConfig)
client.Namespace = namespace
client.Version = version
// Set up Helm upgrade action
client := action.NewUpgrade(actionConfig)
client.Namespace = namespace
client.Version = version
tempValuesName := releaseName + "tempvalues.yaml"
tempValuesPath := path.Join(helper.HelmCache, tempValuesName)
err = createValuesYAML(chart.Values, tempValuesPath)
if err != nil {
return fmt.Errorf("error creating tempvalues.yaml: %w", err)
}
valueFiles := []string{tempValuesPath}
tempValuesName := releaseName + "tempvalues.yaml"
tempValuesPath := path.Join(helper.HelmCache, tempValuesName)
err = createValuesYAML(chart.Values, tempValuesPath)
if err != nil {
return fmt.Errorf("error creating tempvalues.yaml: %w", err)
}
valueFiles := []string{tempValuesPath}
// Get the directory part of the path
directory := filepath.Dir(valuesFile)
// Get the directory part of the path
directory := filepath.Dir(valuesFile)
helmreleasePath := path.Join(directory, "helm-release.yaml")
helmreleasePath := path.Join(directory, "helm-release.yaml")
helmRelease, err := LoadHelmRelease(helmreleasePath)
if err != nil {
return fmt.Errorf("error loading helm-release.yaml: %w", err)
}
helmRelease, err := LoadHelmRelease(helmreleasePath)
if err != nil {
return fmt.Errorf("error loading helm-release.yaml: %w", err)
}
tempHRValuesName := releaseName + "temphrvalues.yaml"
tempHRValuesPath := path.Join(helper.HelmCache, tempHRValuesName)
err = createValuesYAML(helmRelease.Spec.Values, tempHRValuesPath)
if err != nil {
return fmt.Errorf("error creating temphrvalues.yaml: %w", err)
}
helper.EnvSubst(tempHRValuesPath, helper.TalEnv)
valueFiles = append(valueFiles, tempHRValuesPath)
tempHRValuesName := releaseName + "temphrvalues.yaml"
tempHRValuesPath := path.Join(helper.HelmCache, tempHRValuesName)
err = createValuesYAML(helmRelease.Spec.Values, tempHRValuesPath)
if err != nil {
return fmt.Errorf("error creating temphrvalues.yaml: %w", err)
}
helper.EnvSubst(tempHRValuesPath, helper.TalEnv)
valueFiles = append(valueFiles, tempHRValuesPath)
if _, err := os.Stat(valuesFile); err == nil {
valueFiles = append(valueFiles, valuesFile)
}
if _, err := os.Stat(valuesFile); err == nil {
valueFiles = append(valueFiles, valuesFile)
}
overrideValuesPath := path.Join(directory, "bootstrap-values.yaml.ct")
overrideValuesPath := path.Join(directory, "bootstrap-values.yaml.ct")
if _, err := os.Stat(overrideValuesPath); err == nil {
valueFiles = append(valueFiles, overrideValuesPath)
}
if _, err := os.Stat(overrideValuesPath); err == nil {
valueFiles = append(valueFiles, overrideValuesPath)
}
// Prepare values for upgrade
valOpts := &values.Options{
ValueFiles: valueFiles, // Specify value file to merge
}
// Prepare values for upgrade
valOpts := &values.Options{
ValueFiles: valueFiles, // Specify value file to merge
}
// Create getter to fetch values from file
valProviders := getter.All(settings)
// Create getter to fetch values from file
valProviders := getter.All(settings)
// Merge values with chart's default values
vals, err := valOpts.MergeValues(valProviders)
if err != nil {
return fmt.Errorf("failed to merge values: %w", err)
}
// Merge values with chart's default values
vals, err := valOpts.MergeValues(valProviders)
if err != nil {
return fmt.Errorf("failed to merge values: %w", err)
}
// Perform the upgrade with merged values
release, err := client.Run(releaseName, chart, vals)
if err != nil {
return fmt.Errorf("failed to upgrade chart: %w", err)
}
// Perform the upgrade with merged values
release, err := client.Run(releaseName, chart, vals)
if err != nil {
return fmt.Errorf("failed to upgrade chart: %w", err)
}
if wait {
waitForRelease(actionConfig, release.Name, client.Namespace)
}
if wait {
waitForRelease(actionConfig, release.Name, client.Namespace)
}
log.Printf("Upgraded Chart: %s in namespace: %s\n", release.Name, release.Namespace)
log.Printf("Upgraded Chart values: %v\n", release.Config)
log.Printf("Upgraded Chart: %s in namespace: %s\n", release.Name, release.Namespace)
log.Printf("Upgraded Chart values: %v\n", release.Config)
return nil
return nil
}
func namespaceExists(actionConfig *action.Configuration, namespace string) (bool, error) {
// Retrieve Kubernetes client set from actionConfig
clientset, err := actionConfig.KubernetesClientSet()
if err != nil {
return false, fmt.Errorf("failed to get Kubernetes client set: %w", err)
}
// Retrieve Kubernetes client set from actionConfig
clientset, err := actionConfig.KubernetesClientSet()
if err != nil {
return false, fmt.Errorf("failed to get Kubernetes client set: %w", err)
}
// Use clientset to check if the namespace exists
_, err = clientset.CoreV1().Namespaces().Get(context.Background(), namespace, metav1.GetOptions{})
if err != nil {
return false, nil // Namespace doesn't exist or other error occurred
}
return true, nil // Namespace exists
// Use clientset to check if the namespace exists
_, err = clientset.CoreV1().Namespaces().Get(context.Background(), namespace, metav1.GetOptions{})
if err != nil {
return false, nil // Namespace doesn't exist or other error occurred
}
return true, nil // Namespace exists
}
func createNamespace(actionConfig *action.Configuration, namespace string) error {
// Retrieve Kubernetes client set from actionConfig
clientset, err := actionConfig.KubernetesClientSet()
if err != nil {
return fmt.Errorf("failed to get Kubernetes client set: %w", err)
}
// Retrieve Kubernetes client set from actionConfig
clientset, err := actionConfig.KubernetesClientSet()
if err != nil {
return fmt.Errorf("failed to get Kubernetes client set: %w", err)
}
// Create the namespace using clientset
_, err = clientset.CoreV1().Namespaces().Create(context.Background(), &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: namespace,
},
}, metav1.CreateOptions{})
if err != nil {
if strings.Contains(err.Error(), "already exists") {
// Create the namespace using clientset
_, err = clientset.CoreV1().Namespaces().Create(context.Background(), &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: namespace,
},
}, metav1.CreateOptions{})
if err != nil {
if strings.Contains(err.Error(), "already exists") {
} else {
return fmt.Errorf("failed to create namespace: %w", err)
}
}
return nil
} else {
return fmt.Errorf("failed to create namespace: %w", err)
}
}
return nil
}
func createValuesYAML(values map[string]interface{}, fileName string) error {
removeFileIfExists(fileName)
// Marshal values map into YAML format
data, err := yaml.Marshal(values)
if err != nil {
return err
}
removeFileIfExists(fileName)
// Marshal values map into YAML format
data, err := yaml.Marshal(values)
if err != nil {
return err
}
// Write YAML data into the file
err = ioutil.WriteFile(fileName, data, 0644)
if err != nil {
return err
}
// Write YAML data into the file
err = ioutil.WriteFile(fileName, data, 0644)
if err != nil {
return err
}
return nil
return nil
}
func removeFileIfExists(fileName string) error {
// Check if the file exists
_, err := os.Stat(fileName)
if err == nil {
// Delete the file
err = os.Remove(fileName)
if err != nil {
return err
}
} else if !os.IsNotExist(err) {
// Return other errors if the file check failed for a reason other than not existing
return err
}
// Check if the file exists
_, err := os.Stat(fileName)
if err == nil {
// Delete the file
err = os.Remove(fileName)
if err != nil {
return err
}
} else if !os.IsNotExist(err) {
// Return other errors if the file check failed for a reason other than not existing
return err
}
return nil
return nil
}
func updateHelmRepo(name string, url string, silent bool) error {
// Create a Helm repository configuration
repoConfig := &repo.Entry{
Name: name,
URL: url,
}
// Create a Helm repository configuration
repoConfig := &repo.Entry{
Name: name,
URL: url,
}
// Initialize Helm settings
settings := cli.New()
// Initialize Helm settings
settings := cli.New()
// Create a repository object
r, err := repo.NewChartRepository(repoConfig, getter.All(settings))
if err != nil {
return fmt.Errorf("failed to create chart repository: %w", err)
}
// Create a repository object
r, err := repo.NewChartRepository(repoConfig, getter.All(settings))
if err != nil {
return fmt.Errorf("failed to create chart repository: %w", err)
}
// Ensure the repository cache directory exists
cacheDir := settings.RepositoryCache
if err := os.MkdirAll(cacheDir, os.ModePerm); err != nil {
return fmt.Errorf("failed to create cache directory: %w", err)
}
// Ensure the repository cache directory exists
cacheDir := settings.RepositoryCache
if err := os.MkdirAll(cacheDir, os.ModePerm); err != nil {
return fmt.Errorf("failed to create cache directory: %w", err)
}
// Download the latest index file
if _, err := r.DownloadIndexFile(); err != nil {
return fmt.Errorf("failed to download index file: %w", err)
}
// Download the latest index file
if _, err := r.DownloadIndexFile(); err != nil {
return fmt.Errorf("failed to download index file: %w", err)
}
// Load existing repositories file or create a new one
repoFile := settings.RepositoryConfig
repoFileContent := repo.NewFile()
if _, err := os.Stat(repoFile); err == nil {
repoFileContent, err = repo.LoadFile(repoFile)
if err != nil {
return fmt.Errorf("failed to load repositories file: %w", err)
}
}
// Load existing repositories file or create a new one
repoFile := settings.RepositoryConfig
repoFileContent := repo.NewFile()
if _, err := os.Stat(repoFile); err == nil {
repoFileContent, err = repo.LoadFile(repoFile)
if err != nil {
return fmt.Errorf("failed to load repositories file: %w", err)
}
}
// Update the repositories file with the new repository
if !repoFileContent.Has(name) {
repoFileContent.Update(repoConfig)
}
// Update the repositories file with the new repository
if !repoFileContent.Has(name) {
repoFileContent.Update(repoConfig)
}
if err := repoFileContent.WriteFile(repoFile, 0644); err != nil {
return fmt.Errorf("failed to write repositories file: %w", err)
}
if err := repoFileContent.WriteFile(repoFile, 0644); err != nil {
return fmt.Errorf("failed to write repositories file: %w", err)
}
if !silent {
log.Info().Msgf("Successfully updated repository '%s' from %s\n", name, url)
}
return nil
if !silent {
log.Info().Msgf("Successfully updated repository '%s' from %s\n", name, url)
}
return nil
}
// cleanRepoURL performs the specified operations on the input URL
func cleanRepoURL(url string) string {
// Remove http:// or https:// prefix
url = strings.TrimPrefix(url, "http://")
url = strings.TrimPrefix(url, "https://")
// Remove http:// or https:// prefix
url = strings.TrimPrefix(url, "http://")
url = strings.TrimPrefix(url, "https://")
// Remove charts. prefix if present
url = strings.TrimPrefix(url, "charts.")
// Remove charts. prefix if present
url = strings.TrimPrefix(url, "charts.")
// Remove helm. prefix if present
url = strings.TrimPrefix(url, "helm.")
// Remove helm. prefix if present
url = strings.TrimPrefix(url, "helm.")
// Remove everything after the last dot
lastDotIndex := strings.LastIndex(url, ".")
if lastDotIndex != -1 {
url = url[:lastDotIndex]
}
// Remove everything after the last dot
lastDotIndex := strings.LastIndex(url, ".")
if lastDotIndex != -1 {
url = url[:lastDotIndex]
}
url = repoURL(url)
url = repoURL(url)
return url
return url
}
func repoURL(url string) string {
parts := strings.SplitN(url, "/", 2) // Split into two parts at the first "/"
if len(parts) > 0 {
url = parts[0]
}
parts := strings.SplitN(url, "/", 2) // Split into two parts at the first "/"
if len(parts) > 0 {
url = parts[0]
}
return url
return url
}
func waitForRelease(actionConfig *action.Configuration, releaseName, namespace string) {
statusClient := action.NewStatus(actionConfig)
for {
rel, err := statusClient.Run(releaseName)
if err != nil {
log.Info().Msgf("failed to get release status: %v", err)
}
if rel.Info.Status == release.StatusDeployed {
log.Info().Msgf("Release %s is now deployed\n", releaseName)
break
}
log.Info().Msgf("Waiting for release %s to be deployed (current status: %s)\n", releaseName, rel.Info.Status)
time.Sleep(5 * time.Second)
}
statusClient := action.NewStatus(actionConfig)
for {
rel, err := statusClient.Run(releaseName)
if err != nil {
log.Info().Msgf("failed to get release status: %v", err)
}
if rel.Info.Status == release.StatusDeployed {
log.Info().Msgf("Release %s is now deployed\n", releaseName)
break
}
log.Info().Msgf("Waiting for release %s to be deployed (current status: %s)\n", releaseName, rel.Info.Status)
time.Sleep(5 * time.Second)
}
}
+149 -149
View File
@@ -1,200 +1,200 @@
package fluxhandler
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
"github.com/rs/zerolog/log"
"gopkg.in/yaml.v3"
"github.com/rs/zerolog/log"
"gopkg.in/yaml.v3"
)
type HelmChart struct {
ChartPath string
Retry bool
Wait bool
ChartPath string
Retry bool
Wait bool
}
type SourceRef struct {
Kind string `yaml:"kind,omitempty"`
Name string `yaml:"name,omitempty"`
Namespace string `yaml:"namespace,omitempty"`
Kind string `yaml:"kind,omitempty"`
Name string `yaml:"name,omitempty"`
Namespace string `yaml:"namespace,omitempty"`
}
type ChartSpec struct {
Chart string `yaml:"chart,omitempty"`
Version string `yaml:"version,omitempty"`
SourceRef SourceRef `yaml:"sourceRef,omitempty"`
Chart string `yaml:"chart,omitempty"`
Version string `yaml:"version,omitempty"`
SourceRef SourceRef `yaml:"sourceRef,omitempty"`
}
type Chart struct {
Spec ChartSpec `yaml:"spec,omitempty"`
Spec ChartSpec `yaml:"spec,omitempty"`
}
type Spec struct {
Interval string `yaml:"interval,omitempty"`
Chart Chart `yaml:"chart,omitempty"`
ReleaseName string `yaml:"releaseName,omitempty"`
Values map[string]interface{} `yaml:"values,omitempty"`
Interval string `yaml:"interval,omitempty"`
Chart Chart `yaml:"chart,omitempty"`
ReleaseName string `yaml:"releaseName,omitempty"`
Values map[string]interface{} `yaml:"values,omitempty"`
}
type Metadata struct {
Name string `yaml:"name,omitempty"`
Namespace string `yaml:"namespace,omitempty"`
Name string `yaml:"name,omitempty"`
Namespace string `yaml:"namespace,omitempty"`
}
type HelmRelease struct {
Metadata Metadata `yaml:"metadata,omitempty"`
Spec Spec `yaml:"spec,omitempty"`
Metadata Metadata `yaml:"metadata,omitempty"`
Spec Spec `yaml:"spec,omitempty"`
}
func LoadHelmRelease(filename string) (*HelmRelease, error) {
// Read YAML file
yamlFile, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("failed to read file: %w", err)
}
// Read YAML file
yamlFile, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("failed to read file: %w", err)
}
// Initialize HelmRelease struct
config := &HelmRelease{}
// Initialize HelmRelease struct
config := &HelmRelease{}
// Unmarshal YAML into struct
err = yaml.Unmarshal(yamlFile, config)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal YAML: %w", err)
}
// Unmarshal YAML into struct
err = yaml.Unmarshal(yamlFile, config)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal YAML: %w", err)
}
// Ensure Values is not nil
if config.Spec.Values == nil {
config.Spec.Values = make(map[string]interface{})
}
return config, nil
// Ensure Values is not nil
if config.Spec.Values == nil {
config.Spec.Values = make(map[string]interface{})
}
return config, nil
}
func InstallCharts(charts []HelmChart, HelmRepos map[string]*HelmRepo, async bool) {
var wg sync.WaitGroup
for _, chart := range charts {
wg.Add(1)
go func(chart HelmChart) {
defer wg.Done()
valuesFile := filepath.Join(chart.ChartPath, "values.yaml")
helmreleaseFile := filepath.Join(chart.ChartPath, "helm-release.yaml")
helmRelease, err := LoadHelmRelease(helmreleaseFile)
if err != nil {
log.Info().Msgf("ERROR LOADING helmRelease for: %v", chart)
os.Exit(1)
}
if helmRelease == nil {
log.Info().Msgf("ERROR Empty helmRelease for: %v", chart)
os.Exit(1)
}
var wg sync.WaitGroup
for _, chart := range charts {
wg.Add(1)
go func(chart HelmChart) {
defer wg.Done()
valuesFile := filepath.Join(chart.ChartPath, "values.yaml")
helmreleaseFile := filepath.Join(chart.ChartPath, "helm-release.yaml")
helmRelease, err := LoadHelmRelease(helmreleaseFile)
if err != nil {
log.Info().Msgf("ERROR LOADING helmRelease for: %v", chart)
os.Exit(1)
}
if helmRelease == nil {
log.Info().Msgf("ERROR Empty helmRelease for: %v", chart)
os.Exit(1)
}
releaseName := helmRelease.Metadata.Name
if helmRelease.Spec.ReleaseName != "" {
releaseName = helmRelease.Spec.ReleaseName
}
releaseName := helmRelease.Metadata.Name
if helmRelease.Spec.ReleaseName != "" {
releaseName = helmRelease.Spec.ReleaseName
}
if HelmRepos[helmRelease.Spec.Chart.Spec.SourceRef.Name] == nil {
log.Info().Msgf("ERROR Empty helmRepo for: ", helmRelease.Spec.Chart.Spec.SourceRef.Name)
os.Exit(1)
}
if HelmRepos[helmRelease.Spec.Chart.Spec.SourceRef.Name] == nil {
log.Info().Msgf("ERROR Empty helmRepo for: ", helmRelease.Spec.Chart.Spec.SourceRef.Name)
os.Exit(1)
}
if HelmRepos[helmRelease.Spec.Chart.Spec.SourceRef.Name].Spec.URL == "" {
log.Info().Msgf("ERROR Empty repoURL for: ", helmRelease.Spec.Chart.Spec.SourceRef.Name)
os.Exit(1)
}
if HelmRepos[helmRelease.Spec.Chart.Spec.SourceRef.Name].Spec.URL == "" {
log.Info().Msgf("ERROR Empty repoURL for: ", helmRelease.Spec.Chart.Spec.SourceRef.Name)
os.Exit(1)
}
log.Info().Msgf("Bootstrap: Installing %s\n", helmRelease.Metadata.Name)
// We need to split install from dependency downloading, so we can parallel downloading
if err := HelmInstall(HelmRepos[helmRelease.Spec.Chart.Spec.SourceRef.Name].Spec.URL, helmRelease.Spec.Chart.Spec.Chart, releaseName, helmRelease.Metadata.Namespace, valuesFile, helmRelease.Spec.Chart.Spec.Version, chart.Retry, chart.Wait, true); err != nil {
if strings.Contains(err.Error(), "webhook") {
} else {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
if !async {
os.Exit(1)
}
}
}
}(chart)
if !async {
wg.Wait()
}
}
if async {
wg.Wait()
}
log.Info().Msgf("Bootstrap: Installing %s\n", helmRelease.Metadata.Name)
// We need to split install from dependency downloading, so we can parallel downloading
if err := HelmInstall(HelmRepos[helmRelease.Spec.Chart.Spec.SourceRef.Name].Spec.URL, helmRelease.Spec.Chart.Spec.Chart, releaseName, helmRelease.Metadata.Namespace, valuesFile, helmRelease.Spec.Chart.Spec.Version, chart.Retry, chart.Wait, true); err != nil {
if strings.Contains(err.Error(), "webhook") {
} else {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
if !async {
os.Exit(1)
}
}
}
}(chart)
if !async {
wg.Wait()
}
}
if async {
wg.Wait()
}
}
// UpgradeCharts upgrades Helm releases with provided Helm charts and repositories
func UpgradeCharts(charts []HelmChart, HelmRepos map[string]*HelmRepo, async bool) {
var wg sync.WaitGroup
for _, chart := range charts {
wg.Add(1)
go func(chart HelmChart) {
defer wg.Done()
var wg sync.WaitGroup
for _, chart := range charts {
wg.Add(1)
go func(chart HelmChart) {
defer wg.Done()
// Determine paths
valuesFile := filepath.Join(chart.ChartPath, "values.yaml")
helmreleaseFile := filepath.Join(chart.ChartPath, "helm-release.yaml")
// Determine paths
valuesFile := filepath.Join(chart.ChartPath, "values.yaml")
helmreleaseFile := filepath.Join(chart.ChartPath, "helm-release.yaml")
// Load Helm release
helmRelease, err := LoadHelmRelease(helmreleaseFile)
if err != nil {
fmt.Fprintf(os.Stderr, "Error loading Helm release for chart at %s: %v\n", chart.ChartPath, err)
if !async {
os.Exit(1)
}
return
}
if helmRelease == nil {
fmt.Fprintf(os.Stderr, "Empty Helm release for chart at %s\n", chart.ChartPath)
if !async {
os.Exit(1)
}
return
}
// Load Helm release
helmRelease, err := LoadHelmRelease(helmreleaseFile)
if err != nil {
fmt.Fprintf(os.Stderr, "Error loading Helm release for chart at %s: %v\n", chart.ChartPath, err)
if !async {
os.Exit(1)
}
return
}
if helmRelease == nil {
fmt.Fprintf(os.Stderr, "Empty Helm release for chart at %s\n", chart.ChartPath)
if !async {
os.Exit(1)
}
return
}
// Determine release name
releaseName := helmRelease.Metadata.Name
if helmRelease.Spec.ReleaseName != "" {
releaseName = helmRelease.Spec.ReleaseName
}
// Determine release name
releaseName := helmRelease.Metadata.Name
if helmRelease.Spec.ReleaseName != "" {
releaseName = helmRelease.Spec.ReleaseName
}
// Determine chart name
chartName := helmRelease.Spec.Chart.Spec.Chart
// Determine chart name
chartName := helmRelease.Spec.Chart.Spec.Chart
// Validate Helm repository
repoName := helmRelease.Spec.Chart.Spec.SourceRef.Name
repo, ok := HelmRepos[repoName]
if !ok || repo.Spec.URL == "" {
fmt.Fprintf(os.Stderr, "Empty or invalid Helm repository for %s\n", repoName)
if !async {
os.Exit(1)
}
return
}
// Validate Helm repository
repoName := helmRelease.Spec.Chart.Spec.SourceRef.Name
repo, ok := HelmRepos[repoName]
if !ok || repo.Spec.URL == "" {
fmt.Fprintf(os.Stderr, "Empty or invalid Helm repository for %s\n", repoName)
if !async {
os.Exit(1)
}
return
}
// Perform Helm upgrade
log.Info().Msgf("Upgrading %s\n", helmRelease.Metadata.Name)
err = HelmUpgrade(repo.Spec.URL, chartName, releaseName, helmRelease.Metadata.Namespace, valuesFile, helmRelease.Spec.Chart.Spec.Version, chart.Wait, true)
if err != nil {
fmt.Fprintf(os.Stderr, "Error upgrading %s: %v\n", helmRelease.Metadata.Name, err)
if !async {
os.Exit(1)
}
return
}
}(chart)
// Perform Helm upgrade
log.Info().Msgf("Upgrading %s\n", helmRelease.Metadata.Name)
err = HelmUpgrade(repo.Spec.URL, chartName, releaseName, helmRelease.Metadata.Namespace, valuesFile, helmRelease.Spec.Chart.Spec.Version, chart.Wait, true)
if err != nil {
fmt.Fprintf(os.Stderr, "Error upgrading %s: %v\n", helmRelease.Metadata.Name, err)
if !async {
os.Exit(1)
}
return
}
}(chart)
if !async {
wg.Wait()
}
}
if !async {
wg.Wait()
}
}
if async {
wg.Wait()
}
if async {
wg.Wait()
}
}
+47 -47
View File
@@ -1,76 +1,76 @@
package fluxhandler
import (
"io/ioutil"
"path/filepath"
"strings"
"io/ioutil"
"path/filepath"
"strings"
"github.com/rs/zerolog/log"
"gopkg.in/yaml.v3"
"github.com/rs/zerolog/log"
"gopkg.in/yaml.v3"
)
type HelmRepoMetadata struct {
Name string `yaml:"name,omitempty"`
Namespace string `yaml:"namespace,omitempty"`
Name string `yaml:"name,omitempty"`
Namespace string `yaml:"namespace,omitempty"`
}
type HelmRepoSpec struct {
Interval string `yaml:"interval,omitempty"`
URL string `yaml:"url,omitempty"`
Interval string `yaml:"interval,omitempty"`
URL string `yaml:"url,omitempty"`
}
type HelmRepo struct {
Metadata HelmRepoMetadata `yaml:"metadata,omitempty"`
Spec HelmRepoSpec `yaml:"spec,omitempty"`
Metadata HelmRepoMetadata `yaml:"metadata,omitempty"`
Spec HelmRepoSpec `yaml:"spec,omitempty"`
}
// LoadAllHelmRepos loads all .yaml files under a directory into a map of HelmRepo structs,
// ignoring kustomize.yaml and logging errors without stopping the entire process.
func LoadAllHelmRepos(dirPath string) (map[string]*HelmRepo, error) {
files, err := ioutil.ReadDir(dirPath)
if err != nil {
return nil, err
}
files, err := ioutil.ReadDir(dirPath)
if err != nil {
return nil, err
}
repos := make(map[string]*HelmRepo)
repos := make(map[string]*HelmRepo)
for _, file := range files {
if !file.IsDir() && strings.HasSuffix(file.Name(), ".yaml") {
// Ignore kustomize.yaml file
if file.Name() == "kustomize.yaml" {
continue
}
filename := filepath.Join(dirPath, file.Name())
repo, err := LoadHelmRepo(filename)
if err != nil {
// Log the error but continue processing other files
log.Info().Msgf("Error loading repo from file %s: %v\n", file.Name(), err)
continue
}
// Use metadata.name as the key in the map
repos[repo.Metadata.Name] = repo
}
}
for _, file := range files {
if !file.IsDir() && strings.HasSuffix(file.Name(), ".yaml") {
// Ignore kustomize.yaml file
if file.Name() == "kustomize.yaml" {
continue
}
filename := filepath.Join(dirPath, file.Name())
repo, err := LoadHelmRepo(filename)
if err != nil {
// Log the error but continue processing other files
log.Info().Msgf("Error loading repo from file %s: %v\n", file.Name(), err)
continue
}
// Use metadata.name as the key in the map
repos[repo.Metadata.Name] = repo
}
}
return repos, nil
return repos, nil
}
// LoadHelmRepo loads a single HelmRepo struct from a YAML file
func LoadHelmRepo(filename string) (*HelmRepo, error) {
// Read YAML file
yamlFile, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
// Read YAML file
yamlFile, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
// Initialize HelmRepo struct
repo := &HelmRepo{}
// Initialize HelmRepo struct
repo := &HelmRepo{}
// Unmarshal YAML into struct
err = yaml.Unmarshal(yamlFile, repo)
if err != nil {
return nil, err
}
// Unmarshal YAML into struct
err = yaml.Unmarshal(yamlFile, repo)
if err != nil {
return nil, err
}
return repo, nil
return repo, nil
}
+112 -112
View File
@@ -1,28 +1,28 @@
package fluxhandler
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
// check if a file exists
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
// createKsYaml creates ks.yaml file with Flux Kustomization
func createKsYaml(path, parentFolder string) error {
// Ensure the path uses forward slashes
linuxPath := filepath.ToSlash(path)
// Ensure the path uses forward slashes
linuxPath := filepath.ToSlash(path)
content := fmt.Sprintf(`apiVersion: kustomize.toolkit.fluxcd.io/v1
content := fmt.Sprintf(`apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: %s
@@ -36,125 +36,125 @@ spec:
name: cluster
`, parentFolder, linuxPath)
return ioutil.WriteFile(filepath.Join(path, "ks.yaml"), []byte(content), 0644)
return ioutil.WriteFile(filepath.Join(path, "ks.yaml"), []byte(content), 0644)
}
// createOrUpdateKustomizationYaml creates or updates kustomization.yaml file
func createOrUpdateKustomizationYaml(path string) error {
kustomizationPath := filepath.Join(path, "kustomization.yaml")
var content string
if fileExists(kustomizationPath) {
// Read existing kustomization.yaml file
data, err := ioutil.ReadFile(kustomizationPath)
if err != nil {
return err
}
content = string(data)
} else {
content = `apiVersion: kustomize.config.k8s.io/v1beta1
kustomizationPath := filepath.Join(path, "kustomization.yaml")
var content string
if fileExists(kustomizationPath) {
// Read existing kustomization.yaml file
data, err := ioutil.ReadFile(kustomizationPath)
if err != nil {
return err
}
content = string(data)
} else {
content = `apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
`
}
}
// List all files and folders in the current directory
files, err := ioutil.ReadDir(path)
if err != nil {
return err
}
// List all files and folders in the current directory
files, err := ioutil.ReadDir(path)
if err != nil {
return err
}
// Collect resources to add to kustomization.yaml
var resources []string
for _, file := range files {
name := file.Name()
// Ignore kustomization.yaml and ks.yaml files
if name == "kustomization.yaml" || name == "ks.yaml" {
continue
}
// Include only YAML files and directories
if strings.HasSuffix(name, ".yaml") || file.IsDir() {
if file.IsDir() && fileExists(filepath.Join(path, name, "ks.yaml")) {
// Update folder entry to include ks.yaml
name = fmt.Sprintf("%s/ks.yaml", name)
}
// Check if the file/folder is already listed
if !strings.Contains(content, name) {
if name == "namespace.yaml" {
resources = append([]string{name}, resources...)
} else {
resources = append(resources, name)
}
}
}
}
// Collect resources to add to kustomization.yaml
var resources []string
for _, file := range files {
name := file.Name()
// Ignore kustomization.yaml and ks.yaml files
if name == "kustomization.yaml" || name == "ks.yaml" {
continue
}
// Include only YAML files and directories
if strings.HasSuffix(name, ".yaml") || file.IsDir() {
if file.IsDir() && fileExists(filepath.Join(path, name, "ks.yaml")) {
// Update folder entry to include ks.yaml
name = fmt.Sprintf("%s/ks.yaml", name)
}
// Check if the file/folder is already listed
if !strings.Contains(content, name) {
if name == "namespace.yaml" {
resources = append([]string{name}, resources...)
} else {
resources = append(resources, name)
}
}
}
}
// Update kustomization.yaml content
for _, resource := range resources {
content += fmt.Sprintf(" - %s\n", resource)
}
// Update kustomization.yaml content
for _, resource := range resources {
content += fmt.Sprintf(" - %s\n", resource)
}
contentLines := strings.Split(content, "\n")
for _, item := range contentLines {
contentLines := strings.Split(content, "\n")
for _, item := range contentLines {
if strings.HasSuffix(item, "/ks.yaml") {
prefix := strings.TrimSuffix(item, "/ks.yaml")
if strings.HasSuffix(item, "/ks.yaml") {
prefix := strings.TrimSuffix(item, "/ks.yaml")
// Remove other resources with the same prefix from content
var updatedContent []string
for _, line := range contentLines {
if line != prefix {
updatedContent = append(updatedContent, line)
}
}
contentLines = updatedContent
}
}
content = strings.Join(contentLines, "\n")
// Remove other resources with the same prefix from content
var updatedContent []string
for _, line := range contentLines {
if line != prefix {
updatedContent = append(updatedContent, line)
}
}
contentLines = updatedContent
}
}
content = strings.Join(contentLines, "\n")
// Write back the updated kustomization.yaml file
return ioutil.WriteFile(kustomizationPath, []byte(content), 0644)
// Write back the updated kustomization.yaml file
return ioutil.WriteFile(kustomizationPath, []byte(content), 0644)
}
// processDirectory processes each directory recursively
func ProcessDirectory(path string) error {
hasAppFolder := false
hasKsYaml := false
hasAppFolder := false
hasKsYaml := false
// Check for "app" folder and "ks.yaml" file
files, err := ioutil.ReadDir(path)
if err != nil {
return err
}
for _, file := range files {
if file.IsDir() && file.Name() == "app" {
hasAppFolder = true
}
if file.Name() == "ks.yaml" {
hasKsYaml = true
}
}
// Check for "app" folder and "ks.yaml" file
files, err := ioutil.ReadDir(path)
if err != nil {
return err
}
for _, file := range files {
if file.IsDir() && file.Name() == "app" {
hasAppFolder = true
}
if file.Name() == "ks.yaml" {
hasKsYaml = true
}
}
// Create ks.yaml if "app" folder exists and ks.yaml does not exist
if hasAppFolder && !hasKsYaml {
parentFolder := filepath.Base(path)
if err := createKsYaml(path, parentFolder); err != nil {
return err
}
} else if !hasKsYaml { // Only create/update kustomization.yaml if ks.yaml does not exist
// Create or update kustomization.yaml
if err := createOrUpdateKustomizationYaml(path); err != nil {
return err
}
}
// Create ks.yaml if "app" folder exists and ks.yaml does not exist
if hasAppFolder && !hasKsYaml {
parentFolder := filepath.Base(path)
if err := createKsYaml(path, parentFolder); err != nil {
return err
}
} else if !hasKsYaml { // Only create/update kustomization.yaml if ks.yaml does not exist
// Create or update kustomization.yaml
if err := createOrUpdateKustomizationYaml(path); err != nil {
return err
}
}
// Recurse into subdirectories
for _, file := range files {
if file.IsDir() {
if err := ProcessDirectory(filepath.Join(path, file.Name())); err != nil {
return err
}
}
}
// Recurse into subdirectories
for _, file := range files {
if file.IsDir() {
if err := ProcessDirectory(filepath.Join(path, file.Name())); err != nil {
return err
}
}
}
return nil
return nil
}
+165 -165
View File
@@ -1,238 +1,238 @@
package fluxhandler
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/rs/zerolog/log"
"github.com/rs/zerolog/log"
"github.com/truecharts/public/clustertool/pkg/helper"
"golang.org/x/crypto/ssh"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/yaml"
"github.com/truecharts/public/clustertool/pkg/helper"
"golang.org/x/crypto/ssh"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/yaml"
)
// Define a struct to map the YAML content
type Config struct {
StringData map[string]string `yaml:"stringData"`
StringData map[string]string `yaml:"stringData"`
}
// CreateGitSecret generates a Kubernetes secret YAML file and a public key text file.
func CreateGitSecret(gitURL string) error {
if gitURL == "" {
gitURL = "github.com"
}
if gitURL == "" {
gitURL = "github.com"
}
// Paths for files
secretPath := filepath.Join(helper.ClusterPath, "kubernetes", "flux-system", "flux", "deploykey.secret.yaml")
publicKeyPath := filepath.Join(".", "ssh-public-key.txt")
// Paths for files
secretPath := filepath.Join(helper.ClusterPath, "kubernetes", "flux-system", "flux", "deploykey.secret.yaml")
publicKeyPath := filepath.Join(".", "ssh-public-key.txt")
// Check if secret YAML already exists
if _, err := os.Stat(secretPath); os.IsNotExist(err) {
// Generate ECDSA private key
privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
if err != nil {
return fmt.Errorf("failed to generate ECDSA private key: %w", err)
}
// Check if secret YAML already exists
if _, err := os.Stat(secretPath); os.IsNotExist(err) {
// Generate ECDSA private key
privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
if err != nil {
return fmt.Errorf("failed to generate ECDSA private key: %w", err)
}
// Encode private key to PEM format
privateKeyPEMBlock, err := pemBlockForKey(privateKey)
if err != nil {
return fmt.Errorf("failed to create PEM block for private key: %w", err)
}
// Encode private key to PEM format
privateKeyPEMBlock, err := pemBlockForKey(privateKey)
if err != nil {
return fmt.Errorf("failed to create PEM block for private key: %w", err)
}
// Generate OpenSSH formatted public key
publicKey, err := publicKeyToOpenSSH(&privateKey.PublicKey)
if err != nil {
return fmt.Errorf("failed to generate OpenSSH public key: %w", err)
}
// Generate OpenSSH formatted public key
publicKey, err := publicKeyToOpenSSH(&privateKey.PublicKey)
if err != nil {
return fmt.Errorf("failed to generate OpenSSH public key: %w", err)
}
// Write public key to file
err = os.WriteFile(publicKeyPath, []byte(publicKey), 0644)
if err != nil {
return fmt.Errorf("failed to write public key to file: %w", err)
}
log.Info().Msgf("Public key saved to: %s\n", publicKeyPath)
// Write public key to file
err = os.WriteFile(publicKeyPath, []byte(publicKey), 0644)
if err != nil {
return fmt.Errorf("failed to write public key to file: %w", err)
}
log.Info().Msgf("Public key saved to: %s\n", publicKeyPath)
// Generate known_hosts entry
knownHosts := getKnownHostsEntry(gitURL)
// Generate known_hosts entry
knownHosts := getKnownHostsEntry(gitURL)
// Generate Kubernetes secret YAML content
secret := map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"name": "deploy-key",
"namespace": "flux-system",
},
"stringData": map[string]interface{}{
"identity": string(privateKeyPEMBlock),
"identity.pub": publicKey,
"known_hosts": knownHosts,
},
"type": string(corev1.SecretTypeOpaque),
}
// Generate Kubernetes secret YAML content
secret := map[string]interface{}{
"apiVersion": "v1",
"kind": "Secret",
"metadata": map[string]interface{}{
"name": "deploy-key",
"namespace": "flux-system",
},
"stringData": map[string]interface{}{
"identity": string(privateKeyPEMBlock),
"identity.pub": publicKey,
"known_hosts": knownHosts,
},
"type": string(corev1.SecretTypeOpaque),
}
secretYAML, err := yaml.Marshal(secret)
if err != nil {
return fmt.Errorf("failed to marshal secret to YAML: %w", err)
}
secretYAML, err := yaml.Marshal(secret)
if err != nil {
return fmt.Errorf("failed to marshal secret to YAML: %w", err)
}
// Write Kubernetes secret YAML to file
err = os.MkdirAll(filepath.Dir(secretPath), 0755)
if err != nil {
return fmt.Errorf("failed to create directories: %w", err)
}
err = os.WriteFile(secretPath, secretYAML, 0644)
if err != nil {
return fmt.Errorf("failed to write secret YAML to file: %w", err)
}
log.Info().Msgf("Kubernetes secret YAML saved to: %s\n", secretPath)
} else {
// Secret YAML already exists, check if public key file exists
if _, err := os.Stat(publicKeyPath); os.IsNotExist(err) {
// Public key file does not exist, generate from existing secret
secretYAML, err := os.ReadFile(secretPath)
if err != nil {
return fmt.Errorf("failed to read existing secret YAML: %w", err)
}
// Write Kubernetes secret YAML to file
err = os.MkdirAll(filepath.Dir(secretPath), 0755)
if err != nil {
return fmt.Errorf("failed to create directories: %w", err)
}
err = os.WriteFile(secretPath, secretYAML, 0644)
if err != nil {
return fmt.Errorf("failed to write secret YAML to file: %w", err)
}
log.Info().Msgf("Kubernetes secret YAML saved to: %s\n", secretPath)
} else {
// Secret YAML already exists, check if public key file exists
if _, err := os.Stat(publicKeyPath); os.IsNotExist(err) {
// Public key file does not exist, generate from existing secret
secretYAML, err := os.ReadFile(secretPath)
if err != nil {
return fmt.Errorf("failed to read existing secret YAML: %w", err)
}
var secret corev1.Secret
if err := yaml.Unmarshal(secretYAML, &secret); err != nil {
return fmt.Errorf("failed to unmarshal secret YAML: %w", err)
}
var secret corev1.Secret
if err := yaml.Unmarshal(secretYAML, &secret); err != nil {
return fmt.Errorf("failed to unmarshal secret YAML: %w", err)
}
if ppk, ok := secret.StringData["identity.pub"]; ok {
err = os.WriteFile(publicKeyPath, []byte(ppk), 0644)
if err != nil {
return fmt.Errorf("failed to write public key to file: %w", err)
}
log.Info().Msgf("Public key saved to: %s\n", publicKeyPath)
} else {
return fmt.Errorf("identity.pub not found in existing secret YAML")
}
} else {
log.Info().Msgf("Public key file already exists: %s\n", publicKeyPath)
}
}
if ppk, ok := secret.StringData["identity.pub"]; ok {
err = os.WriteFile(publicKeyPath, []byte(ppk), 0644)
if err != nil {
return fmt.Errorf("failed to write public key to file: %w", err)
}
log.Info().Msgf("Public key saved to: %s\n", publicKeyPath)
} else {
return fmt.Errorf("identity.pub not found in existing secret YAML")
}
} else {
log.Info().Msgf("Public key file already exists: %s\n", publicKeyPath)
}
}
return nil
return nil
}
func CreateSshPatch() {
log.Info().Msg("generating talospatch for flux ssh key...")
// Paths to the YAML files
secretPath := filepath.Join(helper.ClusterPath, "kubernetes", "flux-system", "flux", "deploykey.secret.yaml")
sopsPatchPath := filepath.Join(helper.ClusterPath, "talos", "patches", "sopssecret.yaml")
log.Info().Msg("generating talospatch for flux ssh key...")
// Paths to the YAML files
secretPath := filepath.Join(helper.ClusterPath, "kubernetes", "flux-system", "flux", "deploykey.secret.yaml")
sopsPatchPath := filepath.Join(helper.ClusterPath, "talos", "patches", "sopssecret.yaml")
// Read the YAML file
yamlFile, err := ioutil.ReadFile(secretPath)
if err != nil {
log.Fatal().Err(err).Msg("error: %v")
}
// Read the YAML file
yamlFile, err := ioutil.ReadFile(secretPath)
if err != nil {
log.Fatal().Err(err).Msg("error: %v")
}
// Unmarshal the YAML content into a Config struct
var config Config
err = yaml.Unmarshal(yamlFile, &config)
if err != nil {
log.Fatal().Err(err).Msg("error: %v")
}
// Unmarshal the YAML content into a Config struct
var config Config
err = yaml.Unmarshal(yamlFile, &config)
if err != nil {
log.Fatal().Err(err).Msg("error: %v")
}
// Extract the stringData content and convert it to a multi-line string
stringData, err := yaml.Marshal(config.StringData)
if err != nil {
log.Fatal().Err(err).Msg("error: %v")
}
// Extract the stringData content and convert it to a multi-line string
stringData, err := yaml.Marshal(config.StringData)
if err != nil {
log.Fatal().Err(err).Msg("error: %v")
}
// Convert byte array to string
deployKeyData := string(stringData)
// Convert byte array to string
deployKeyData := string(stringData)
// Replace the placeholder in sopspath.yaml
err = ReplacePlaceholder(sopsPatchPath, "REPLACEWITHDEPLOYKEY", indentYaml(deployKeyData, " "))
if err != nil {
log.Fatal().Err(err).Msg("Failed to replace placeholder: %v")
}
// Replace the placeholder in sopspath.yaml
err = ReplacePlaceholder(sopsPatchPath, "REPLACEWITHDEPLOYKEY", indentYaml(deployKeyData, " "))
if err != nil {
log.Fatal().Err(err).Msg("Failed to replace placeholder: %v")
}
}
// indentYaml indents each line of the YAML string with the specified indentation.
func indentYaml(yamlStr, indent string) string {
lines := strings.Split(yamlStr, "\n")
for i, line := range lines {
if line != "" {
lines[i] = indent + line
}
}
return strings.Join(lines, "\n")
lines := strings.Split(yamlStr, "\n")
for i, line := range lines {
if line != "" {
lines[i] = indent + line
}
}
return strings.Join(lines, "\n")
}
// ReplacePlaceholder replaces the placeholder in the file at the given path with the specified replacement string.
func ReplacePlaceholder(filePath, placeholder, replacement string) error {
data, err := ioutil.ReadFile(filePath)
if err != nil {
return err
}
data, err := ioutil.ReadFile(filePath)
if err != nil {
return err
}
fileContent := string(data)
fileContent = strings.Replace(fileContent, placeholder, replacement, -1)
fileContent := string(data)
fileContent = strings.Replace(fileContent, placeholder, replacement, -1)
return ioutil.WriteFile(filePath, []byte(fileContent), 0644)
return ioutil.WriteFile(filePath, []byte(fileContent), 0644)
}
// pemBlockForKey creates a PEM block for the given private key
func pemBlockForKey(key *ecdsa.PrivateKey) ([]byte, error) {
der, err := x509.MarshalECPrivateKey(key)
if err != nil {
return nil, fmt.Errorf("failed to marshal ECDSA private key: %w", err)
}
der, err := x509.MarshalECPrivateKey(key)
if err != nil {
return nil, fmt.Errorf("failed to marshal ECDSA private key: %w", err)
}
block := &pem.Block{
Type: "EC PRIVATE KEY",
Bytes: der,
}
return pem.EncodeToMemory(block), nil
block := &pem.Block{
Type: "EC PRIVATE KEY",
Bytes: der,
}
return pem.EncodeToMemory(block), nil
}
// publicKeyToOpenSSH converts an ECDSA public key to OpenSSH format
func publicKeyToOpenSSH(pub *ecdsa.PublicKey) (string, error) {
pubKey, err := ssh.NewPublicKey(pub)
if err != nil {
return "", fmt.Errorf("failed to convert ECDSA public key to SSH format: %w", err)
}
return string(ssh.MarshalAuthorizedKey(pubKey)), nil
pubKey, err := ssh.NewPublicKey(pub)
if err != nil {
return "", fmt.Errorf("failed to convert ECDSA public key to SSH format: %w", err)
}
return string(ssh.MarshalAuthorizedKey(pubKey)), nil
}
// getKnownHostsEntry generates the known_hosts entry for the given URL
func getKnownHostsEntry(url string) string {
if url == "github.com" {
return getGithubKnownHostsEntry()
}
return generateKnownHostsEntry(url)
if url == "github.com" {
return getGithubKnownHostsEntry()
}
return generateKnownHostsEntry(url)
}
// getGithubKnownHostsEntry generates the known_hosts entry specifically for github.com
func getGithubKnownHostsEntry() string {
return "github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg="
return "github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg="
}
// generateKnownHostsEntry generates an SSH known_hosts entry for the given URL
func generateKnownHostsEntry(url string) string {
return fmt.Sprintf("%s ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=", url)
return fmt.Sprintf("%s ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=", url)
}
// encodeToBase64 encodes data to a base64 string
func encodeToBase64(data []byte) string {
return string(data)
return string(data)
}
// decodeBase64 decodes a base64 string
func decodeBase64(data string) ([]byte, error) {
return []byte(data), nil
return []byte(data), nil
}