fix: refactor publish command #14

Merged
marcel.henselin merged 1 commit from fix/postgres_instance_mapper into alpha 2026-01-30 10:15:50 +00:00
8 changed files with 650 additions and 425 deletions

View file

@ -0,0 +1,149 @@
package publish
import (
"encoding/json"
"fmt"
"log"
"os"
"path"
"strings"
)
type Architecture struct {
Protocols []string `json:"protocols"`
OS string `json:"os"`
Arch string `json:"arch"`
FileName string `json:"filename"`
DownloadUrl string `json:"download_url"`
ShaSumsUrl string `json:"shasums_url"`
ShaSumsSignatureUrl string `json:"shasums_signature_url"`
ShaSum string `json:"shasum"`
SigningKeys []SigningKey `json:"signing_keys"`
}
type SigningKey struct {
KeyId string `json:"key_id"`
AsciiArmor string `json:"ascii_armor"`
TrustSignature string `json:"trust_signature"`
Source string `json:"source"`
SourceUrl string `json:"source_url"`
}
func (p *Provider) CreateArchitectureFiles() error {
// var namespace, provider, distPath, repoName, version, gpgFingerprint, gpgPubKeyFile, domain string
log.Println("* Creating architecture files in target directories")
// filename = terraform-provider-[provider]_0.0.1_darwin_amd64.zip - provider_name + version + target + architecture + .zip
// prefix := fmt.Sprintf("v1/providers/%s/%s/%s/", namespace, provider, version)
prefix := path.Join("v1", "providers", p.Namespace, p.Provider, p.Version)
// pathPrefix := fmt.Sprintf("release/%s", prefix)
pathPrefix := path.Join("release", prefix)
// urlPrefix := fmt.Sprintf("https://%s/%s", domain, prefix)
urlPrefix := path.Join("https://", p.Domain, prefix)
// download url = https://example.com/v1/providers/namespace/provider/0.0.1/download/terraform-provider_0.0.1_darwin_amd64.zip
downloadUrlPrefix := path.Join(urlPrefix, "download")
downloadPathPrefix := path.Join(pathPrefix, "download")
// shasums url = https://example.com/v1/providers/namespace/provider/0.0.1/terraform-provider_0.0.1_SHA256SUMS
shasumsUrl := path.Join(urlPrefix, fmt.Sprintf("%s_%s_SHA256SUMS", p.RepoName, p.Version))
// shasums_signature_url = https://example.com/v1/providers/namespace/provider/0.0.1/terraform-provider_0.0.1_SHA256SUMS.sig
shasumsSigUrl := shasumsUrl + ".sig"
gpgAsciiPub, err := p.ReadGpgFile()
if err != nil {
return err
}
shaSums, err := p.GetShaSums()
if err != nil {
return err
}
for _, sum := range shaSums {
downloadUrl := path.Join(downloadUrlPrefix, sum.Path)
// get os and arch from filename
removeFileExtension := strings.Split(sum.Path, ".zip")
fileNameSplit := strings.Split(removeFileExtension[0], "_")
// Get build target and architecture from the zip file name
target := fileNameSplit[2]
arch := fileNameSplit[3]
// build filepath
archFileName := path.Join(downloadPathPrefix, target, arch)
a := Architecture{
Protocols: []string{"5.1"},
OS: target,
Arch: arch,
FileName: sum.Path,
DownloadUrl: downloadUrl,
ShaSumsUrl: shasumsUrl,
ShaSumsSignatureUrl: shasumsSigUrl,
ShaSum: sum.Sum,
SigningKeys: []SigningKey{
{
KeyId: p.GpgFingerprint,
AsciiArmor: gpgAsciiPub,
TrustSignature: "",
Source: "",
SourceUrl: "",
},
},
}
// var architectureTemplate = []byte(fmt.Sprintf(`
//{
// "protocols": [
// "4.0",
// "5.1",
// "6.0"
// ],
// "os": "%s",
// "arch": "%s",
// "filename": "%s",
// "download_url": "%s",
// "shasums_url": "%s",
// "shasums_signature_url": "%s",
// "shasum": "%s",
// "signing_keys": {
// "gpg_public_keys": [
// {
// "key_id": "%s",
// "ascii_armor": "%s",
// "trust_signature": "",
// "source": "",
// "source_url": ""
// }
// ]
// }
//}
//`, target, arch, fileName, downloadUrl, shasumsUrl, shasumsSigUrl, shasum, gpgFingerprint, gpgAsciiPub))
log.Printf(" - Arch file: %s", archFileName)
err := WriteArchitectureFile(archFileName, a)
if err != nil {
return err
}
}
return nil
}
func WriteArchitectureFile(filePath string, arch Architecture) error {
jsonString, err := json.Marshal(arch)
if err != nil {
return fmt.Errorf("error encoding data: %w", err)
}
err = os.WriteFile(filePath, jsonString, os.ModePerm)
if err != nil {
return fmt.Errorf("error writing data: %w", err)
}
return nil
}

20
cmd/cmd/publish/gpg.go Normal file
View file

@ -0,0 +1,20 @@
package publish
import (
"fmt"
)
func (p *Provider) ReadGpgFile() (string, error) {
// Get contents of GPG key
gpgFile, err := ReadFile(p.GpgPubKeyFile)
if err != nil {
return "", fmt.Errorf("error reading '%s' file: %w", p.GpgPubKeyFile, err)
}
// loop through every line and stick with \\n
gpgAsciiPub := ""
for _, line := range gpgFile {
gpgAsciiPub = gpgAsciiPub + line + "\\n"
}
return gpgAsciiPub, nil
}

233
cmd/cmd/publish/provider.go Normal file
View file

@ -0,0 +1,233 @@
package publish
import (
"bufio"
"errors"
"fmt"
"io"
"io/fs"
"log"
"os"
"path"
"strings"
)
type Provider struct {
Namespace string
Provider string
DistPath string
RepoName string
Version string
GpgFingerprint string
GpgPubKeyFile string
Domain string
}
func (p *Provider) CreateV1Dir() error {
// Path to semantic version dir
versionPath := p.providerDirs()
// Files to create under v1/providers/[namespace]/[provider_name]
err := p.createVersionsFile()
if err != nil {
return fmt.Errorf("[CreateV1Dir] - create versions file:%w", err)
} // Creates version file one above download, which is why downloadPath isn't used
// Files/Directories to create under v1/providers/[namespace]/[provider_name]/[version]
err = p.copyShaFiles(versionPath)
if err != nil {
return fmt.Errorf("[CreateV1Dir] - copy sha files: %w", err)
}
log.Printf("* Creating download/ in %s directory", versionPath)
downloadsPath := path.Join(versionPath, "download")
err = CreateDir(downloadsPath)
if err != nil {
return err
}
// Create darwin, freebsd, linux, windows dirs
for _, v := range [4]string{"darwin", "freebsd", "linux", "windows"} {
err = CreateDir(path.Join(downloadsPath, v))
if err != nil {
return fmt.Errorf("error creating dir '%s': %w", path.Join(downloadsPath, v), err)
}
}
// Copy all zips
err = p.copyBuildZips(downloadsPath)
if err != nil {
return err
}
// Create all individual files for build targets and each architecture for the build targets
err = p.CreateArchitectureFiles()
if err != nil {
return err
}
return nil
}
func (p *Provider) copyBuildZips(destPath string) error {
log.Println("* Copying build zips")
shaSums, err := p.GetShaSums()
if err != nil {
return err
}
// Loop through and copy each
for _, sum := range shaSums {
zipSrcPath := path.Join(p.DistPath, sum.Path)
zipDestPath := path.Join(destPath, sum.Path)
log.Printf(" - Zip Source: %s", zipSrcPath)
log.Printf(" - Zip Dest: %s", zipDestPath)
// Copy the zip
_, err = CopyFile(zipSrcPath, zipDestPath)
if err != nil {
return fmt.Errorf("error copying file '%s': %w", zipSrcPath, err)
}
}
return nil
}
func (p *Provider) copyShaFiles(destPath string) error {
log.Printf("* Copying SHA files in %s directory", p.DistPath)
// Copy files from srcPath
shaSum := p.RepoName + "_" + p.Version + "_SHA256SUMS"
shaSumPath := path.Join(p.DistPath, shaSum)
// _SHA256SUMS file
_, err := CopyFile(shaSumPath, path.Join(destPath, shaSum))
if err != nil {
return err
}
// _SHA256SUMS.sig file
_, err = CopyFile(shaSumPath+".sig", path.Join(destPath, shaSum+".sig"))
if err != nil {
return err
}
return nil
}
func (p *Provider) createVersionsFile() error {
log.Println("* Writing to release/v1/providers/[namespace]/[repo]/versions file")
versionPath := path.Join("release", "v1", "providers", p.Namespace, p.Provider, "versions")
shasums, err := p.GetShaSums()
if err != nil {
return fmt.Errorf("error getting sha sums: %w", err)
}
// Build the versions file...
version := Version{}
for _, sum := range shasums {
// get os and arch from filename
removeFileExtension := strings.Split(sum.Path, ".zip")
fileNameSplit := strings.Split(removeFileExtension[0], "_")
// Get build target and architecture from the zip file name
target := fileNameSplit[2]
arch := fileNameSplit[3]
version.Platforms = append(version.Platforms, Platform{
OS: target,
Arch: arch,
})
}
data := Data{}
downloadPath := path.Join(p.Domain, "v1", "providers", p.Namespace, p.Provider, "versions")
err = data.LoadFromUrl(fmt.Sprintf("https://%s", downloadPath))
if err != nil {
return fmt.Errorf("error getting existing versions file: %w", err)
}
err = data.AddVersion(version)
if err != nil {
return fmt.Errorf("error appending version: %w", err)
}
err = data.WriteToFile(versionPath)
if err != nil {
return fmt.Errorf("error saving file '%s':%w", versionPath, err)
}
return nil
}
func (p *Provider) providerDirs() string {
log.Println("* Creating release/v1/providers/[namespace]/[repo]/[version] directories")
target := path.Join("release", "v1", "providers", p.Namespace, p.RepoName, p.Version)
err := CreateDir(target)
if err != nil {
return ""
}
return target
}
func CreateDir(path string) error {
log.Printf("* Creating %s directory", path)
err := os.MkdirAll(path, os.ModePerm)
if errors.Is(err, fs.ErrExist) {
return nil
}
return err
}
func ReadFile(filePath string) ([]string, error) {
rFile, err := os.Open(filePath)
if err != nil {
return nil, err
}
fileScanner := bufio.NewScanner(rFile)
fileScanner.Split(bufio.ScanLines)
var fileLines []string
for fileScanner.Scan() {
fileLines = append(fileLines, fileScanner.Text())
}
err = rFile.Close()
if err != nil {
return nil, err
}
return fileLines, nil
}
func CopyFile(src, dst string) (int64, error) {
sourceFileStat, err := os.Stat(src)
if err != nil {
return 0, err
}
if !sourceFileStat.Mode().IsRegular() {
return 0, fmt.Errorf("%s is not a regular file", src)
}
source, err := os.Open(src)
if err != nil {
return 0, err
}
defer source.Close()
destination, err := os.Create(dst)
if err != nil {
return 0, err
}
defer destination.Close()
nBytes, err := io.Copy(destination, source)
return nBytes, err
}

View file

@ -0,0 +1,39 @@
package publish
import (
"log/slog"
"path"
"regexp"
)
func (p *Provider) GetShaSums() (ShaSums, error) {
return GetShaSumContents(p.DistPath, p.RepoName, p.Version)
}
type ShaSums []ShaSum
type ShaSum struct {
Sum string
Path string
}
func GetShaSumContents(distPath, repoName, version string) (ShaSums, error) {
shaSumFileName := repoName + "_" + version + "_SHA256SUMS"
shaSumPath := path.Join(distPath, shaSumFileName)
shaSumLine, err := ReadFile(shaSumPath)
if err != nil {
return nil, err
}
regEx := regexp.MustCompile(`([0-9a-fA-F]+)\s+(.+)`)
shaSums := ShaSums{}
for _, line := range shaSumLine {
matches := regEx.FindAllStringSubmatch(line, -1)
if len(matches) < 1 {
slog.Warn("unable to parse SHA sum line", "line", line)
continue
}
shaSums = append(shaSums, ShaSum{Sum: matches[0][0], Path: matches[0][1]})
}
return shaSums, nil
}

143
cmd/cmd/publish/versions.go Normal file
View file

@ -0,0 +1,143 @@
package publish
import (
"encoding/json"
"fmt"
"io"
"log/slog"
"net/http"
"net/url"
"os"
)
type Version struct {
Version string `json:"version"`
Protocols []string `json:"protocols"`
Platforms []Platform `json:"platforms"`
}
type Platform struct {
OS string `json:"os" yaml:"os"`
Arch string `json:"arch" yaml:"arch"`
}
type Data struct {
Versions []Version `json:"versions"`
}
func (d *Data) WriteToFile(filePath string) error {
//file, err := os.OpenFile(filePath, os.O_CREATE, os.ModePerm)
//if err != nil {
// return fmt.Errorf("error creating file: %w", err)
//}
//defer file.Close()
//
jsonString, err := json.Marshal(d)
if err != nil {
return fmt.Errorf("error encoding data: %w", err)
}
err = os.WriteFile(filePath, jsonString, os.ModePerm)
if err != nil {
return fmt.Errorf("error writing data: %w", err)
}
return nil
}
func (d *Data) AddVersion(v Version) error {
d.Versions = append(d.Versions, v)
return nil
}
func (d *Data) Validate() error {
for _, v := range d.Versions {
err := v.Validate()
if err != nil {
return err
}
}
return nil
}
func (d *Data) LoadFromFile(filePath string) error {
plan, err := os.ReadFile(filePath)
if err != nil {
return err
}
err = json.Unmarshal(plan, &d)
if err != nil {
return err
}
return nil
}
func (d *Data) LoadFromUrl(uri string) error {
u, err := url.ParseRequestURI(uri)
if err != nil {
return err
}
file, err := os.CreateTemp("", "versions.*.json")
if err != nil {
return err
}
defer os.Remove(file.Name()) // Clean up
err = DownloadFile(
u.String(),
file.Name(),
)
if err != nil {
return err
}
return d.LoadFromFile(file.Name())
}
func (v *Version) Validate() error {
slog.Warn("validation needs to be implemented")
return nil
}
func (v *Version) AddPlatform(p Platform) error {
if p.OS == "" || p.Arch == "" {
return fmt.Errorf("OS and Arch MUST NOT be empty")
}
v.Platforms = append(v.Platforms, p)
return nil
}
func (v *Version) AddProtocol(p string) error {
if p == "" {
return fmt.Errorf("protocol MUST NOT be empty")
}
v.Protocols = append(v.Protocols, p)
return nil
}
// DownloadFile will download a url and store it in local filepath.
// It writes to the destination file as it downloads it, without
// loading the entire file into memory.
func DownloadFile(url string, filepath string) error {
// Create the file
out, err := os.Create(filepath)
if err != nil {
return err
}
defer out.Close()
// Get the data
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
// Write the body to file
_, err = io.Copy(out, resp.Body)
if err != nil {
return err
}
return nil
}

View file

@ -1,17 +1,14 @@
package cmd
import (
"bufio"
"errors"
"fmt"
"io"
"io/fs"
"log"
"os"
"path"
"path/filepath"
"strings"
publish2 "github.com/mhenselin/terraform-provider-stackitprivatepreview/cmd/cmd/publish"
"github.com/spf13/cobra"
)
@ -26,7 +23,7 @@ var (
gpgPubKeyFile string
)
var rootCmd = &cobra.Command{
var publishCmd = &cobra.Command{
Use: "publish",
Short: "Publish terraform provider",
Long: `...`,
@ -36,66 +33,75 @@ var rootCmd = &cobra.Command{
}
func init() { // nolint: gochecknoinits
rootCmd.Flags().StringVarP(&namespace, "namespace", "n", "", "Namespace for the Terraform registry.")
rootCmd.Flags().StringVarP(&domain, "domain", "d", "", "Domain for the Terraform registry.")
rootCmd.Flags().StringVarP(&providerName, "providerName", "p", "", "ProviderName for the Terraform registry.")
rootCmd.Flags().StringVarP(&distPath, "distPath", "x", "dist", "Dist Path for the Terraform registry.")
rootCmd.Flags().StringVarP(&repoName, "repoName", "r", "", "RepoName for the Terraform registry.")
rootCmd.Flags().StringVarP(&version, "version", "v", "", "Version for the Terraform registry.")
rootCmd.Flags().StringVarP(&gpgFingerprint, "gpgFingerprint", "f", "", "GPG Fingerprint for the Terraform registry.")
rootCmd.Flags().StringVarP(&gpgPubKeyFile, "gpgPubKeyFile", "k", "", "GPG PubKey file name for the Terraform registry.")
publishCmd.Flags().StringVarP(&namespace, "namespace", "n", "", "Namespace for the Terraform registry.")
publishCmd.Flags().StringVarP(&domain, "domain", "d", "", "Domain for the Terraform registry.")
publishCmd.Flags().StringVarP(&providerName, "providerName", "p", "", "ProviderName for the Terraform registry.")
publishCmd.Flags().StringVarP(&distPath, "distPath", "x", "dist", "Dist Path for the Terraform registry.")
publishCmd.Flags().StringVarP(&repoName, "repoName", "r", "", "RepoName for the Terraform registry.")
publishCmd.Flags().StringVarP(&version, "version", "v", "", "Version for the Terraform registry.")
publishCmd.Flags().StringVarP(&gpgFingerprint, "gpgFingerprint", "f", "", "GPG Fingerprint for the Terraform registry.")
publishCmd.Flags().StringVarP(&gpgPubKeyFile, "gpgPubKeyFile", "k", "", "GPG PubKey file name for the Terraform registry.")
err := rootCmd.MarkFlagRequired("namespace")
err := publishCmd.MarkFlagRequired("namespace")
if err != nil {
return
}
err = rootCmd.MarkFlagRequired("domain")
err = publishCmd.MarkFlagRequired("domain")
if err != nil {
return
}
err = rootCmd.MarkFlagRequired("providerName")
err = publishCmd.MarkFlagRequired("providerName")
if err != nil {
return
}
err = rootCmd.MarkFlagRequired("gpgFingerprint")
err = publishCmd.MarkFlagRequired("gpgFingerprint")
if err != nil {
return
}
err = rootCmd.MarkFlagRequired("gpgPubKeyFile")
err = publishCmd.MarkFlagRequired("gpgPubKeyFile")
if err != nil {
return
}
err = rootCmd.MarkFlagRequired("repoName")
err = publishCmd.MarkFlagRequired("repoName")
if err != nil {
return
}
err = rootCmd.MarkFlagRequired("version")
err = publishCmd.MarkFlagRequired("version")
if err != nil {
return
}
err = rootCmd.MarkFlagRequired("gpgFingerprint")
err = publishCmd.MarkFlagRequired("gpgFingerprint")
if err != nil {
return
}
err = rootCmd.MarkFlagRequired("gpgPubKeyFile")
err = publishCmd.MarkFlagRequired("gpgPubKeyFile")
if err != nil {
return
}
}
func NewPublishCmd() *cobra.Command {
return rootCmd
return publishCmd
}
func publish() error {
log.Println("📦 Packaging Terraform Provider for private registry...")
distPath = filepath.Clean(distPath) + "/"
p := publish2.Provider{
Namespace: namespace,
Provider: providerName,
DistPath: filepath.Clean(distPath) + "/",
RepoName: repoName,
Version: version,
GpgFingerprint: gpgFingerprint,
GpgPubKeyFile: gpgPubKeyFile,
Domain: domain,
}
// Create release dir - only the contents of this need to be uploaded to S3
err := createDir("release")
if err != nil {
return fmt.Errorf("error creating 'release' dir: %s", err)
log.Printf("* Creating reelase directory")
err := os.Mkdir("release", os.ModePerm)
if !errors.Is(err, fs.ErrExist) {
return fmt.Errorf("error creating 'release' dir: %w", err)
}
// Create .wellKnown directory and terraform.json file
@ -104,8 +110,7 @@ func publish() error {
return fmt.Errorf("error creating '.wellKnown' dir: %s", err)
}
// Create v1 directory
err = provider(namespace, providerName, distPath, repoName, version, gpgFingerprint, gpgPubKeyFile, domain)
err = p.CreateV1Dir()
if err != nil {
return fmt.Errorf("error creating 'v1' dir: %s", err)
}
@ -118,385 +123,20 @@ func publish() error {
func wellKnown() error {
log.Println("* Creating .well-known directory")
err := createDir("release/.well-known")
if err != nil {
return err
err := os.Mkdir("release/.well-known", os.ModePerm)
if !errors.Is(err, fs.ErrExist) {
return fmt.Errorf("error creating 'release' dir: %w", err)
}
terraformJson := []byte(`{"providers.v1": "/v1/providers/"}`)
log.Println(" - Writing to .well-known/terraform.json file")
err = writeFile("release/.well-known/terraform.json", terraformJson)
err = os.WriteFile(
"release/.well-known/terraform.json",
[]byte(`{"providers.v1": "/v1/providers/"}`),
0644,
)
if err != nil {
return err
}
return nil
}
// provider is the Terraform name
// repoName is the Repository name
func provider(namespace, provider, distPath, repoName, version, gpgFingerprint, gpgPubKeyFile, domain string) error {
// Path to semantic version dir
versionPath := providerDirs(namespace, provider, version)
// Files to create under v1/providers/[namespace]/[provider_name]
err := createVersionsFile(namespace, provider, distPath, repoName, version)
if err != nil {
return err
} // Creates version file one above download, which is why downloadPath isn't used
// Files/Directories to create under v1/providers/[namespace]/[provider_name]/[version]
copyShaFiles(versionPath, distPath, repoName, version)
downloadPath, err := createDownloadsDir(versionPath)
if err != nil {
return err
}
// Create darwin, freebsd, linux, windows dirs
err = createTargetDirs(*downloadPath)
if err != nil {
return err
}
// Copy all zips
err = copyBuildZips(*downloadPath, distPath, repoName, version)
if err != nil {
return err
}
// Create all individual files for build targets and each architecture for the build targets
err = createArchitectureFiles(namespace, provider, distPath, repoName, version, gpgFingerprint, gpgPubKeyFile, domain)
if err != nil {
return err
}
return nil
}
// Create the directories with a path format v1/providers/[namespace]/[provider_name]/[version]
func providerDirs(namespace, repoName, version string) string {
log.Println("* Creating release/v1/providers/[namespace]/[repo]/[version] directories")
providerPathArr := [6]string{"release", "v1", "providers", namespace, repoName, version}
var currentPath string
for _, v := range providerPathArr {
currentPath = currentPath + v + "/"
err := createDir(currentPath)
if err != nil {
return ""
}
}
return currentPath
}
// Create the versions file under v1/providers/[namespace]/[provider_name]
func createVersionsFile(namespace, provider, distPath, repoName, version string) error {
log.Println("* Writing to release/v1/providers/[namespace]/[repo]/versions file")
versionPath := fmt.Sprintf("release/v1/providers/%s/%s/versions", namespace, provider)
shaSumContents, err := getShaSumContents(distPath, repoName, version)
if err != nil {
return err
}
// Build the versions file...
platforms := ""
for _, line := range shaSumContents {
fileName := line[1] // zip file name
// get os and arch from filename
removeFileExtension := strings.Split(fileName, ".zip")
fileNameSplit := strings.Split(removeFileExtension[0], "_")
// Get build target and architecture from the zip file name
target := fileNameSplit[2]
arch := fileNameSplit[3]
platforms += "{"
platforms += fmt.Sprintf(`"os": "%s",`, target)
platforms += fmt.Sprintf(`"arch": "%s"`, arch)
platforms += "}"
platforms += ","
}
platforms = strings.TrimRight(platforms, ",") // remove trailing comma, json does not allow
var versions = []byte(fmt.Sprintf(`
{
"versions": [
{
"version": "%s",
"protocols": [
"4.0",
"5.1",
"6.0"
],
"platform": [
%s
]
}
]
}
`, version, platforms))
err = writeFile(versionPath, versions)
if err != nil {
return err
}
return nil
}
func copyShaFiles(destPath, srcPath, repoName, version string) {
log.Printf("* Copying SHA files in %s directory", srcPath)
// Copy files from srcPath
shaSum := repoName + "_" + version + "_SHA256SUMS"
shaSumPath := path.Join(srcPath, shaSum)
// _SHA256SUMS file
_, err := copyFile(shaSumPath, path.Join(destPath, shaSum))
if err != nil {
log.Println(err)
}
// _SHA256SUMS.sig file
_, err = copyFile(shaSumPath+".sig", path.Join(destPath, shaSum+".sig"))
if err != nil {
log.Println(err)
}
}
func createDownloadsDir(destPath string) (*string, error) {
log.Printf("* Creating download/ in %s directory", destPath)
downloadPath := path.Join(destPath, "download")
err := createDir(downloadPath)
if err != nil {
return nil, err
}
return &downloadPath, nil
}
func createTargetDirs(destPath string) error {
log.Printf("* Creating target dirs in %s directory", destPath)
targets := [4]string{"darwin", "freebsd", "linux", "windows"}
for _, v := range targets {
err := createDir(destPath + v)
if err != nil {
return err
}
err = createDir(path.Join(destPath, v))
if err != nil {
return err
}
}
return nil
}
func copyBuildZips(destPath, distPath, repoName, version string) error {
log.Println("* Copying build zips")
shaSumContents, err := getShaSumContents(distPath, repoName, version)
if err != nil {
return err
}
// Loop through and copy each
for _, v := range shaSumContents {
zipName := v[1]
zipSrcPath := path.Join(distPath, zipName)
zipDestPath := path.Join(destPath, zipName)
log.Printf(" - Zip Source: %s", zipSrcPath)
log.Printf(" - Zip Dest: %s", zipDestPath)
// Copy the zip
_, err := copyFile(zipSrcPath, zipDestPath)
if err != nil {
return err
}
}
return nil
}
func getShaSumContents(distPath, repoName, version string) ([][]string, error) {
shaSumFileName := repoName + "_" + version + "_SHA256SUMS"
shaSumPath := distPath + "/" + shaSumFileName
shaSumLine, err := readFile(shaSumPath)
if err != nil {
return nil, err
}
buildsAndShaSums := [][]string{}
for _, line := range shaSumLine {
lineSplit := strings.Split(line, " ")
row := []string{lineSplit[0], lineSplit[1]}
buildsAndShaSums = append(buildsAndShaSums, row)
}
// log.Println(buildsAndShaSums)
return buildsAndShaSums, nil
}
// Create architecture files for each build target
func createArchitectureFiles(namespace, provider, distPath, repoName, version, gpgFingerprint, gpgPubKeyFile, domain string) error {
log.Println("* Creating architecture files in target directories")
// filename = terraform-provider-[provider]_0.0.1_darwin_amd64.zip - provider_name + version + target + architecture + .zip
prefix := fmt.Sprintf("v1/providers/%s/%s/%s/", namespace, provider, version)
pathPrefix := fmt.Sprintf("release/%s", prefix)
urlPrefix := fmt.Sprintf("https://%s/%s", domain, prefix)
// download url = https://example.com/v1/providers/namespace/provider/0.0.1/download/terraform-provider_0.0.1_darwin_amd64.zip
downloadUrlPrefix := urlPrefix + "download/"
downloadPathPrefix := pathPrefix + "download/"
// shasums url = https://example.com/v1/providers/namespace/provider/0.0.1/terraform-provider_0.0.1_SHA256SUMS
shasumsUrl := urlPrefix + fmt.Sprintf("%s_%s_SHA256SUMS", repoName, version)
// shasums_signature_url = https://example.com/v1/providers/namespace/provider/0.0.1/terraform-provider_0.0.1_SHA256SUMS.sig
shasumsSigUrl := shasumsUrl + ".sig"
shaSumContents, err := getShaSumContents(distPath, repoName, version)
if err != nil {
return err
}
// Get contents of GPG key
gpgFile, err := readFile(gpgPubKeyFile)
if err != nil {
log.Printf("Error reading '%s' file: %s", gpgPubKeyFile, err)
}
// loop through every line and stick with \\n
gpgAsciiPub := ""
for _, line := range gpgFile {
gpgAsciiPub = gpgAsciiPub + line + "\\n"
}
// log.Println(gpgAsciiPub)
for _, line := range shaSumContents {
shasum := line[0] // shasum of the zip
fileName := line[1] // zip file name
downloadUrl := downloadUrlPrefix + fileName
// get os and arch from filename
removeFileExtension := strings.Split(fileName, ".zip")
fileNameSplit := strings.Split(removeFileExtension[0], "_")
// Get build target and architecture from the zip file name
target := fileNameSplit[2]
arch := fileNameSplit[3]
// build filepath
archFileName := downloadPathPrefix + target + "/" + arch
var architectureTemplate = []byte(fmt.Sprintf(`
{
"protocols": [
"4.0",
"5.1",
"6.0"
],
"os": "%s",
"arch": "%s",
"filename": "%s",
"download_url": "%s",
"shasums_url": "%s",
"shasums_signature_url": "%s",
"shasum": "%s",
"signing_keys": {
"gpg_public_keys": [
{
"key_id": "%s",
"ascii_armor": "%s",
"trust_signature": "",
"source": "",
"source_url": ""
}
]
}
}
`, target, arch, fileName, downloadUrl, shasumsUrl, shasumsSigUrl, shasum, gpgFingerprint, gpgAsciiPub))
log.Printf(" - Arch file: %s", archFileName)
err := writeFile(archFileName, architectureTemplate)
if err != nil {
return err
}
}
return nil
}
func createDir(path string) error {
log.Printf("* Creating %s directory", path)
err := os.Mkdir(path, os.ModePerm)
if errors.Is(err, fs.ErrExist) {
return nil
}
return err
}
func copyFile(src, dst string) (int64, error) {
sourceFileStat, err := os.Stat(src)
if err != nil {
return 0, err
}
if !sourceFileStat.Mode().IsRegular() {
return 0, fmt.Errorf("%s is not a regular file", src)
}
source, err := os.Open(src)
if err != nil {
return 0, err
}
defer source.Close()
destination, err := os.Create(dst)
if err != nil {
return 0, err
}
defer destination.Close()
nBytes, err := io.Copy(destination, source)
return nBytes, err
}
func readFile(filePath string) ([]string, error) {
rFile, err := os.Open(filePath)
if err != nil {
return nil, err
}
fileScanner := bufio.NewScanner(rFile)
fileScanner.Split(bufio.ScanLines)
var fileLines []string
for fileScanner.Scan() {
fileLines = append(fileLines, fileScanner.Text())
}
rFile.Close()
return fileLines, nil
}
func writeFile(fileName string, fileContents []byte) error {
err := os.WriteFile(fileName, fileContents, 0644)
return err
}

View file

@ -15,7 +15,7 @@ import (
)
func mapGetInstanceResponseToModel(ctx context.Context, m *postgresflexalpharesource.InstanceModel, resp *postgresflex.GetInstanceResponse) error {
tflog.Info(ctx, ">>>> MSH DEBUG <<<<", map[string]interface{}{
tflog.Debug(ctx, ">>>> MSH DEBUG <<<<", map[string]interface{}{
"id": m.Id.ValueString(),
"instance_id": m.InstanceId.ValueString(),
"backup_schedule": m.BackupSchedule.ValueString(),
@ -47,17 +47,17 @@ func mapGetInstanceResponseToModel(ctx context.Context, m *postgresflexalphareso
},
)
}
m.ConnectionInfo.Host = types.StringValue(resp.ConnectionInfo.GetHost())
m.ConnectionInfo.Port = types.Int64Value(resp.ConnectionInfo.GetPort())
m.FlavorId = types.StringValue(resp.GetFlavorId())
if m.Id.IsNull() || m.Id.IsUnknown() {
m.Id = utils.BuildInternalTerraformId(m.ProjectId.ValueString(), m.Region.ValueString(), m.InstanceId.ValueString())
}
m.InstanceId = types.StringPointerValue(resp.Id)
//m.IsDeletable = types.BoolUnknown()
//if isDel, ok := resp.GetIsDeletableOk(); ok {
// m.IsDeletable = types.BoolValue(isDel)
m.IsDeletable = types.BoolValue(resp.GetIsDeletable())
//}
netAcl, diags := types.ListValueFrom(ctx, types.StringType, resp.Network.GetAcl())
if diags.HasError() {
@ -93,10 +93,6 @@ func mapGetInstanceResponseToModel(ctx context.Context, m *postgresflexalphareso
m.Name = types.StringValue(resp.GetName())
//m.Status = types.StringUnknown()
//if status, ok := resp.GetStatusOk(); ok {
// m.Status = types.StringValue(string(status))
//}
m.Status = types.StringValue(string(resp.GetStatus()))
storage, diags := postgresflexalpharesource.NewStorageValue(
@ -117,13 +113,14 @@ func mapGetInstanceResponseToModel(ctx context.Context, m *postgresflexalphareso
func mapGetDataInstanceResponseToModel(ctx context.Context, m *postgresflexalphadatasource.InstanceModel, resp *postgresflex.GetInstanceResponse) error {
m.BackupSchedule = types.StringValue(resp.GetBackupSchedule())
//nolint:gocritic
// m.Encryption = postgresflexalpharesource.EncryptionValue{
// KekKeyId: types.StringValue(resp.Encryption.GetKekKeyId()),
// KekKeyRingId: types.StringValue(resp.Encryption.GetKekKeyRingId()),
// KekKeyVersion: types.StringValue(resp.Encryption.GetKekKeyVersion()),
// ServiceAccount: types.StringValue(resp.Encryption.GetServiceAccount()),
// }
m.Encryption = postgresflexalphadatasource.EncryptionValue{
KekKeyId: types.StringValue(resp.Encryption.GetKekKeyId()),
KekKeyRingId: types.StringValue(resp.Encryption.GetKekKeyRingId()),
KekKeyVersion: types.StringValue(resp.Encryption.GetKekKeyVersion()),
ServiceAccount: types.StringValue(resp.Encryption.GetServiceAccount()),
}
m.ConnectionInfo.Host = types.StringValue(resp.ConnectionInfo.GetHost())
m.ConnectionInfo.Port = types.Int64Value(resp.ConnectionInfo.GetPort())
m.FlavorId = types.StringValue(resp.GetFlavorId())
m.Id = utils.BuildInternalTerraformId(m.ProjectId.ValueString(), m.Region.ValueString(), m.InstanceId.ValueString())
m.InstanceId = types.StringPointerValue(resp.Id)

View file

@ -247,16 +247,20 @@ func (r *instanceResource) Create(
}
func modelToCreateInstancePayload(netAcl []string, model postgresflexalpha.InstanceModel, replVal int32) postgresflex.CreateInstanceRequestPayload {
payload := postgresflex.CreateInstanceRequestPayload{
BackupSchedule: model.BackupSchedule.ValueStringPointer(),
Encryption: &postgresflex.InstanceEncryption{
var enc postgresflex.InstanceEncryption
if !model.Encryption.IsNull() && !model.Encryption.IsUnknown() {
enc = postgresflex.InstanceEncryption{
KekKeyId: model.Encryption.KekKeyId.ValueStringPointer(),
KekKeyRingId: model.Encryption.KekKeyRingId.ValueStringPointer(),
KekKeyVersion: model.Encryption.KekKeyVersion.ValueStringPointer(),
ServiceAccount: model.Encryption.ServiceAccount.ValueStringPointer(),
},
FlavorId: model.FlavorId.ValueStringPointer(),
Name: model.Name.ValueStringPointer(),
}
}
payload := postgresflex.CreateInstanceRequestPayload{
BackupSchedule: model.BackupSchedule.ValueStringPointer(),
Encryption: &enc,
FlavorId: model.FlavorId.ValueStringPointer(),
Name: model.Name.ValueStringPointer(),
Network: &postgresflex.InstanceNetwork{
AccessScope: postgresflex.InstanceNetworkGetAccessScopeAttributeType(
model.Network.AccessScope.ValueStringPointer(),