From 3a2daf9c56f0e5dc20fed9d8e2563c96e5595ea5 Mon Sep 17 00:00:00 2001 From: "Marcel S. Henselin" Date: Fri, 30 Jan 2026 11:15:10 +0100 Subject: [PATCH] fix: refactor publish command feat: add connection info fix: prevent postgresql from failing when encryption is empty --- cmd/cmd/publish/architecture.go | 149 ++++++ cmd/cmd/publish/gpg.go | 20 + cmd/cmd/publish/provider.go | 233 +++++++++ cmd/cmd/publish/shasums.go | 39 ++ cmd/cmd/publish/versions.go | 143 ++++++ cmd/cmd/publishCmd.go | 446 ++---------------- .../postgresflexalpha/instance/functions.go | 29 +- .../postgresflexalpha/instance/resource.go | 16 +- 8 files changed, 650 insertions(+), 425 deletions(-) create mode 100644 cmd/cmd/publish/architecture.go create mode 100644 cmd/cmd/publish/gpg.go create mode 100644 cmd/cmd/publish/provider.go create mode 100644 cmd/cmd/publish/shasums.go create mode 100644 cmd/cmd/publish/versions.go diff --git a/cmd/cmd/publish/architecture.go b/cmd/cmd/publish/architecture.go new file mode 100644 index 00000000..1b73283f --- /dev/null +++ b/cmd/cmd/publish/architecture.go @@ -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 +} diff --git a/cmd/cmd/publish/gpg.go b/cmd/cmd/publish/gpg.go new file mode 100644 index 00000000..91e244dc --- /dev/null +++ b/cmd/cmd/publish/gpg.go @@ -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 +} diff --git a/cmd/cmd/publish/provider.go b/cmd/cmd/publish/provider.go new file mode 100644 index 00000000..73f12ab0 --- /dev/null +++ b/cmd/cmd/publish/provider.go @@ -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 +} diff --git a/cmd/cmd/publish/shasums.go b/cmd/cmd/publish/shasums.go new file mode 100644 index 00000000..973b3769 --- /dev/null +++ b/cmd/cmd/publish/shasums.go @@ -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 +} diff --git a/cmd/cmd/publish/versions.go b/cmd/cmd/publish/versions.go new file mode 100644 index 00000000..08588b33 --- /dev/null +++ b/cmd/cmd/publish/versions.go @@ -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 +} diff --git a/cmd/cmd/publishCmd.go b/cmd/cmd/publishCmd.go index 6cb9dae9..0f595009 100644 --- a/cmd/cmd/publishCmd.go +++ b/cmd/cmd/publishCmd.go @@ -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 -} diff --git a/stackit/internal/services/postgresflexalpha/instance/functions.go b/stackit/internal/services/postgresflexalpha/instance/functions.go index 85372e43..8a5e4e4e 100644 --- a/stackit/internal/services/postgresflexalpha/instance/functions.go +++ b/stackit/internal/services/postgresflexalpha/instance/functions.go @@ -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) diff --git a/stackit/internal/services/postgresflexalpha/instance/resource.go b/stackit/internal/services/postgresflexalpha/instance/resource.go index f6ea152f..68c613aa 100644 --- a/stackit/internal/services/postgresflexalpha/instance/resource.go +++ b/stackit/internal/services/postgresflexalpha/instance/resource.go @@ -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(), -- 2.49.1