mirror of
https://github.com/truecharts/charts.git
synced 2026-07-06 15:51:23 -03:00
522 lines
14 KiB
Go
522 lines
14 KiB
Go
package initfiles
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
"gopkg.in/yaml.v3"
|
|
|
|
age "filippo.io/age"
|
|
"github.com/trueforge-org/truecharts/clustertool/pkg/fluxhandler"
|
|
"github.com/trueforge-org/truecharts/clustertool/pkg/helper"
|
|
"github.com/trueforge-org/truecharts/clustertool/pkg/talassist"
|
|
corev1 "k8s.io/api/core/v1"
|
|
)
|
|
|
|
func InitFiles() error {
|
|
removeRunAgainFile()
|
|
ageGen()
|
|
genRootFiles()
|
|
genBaseFiles()
|
|
UpdateRootFiles()
|
|
UpdateBaseFiles()
|
|
talassist.GenSchema()
|
|
GenPatches()
|
|
genKubernetes()
|
|
GenTalEnvConfigMap()
|
|
UpdateGitRepo()
|
|
fluxhandler.CreateGitSecret(helper.TalEnv["GITHUB_REPOSITORY"])
|
|
GenSopsSecret()
|
|
if err := fluxhandler.ProcessDirectory(path.Join(helper.ClusterPath, "kubernetes")); err != nil {
|
|
log.Error().Msgf("Error: %v", err)
|
|
}
|
|
if err := fluxhandler.ProcessDirectory(path.Join(helper.ClusterPath, "kubernetes")); err != nil {
|
|
log.Error().Msgf("Error: %v", err)
|
|
} else {
|
|
log.Info().Msg("Kustomizations processed successfully.")
|
|
}
|
|
|
|
helper.CreateEncrPreCommitHook()
|
|
log.Info().Msg("Init: Completed Successfully!")
|
|
return nil
|
|
}
|
|
|
|
func genKubernetes() error {
|
|
|
|
err := helper.CopyDir(helper.KubeCache, helper.ClusterPath+"/kubernetes", false)
|
|
if err != nil {
|
|
log.Info().Msgf("Error: %v", err)
|
|
} else {
|
|
log.Info().Msgf("Kubernetes files copied successfully.")
|
|
}
|
|
|
|
helper.ReplaceInFile(path.Join(helper.ClusterPath, "/kubernetes/flux-entry.yaml"), "REPLACEWITHCLUSTERNAME", helper.ClusterName)
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msgf("Error: %s", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func GenTalEnvConfigMap() error {
|
|
|
|
log.Info().Msg("Creating TalEnv configmap reference 'clustersettings'.")
|
|
// Read the content of the talenv.yaml file
|
|
talenvContent, err := os.ReadFile(helper.ClusterEnvFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Convert the file content to a string and split it into lines
|
|
talenvLines := strings.Split(string(talenvContent), "\n")
|
|
|
|
// Add indentation to each line
|
|
for i, line := range talenvLines {
|
|
talenvLines[i] = " " + line
|
|
}
|
|
indentClusterName := " CLUSTERNAME: " + helper.ClusterName
|
|
talenvLines = append(talenvLines, indentClusterName)
|
|
|
|
// Join the indented lines back into a single string
|
|
indentedTalenvContent := strings.Join(talenvLines, "\n")
|
|
|
|
clusterSettings := filepath.Join("flux-system", "flux", "clustersettings.secret.yaml")
|
|
clusterSettingsDest := filepath.Join(helper.ClusterPath+"/kubernetes", clusterSettings)
|
|
clusterSettingsSrc := filepath.Join(helper.KubeCache, clusterSettings)
|
|
os.MkdirAll(filepath.Join(helper.ClusterPath, "/kubernetes", "flux-system", "flux"), os.ModePerm)
|
|
err = helper.CopyFile(clusterSettingsSrc, clusterSettingsDest, true)
|
|
log.Debug().Msgf("clusterSettingsDest %v", clusterSettingsDest)
|
|
helper.ReplaceInFile(clusterSettingsDest, "REPLACEWITHENV", indentedTalenvContent)
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("Error: %s")
|
|
}
|
|
log.Info().Msg("Configmap reference Created.")
|
|
return nil
|
|
}
|
|
|
|
func UpdateGitRepo() {
|
|
if helper.TalEnv["GITHUB_REPOSITORY"] != "" {
|
|
repoPath := filepath.Join("repositories", "git", "this-repo.yaml")
|
|
gitrepo := FormatGitURL(helper.TalEnv["GITHUB_REPOSITORY"])
|
|
helper.ReplaceInFile(repoPath, "ssh://REPLACEWITHGITREPO", gitrepo)
|
|
}
|
|
}
|
|
|
|
// FormatGitURL formats the input Git URL according to the specified rules.
|
|
func FormatGitURL(input string) string {
|
|
// Remove "https://" prefix if present
|
|
input = strings.TrimPrefix(input, "https://")
|
|
|
|
if !strings.HasPrefix(input, "ssh://") {
|
|
input = "ssh://" + input
|
|
}
|
|
|
|
// Ensure input starts with "ssh://git@"
|
|
if !strings.HasPrefix(input, "ssh://git@") {
|
|
// Prepend "ssh://git@" if neither "ssh://" nor "git@" is present
|
|
input = strings.Replace(input, "ssh://", "ssh://git@", 1)
|
|
}
|
|
|
|
if strings.Contains(input, "git@git@") {
|
|
input = strings.Replace(input, "git@git@", "git@", 1)
|
|
}
|
|
|
|
// Compile a regex to match and replace the URL pattern
|
|
re := regexp.MustCompile(`^ssh://git@([^:/]+)([:/])([\w-]+)/([\w-]+)\.git$`)
|
|
matches := re.FindStringSubmatch(input)
|
|
|
|
if len(matches) == 5 {
|
|
// Determine the user and repo based on the separator used
|
|
user := matches[3] // Always captured as part of the matched group
|
|
repo := matches[4] // Always captured as part of the matched group
|
|
return fmt.Sprintf("ssh://git@%s/%s/%s.git", matches[1], user, repo)
|
|
}
|
|
|
|
return input // Return the input as is if it doesn't match
|
|
}
|
|
|
|
func genBaseFiles() error {
|
|
clusterEnvPresent := false
|
|
|
|
if _, err := os.Stat(helper.ClusterEnvFile); err == nil {
|
|
clusterEnvPresent = true
|
|
log.Debug().Msg("Detected existing cluster, continuing")
|
|
} else if os.IsNotExist(err) {
|
|
createRunAgainFile()
|
|
log.Warn().Msg("New cluster detected, creating clusterenv.yaml\n Please fill out ClusterEnv.yaml and run init again, after setting-up clusterenv.yaml!")
|
|
} else {
|
|
log.Fatal().Err(err).Msgf("Error checking clusterenv file: %s", err)
|
|
return err
|
|
}
|
|
|
|
err := helper.CopyDir(helper.BaseCache, helper.ClusterPath+"", false)
|
|
if err != nil {
|
|
log.Error().Msgf("Error: %v", err)
|
|
} else {
|
|
log.Info().Msg("Base files copied successfully.")
|
|
}
|
|
|
|
if !clusterEnvPresent {
|
|
os.Exit(0)
|
|
}
|
|
|
|
log.Info().Msg("basefiles successfully altered.")
|
|
return nil
|
|
}
|
|
|
|
// Create the "RUNAGAIN" file
|
|
func createRunAgainFile() {
|
|
file, err := os.Create("RUNAGAIN")
|
|
if err != nil {
|
|
log.Err(err).Msg("error creating runagain file...")
|
|
return
|
|
}
|
|
defer file.Close()
|
|
return
|
|
}
|
|
|
|
// Remove the "RUNAGAIN" file if it exists
|
|
func removeRunAgainFile() error {
|
|
if CheckRunAgainFileExists() {
|
|
err := os.Remove("RUNAGAIN")
|
|
if err != nil {
|
|
log.Err(err).Msg("error removing runagain file...")
|
|
return err
|
|
}
|
|
log.Debug().Msg("RUNAGAIN file removed.")
|
|
} else {
|
|
log.Debug().Msg("RUNAGAIN file does not exist.")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Check if the "RUNAGAIN" file exists
|
|
func CheckRunAgainFileExists() bool {
|
|
_, err := os.Stat("RUNAGAIN")
|
|
return !os.IsNotExist(err)
|
|
}
|
|
|
|
func UpdateBaseFiles() error {
|
|
log.Info().Msg("Updating Base files for cluster: helper.ClusterPath")
|
|
// Read filenames in source directory
|
|
sourceFiles, err := readFilenamesInDir(helper.BaseCache)
|
|
if err != nil {
|
|
log.Info().Msgf("Error reading source directory: %v\n", err)
|
|
return err
|
|
}
|
|
|
|
// Process each file in the target directory
|
|
for _, filename := range sourceFiles {
|
|
sourceFilePath := filepath.Join(helper.BaseCache, filename)
|
|
targetFilePath := filepath.Join(helper.ClusterPath+"", helper.ReplaceDotInFilename(filename))
|
|
helper.ReplaceContentBetweenLines(targetFilePath, sourceFilePath, "## Do not edit between this and DO NOT REMOVE", "## DO NOT REMOVE: Personal setting go under this line")
|
|
}
|
|
log.Info().Msg("basefiles successfully updated.")
|
|
|
|
CheckEnvVariables()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func genRootFiles() error {
|
|
|
|
err := helper.CopyDir(helper.RootCache, "./", false)
|
|
if err != nil {
|
|
log.Info().Msgf("Error: %v", err)
|
|
} else {
|
|
log.Info().Msg("Root files copied successfully.")
|
|
}
|
|
|
|
agePubKey, err := GetPubKey()
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("error: %v")
|
|
}
|
|
log.Info().Msgf("Public Key: %v", agePubKey)
|
|
helper.ReplaceInFile(".sops.yaml", "REPLACEME", agePubKey)
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("Error: %s")
|
|
}
|
|
|
|
log.Info().Msg("basefiles successfully altered.")
|
|
return nil
|
|
}
|
|
|
|
func UpdateRootFiles() error {
|
|
// Read filenames in source directory
|
|
sourceFiles, err := readFilenamesInDir(helper.RootCache)
|
|
if err != nil {
|
|
log.Info().Msgf("Error reading source directory: %v\n", err)
|
|
return err
|
|
}
|
|
|
|
// Process each file in the target directory
|
|
for _, filename := range sourceFiles {
|
|
sourceFilePath := filepath.Join(helper.BaseCache, filename)
|
|
targetFilePath := filepath.Join("./", helper.ReplaceDotInFilename(filename))
|
|
helper.ReplaceContentBetweenLines(targetFilePath, sourceFilePath, "## Do not edit between this and DO NOT REMOVE", "## DO NOT REMOVE: Personal setting go under this line")
|
|
}
|
|
log.Info().Msg("rootfiles successfully updated.")
|
|
|
|
agePubKey, err := GetPubKey()
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("error: %v")
|
|
}
|
|
|
|
helper.ReplaceInFile(".sops.yaml", "REPLACEME", agePubKey)
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("Error: %s")
|
|
}
|
|
|
|
CheckEnvVariables()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
// Function to read all filenames in a directory
|
|
func readFilenamesInDir(dir string) ([]string, error) {
|
|
files, err := ioutil.ReadDir(dir)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var filenames []string
|
|
for _, file := range files {
|
|
if !file.IsDir() {
|
|
filenames = append(filenames, file.Name())
|
|
}
|
|
}
|
|
return filenames, nil
|
|
}
|
|
|
|
func ResetBootstrapValues() error {
|
|
LoadTalEnv(false)
|
|
err := helper.CopyDirFiltered(helper.KubeCache, helper.ClusterPath+"/kubernetes", true, `^bootstrap-values\.yaml.ct$`)
|
|
if err != nil {
|
|
log.Info().Msg("Error:")
|
|
}
|
|
|
|
err2 := helper.EnvSubstRecursive(helper.ClusterPath+"/kubernetes", `^bootstrap-values\.yaml.ct$`, helper.TalEnv)
|
|
if err2 != nil {
|
|
log.Info().Msg("Error:")
|
|
}
|
|
|
|
log.Info().Msg("Bootstrap-Values.yaml Files reset successfully.")
|
|
return nil
|
|
}
|
|
|
|
func GenPatches() error {
|
|
|
|
err := helper.CopyDir(helper.PatchCache, path.Join(helper.ClusterPath, "/talos/patches"), true)
|
|
if err != nil {
|
|
log.Info().Msg("Error:")
|
|
} else {
|
|
log.Info().Msg("Patch files copied successfully.")
|
|
}
|
|
|
|
ageSecKey, err := GetSecKey()
|
|
helper.ReplaceInFile(filepath.Join(helper.ClusterPath+"/talos/patches", "sopssecret.yaml"), "REPLACEWITHSOPS", ageSecKey)
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("Error: %s")
|
|
}
|
|
|
|
setDocker()
|
|
|
|
return nil
|
|
}
|
|
|
|
func setDocker() {
|
|
// Assuming this is part of your function
|
|
if helper.TalEnv["DOCKERHUB_USER"] != "" && helper.TalEnv["DOCKERHUB_PASSWORD"] != "" {
|
|
// Prepare the content to append
|
|
configContent := fmt.Sprintf(`
|
|
# Add Dockerhub Login
|
|
config
|
|
registry-1.docker.io:
|
|
auth:
|
|
username: "%s"
|
|
password: "%s"
|
|
docker.io:
|
|
auth:
|
|
username: "%s"
|
|
password: "%s"`, helper.TalEnv["DOCKERHUB_USER"], helper.TalEnv["DOCKERHUB_PASSWORD"], helper.TalEnv["DOCKERHUB_USER"], helper.TalEnv["DOCKERHUB_PASSWORD"])
|
|
|
|
// Open the file in append mode or create it if it doesn't exist
|
|
file, err := os.OpenFile(filepath.Join(helper.ClusterPath+"/talos/patches", "all.yaml"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("Error opening file: %s")
|
|
}
|
|
defer file.Close()
|
|
|
|
// Write the content to the file
|
|
if _, err := file.Write([]byte(configContent)); err != nil {
|
|
log.Fatal().Err(err).Msg("Error writing to file: %s")
|
|
}
|
|
} else {
|
|
// Optional: Append a note if the environment variables are not set
|
|
emptyContent := `# No DockerHub credentials provided
|
|
`
|
|
file, err := os.OpenFile(filepath.Join(helper.ClusterPath+"/talos/patches", "all.yaml"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("Error opening file: %s")
|
|
}
|
|
defer file.Close()
|
|
|
|
if _, err := file.Write([]byte(emptyContent)); err != nil {
|
|
log.Fatal().Err(err).Msg("Error writing to file: %s")
|
|
}
|
|
}
|
|
}
|
|
|
|
func ageGen() error {
|
|
outFlag := "age.agekey"
|
|
|
|
if _, err := os.Stat(outFlag); err == nil {
|
|
|
|
} else if errors.Is(err, os.ErrNotExist) {
|
|
out := os.Stdout
|
|
f, err := os.OpenFile(outFlag, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("failed to open output file %q: %v")
|
|
}
|
|
defer func() {
|
|
if err := f.Close(); err != nil {
|
|
log.Fatal().Err(err).Msg("failed to close output file %q: %v")
|
|
}
|
|
}()
|
|
out = f
|
|
if fi, err := out.Stat(); err == nil && fi.Mode().IsRegular() && fi.Mode().Perm()&0004 != 0 {
|
|
log.Info().Msgf("writing secret key to a world-readable file\n")
|
|
}
|
|
|
|
k, err := age.GenerateX25519Identity()
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("internal error: %v")
|
|
}
|
|
|
|
fmt.Fprintf(out, "# created: %s\n", time.Now().Format(time.RFC3339))
|
|
fmt.Fprintf(out, "# public key: %s\n", k.Recipient())
|
|
fmt.Fprintf(out, "%s\n", k)
|
|
|
|
} else {
|
|
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func GetPubKey() (string, error) {
|
|
// Open the file
|
|
filename := "age.agekey"
|
|
file, err := os.Open(filename)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to open file: %v", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
var publicKey string
|
|
|
|
// Read the file line by line
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
// Find the line with the public key
|
|
if strings.HasPrefix(line, "# public key:") {
|
|
parts := strings.Split(line, ": ")
|
|
if len(parts) == 2 {
|
|
publicKey = parts[1]
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
return "", fmt.Errorf("failed to scan file: %v", err)
|
|
}
|
|
|
|
if publicKey == "" {
|
|
return "", fmt.Errorf("public key not found")
|
|
}
|
|
|
|
return publicKey, nil
|
|
}
|
|
|
|
// getSecretKeyFromFile reads the specified file and returns the secret key found within it.
|
|
func GetSecKey() (string, error) {
|
|
// Open the file
|
|
filename := "age.agekey"
|
|
file, err := os.Open(filename)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to open file: %v", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
var secretKey string
|
|
|
|
// Read the file line by line
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
// Find the line that contains the secret key prefix
|
|
if strings.HasPrefix(line, "AGE-SECRET-KEY-") {
|
|
secretKey = line
|
|
break
|
|
}
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
return "", fmt.Errorf("failed to scan file: %v", err)
|
|
}
|
|
|
|
if secretKey == "" {
|
|
return "", fmt.Errorf("secret key not found")
|
|
}
|
|
|
|
return secretKey, nil
|
|
}
|
|
|
|
func GenSopsSecret() error {
|
|
secretPath := filepath.Join(helper.ClusterPath, "kubernetes", "flux-system", "flux", "sopssecret.secret.yaml")
|
|
ageSecKey, err := GetSecKey()
|
|
// Generate Kubernetes secret YAML content
|
|
secret := map[string]interface{}{
|
|
"apiVersion": "v1",
|
|
"kind": "Secret",
|
|
"metadata": map[string]interface{}{
|
|
"name": "sops-age",
|
|
"namespace": "flux-system",
|
|
},
|
|
"stringData": map[string]interface{}{
|
|
"age.agekey": ageSecKey,
|
|
},
|
|
"type": string(corev1.SecretTypeOpaque),
|
|
}
|
|
|
|
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("SOPS secret YAML saved to: %s\n", secretPath)
|
|
return nil
|
|
}
|