setup

package
v0.97.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 2, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Cmd = &cobra.Command{
	Use:   "setup [additional-terraform-args...]",
	Short: "Initialize Terraform with GitLab backend using config file",
	Run: func(c *cobra.Command, args []string) {

		configDir := filepath.Join(".sikalabs", "terraform")
		yamlPath := filepath.Join(configDir, "terraform.yaml")
		jsonPath := filepath.Join(configDir, "terraform.json")

		var configPath string
		var data []byte
		var err error
		var config TerraformConfig

		if _, err = os.Stat(yamlPath); err == nil {
			configPath = yamlPath
			data, err = os.ReadFile(configPath)
			error_utils.HandleError(err, "Failed to read config file")
			err = yaml.Unmarshal(data, &config)
			error_utils.HandleError(err, "Failed to parse YAML config file")
		} else if _, err = os.Stat(jsonPath); err == nil {
			configPath = jsonPath
			data, err = os.ReadFile(configPath)
			error_utils.HandleError(err, "Failed to read config file")
			err = json.Unmarshal(data, &config)
			error_utils.HandleError(err, "Failed to parse JSON config file")
		} else {
			error_utils.HandleError(fmt.Errorf("config file not found"), "Neither terraform.yaml nor terraform.json found in .sikalabs/terraform/")
		}

		username := FlagUsername
		token := FlagToken

		if username == "" && token == "" {

			parsedURL, err := url.Parse(config.GitlabURL)
			if err == nil && parsedURL.Host != "" {
				gitlabDomain := parsedURL.Host
				itemName := fmt.Sprintf("GITLAB_TOKEN_TF_STATE_%s", gitlabDomain)

				fmt.Printf("Checking 1Password for token: %s\n", itemName)

				opToken, err := exec_utils.ExecStr("op", "item", "get", itemName, "--vault", "employee", "--fields", "password", "--reveal")
				if err == nil && opToken != "" {

					token = strings.TrimSpace(opToken)
					username = "token"
					fmt.Println("Using token from 1Password")
				} else {
					fmt.Println("Token not found in 1Password, will prompt for credentials")
				}
			}
		}

		if !FlagSkipFiles && len(config.FilesInVault) > 0 {
			if config.VaultAddr == "" {
				error_utils.HandleError(fmt.Errorf("vault address is required"), "VaultAddr must be configured in terraform config when FilesInVault is used")
			}

			fmt.Println("Logging in to Vault...")
			if !FlagDryRun {
				err = exec_utils.ExecOut("vault", "login",
					"-address", config.VaultAddr,
					"-method=oidc")
				error_utils.HandleError(err, "Failed to login to Vault")
			}

			fmt.Println("Downloading files from Vault...")
			for localPath, vaultPath := range config.FilesInVault {
				fmt.Printf("  Downloading %s from %s\n", localPath, vaultPath)

				if !FlagDryRun {
					err = exec_utils.ExecOut(
						"slu", "vault", "copy-file-from-vault",
						"--vault-address", config.VaultAddr,
						"--secret-path", vaultPath,
						"--file-path", localPath,
					)
					error_utils.HandleError(err, fmt.Sprintf("Failed to download file %s from vault", localPath))
				}
			}
		}

		if !FlagSkipFiles && len(config.FilesWithCustomTooling) > 0 {
			fmt.Println("Executing custom tooling commands...")
			for fileName, tooling := range config.FilesWithCustomTooling {
				fmt.Printf("  Executing command for %s\n", fileName)
				if tooling.Get == "" {
					error_utils.HandleError(fmt.Errorf("get command is empty"), fmt.Sprintf("Get command is required for file %s", fileName))
				}

				if !FlagDryRun {
					err = exec_utils.ExecShOut(tooling.Get)
					error_utils.HandleError(err, fmt.Sprintf("Failed to execute custom tooling command for file %s", fileName))
				} else {
					fmt.Printf("    Command: %s\n", tooling.Get)
				}
			}
		}

		fi, err := os.Stdin.Stat()
		isPipe := err == nil && fi.Mode()&os.ModeNamedPipe != 0

		if isPipe {

			scanner := bufio.NewScanner(os.Stdin)

			if username == "" && scanner.Scan() {
				username = strings.TrimSpace(scanner.Text())
			}

			if token == "" && scanner.Scan() {
				token = strings.TrimSpace(scanner.Text())
			}

			if err := scanner.Err(); err != nil {
				error_utils.HandleError(err, "Failed to read from stdin")
			}
		} else {

			if username == "" {
				fmt.Print("GitLab Username: ")
				scanner := bufio.NewScanner(os.Stdin)
				if scanner.Scan() {
					username = strings.TrimSpace(scanner.Text())
				}
				if err := scanner.Err(); err != nil {
					error_utils.HandleError(err, "Failed to read username")
				}
			}

			if token == "" {
				fmt.Print("GitLab Token: ")
				scanner := bufio.NewScanner(os.Stdin)
				if scanner.Scan() {
					token = strings.TrimSpace(scanner.Text())
				}
				if err := scanner.Err(); err != nil {
					error_utils.HandleError(err, "Failed to read token")
				}
			}
		}

		if username == "" {
			error_utils.HandleError(fmt.Errorf("username is required"), "Username must be provided via --username flag or stdin")
		}

		if token == "" {
			error_utils.HandleError(fmt.Errorf("token is required"), "Token must be provided via --token flag or stdin")
		}

		backendAddress := fmt.Sprintf("address=%s/api/v4/projects/%s/terraform/state/%s",
			config.GitlabURL, config.ProjectID, config.StateName)
		lockAddress := fmt.Sprintf("lock_address=%s/api/v4/projects/%s/terraform/state/%s/lock",
			config.GitlabURL, config.ProjectID, config.StateName)
		unlockAddress := fmt.Sprintf("unlock_address=%s/api/v4/projects/%s/terraform/state/%s/lock",
			config.GitlabURL, config.ProjectID, config.StateName)
		usernameConfig := fmt.Sprintf("username=%s", username)
		password := fmt.Sprintf("password=%s", token)

		cmdArgs := []string{
			"init",
			fmt.Sprintf("-backend-config=%s", backendAddress),
			fmt.Sprintf("-backend-config=%s", lockAddress),
			fmt.Sprintf("-backend-config=%s", unlockAddress),
			fmt.Sprintf("-backend-config=%s", usernameConfig),
			fmt.Sprintf("-backend-config=%s", password),
			"-backend-config=lock_method=POST",
			"-backend-config=unlock_method=DELETE",
			"-backend-config=retry_wait_min=5",
		}

		cmdArgs = append(cmdArgs, args...)

		if FlagDryRun {
			fmt.Printf("terraform %s\n", strings.Join(cmdArgs, " "))
			os.Exit(0)
		}

		err = exec_utils.ExecInOut("terraform", cmdArgs...)
		error_utils.HandleError(err, "Failed to execute terraform init")
	},
}
View Source
var FlagDryRun bool
View Source
var FlagSkipFiles bool
View Source
var FlagToken string
View Source
var FlagUsername string

Functions

This section is empty.

Types

type CustomToolingConfig added in v0.94.0

type CustomToolingConfig struct {
	Get string `json:"Get" yaml:"Get"`
}

type TerraformConfig

type TerraformConfig struct {
	Meta struct {
		SchemaVersion string `json:"SchemaVersion" yaml:"SchemaVersion"`
	} `json:"Meta" yaml:"Meta"`
	GitlabURL              string                         `json:"GitlabURL" yaml:"GitlabURL"`
	ProjectID              string                         `json:"ProjectID" yaml:"ProjectID"`
	StateName              string                         `json:"StateName" yaml:"StateName"`
	VaultAddr              string                         `json:"VaultAddr,omitempty" yaml:"VaultAddr,omitempty"`
	FilesInVault           map[string]string              `json:"FilesInVault,omitempty" yaml:"FilesInVault,omitempty"`
	FilesWithCustomTooling map[string]CustomToolingConfig `json:"FilesWithCustomTooling,omitempty" yaml:"FilesWithCustomTooling,omitempty"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL