feat: initial copy of v0.1.0
This commit is contained in:
parent
4cc801a7f3
commit
7d4cbb6b08
538 changed files with 63361 additions and 55213 deletions
137
generator/cmd/publish/architecture.go
Normal file
137
generator/cmd/publish/architecture.go
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
package publish
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"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 {
|
||||
GpgPublicKeys []GpgPublicKey `json:"gpg_public_keys"`
|
||||
}
|
||||
|
||||
type GpgPublicKey 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 {
|
||||
log.Println("* Creating architecture files in target directories")
|
||||
|
||||
prefix := path.Join("v1", "providers", p.Namespace, p.Provider, p.Version)
|
||||
|
||||
pathPrefix := path.Join("release", prefix)
|
||||
|
||||
urlPrefix, err := url.JoinPath("https://", p.Domain, prefix)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating base url: %w", err)
|
||||
}
|
||||
|
||||
downloadUrlPrefix, err := url.JoinPath(urlPrefix, "download")
|
||||
if err != nil {
|
||||
return fmt.Errorf("error crearting download url: %w", err)
|
||||
}
|
||||
downloadPathPrefix := path.Join(pathPrefix, "download")
|
||||
|
||||
shasumsUrl, err := url.JoinPath(urlPrefix, fmt.Sprintf("%s_%s_SHA256SUMS", p.RepoName, p.Version))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating shasums url: %w", err)
|
||||
}
|
||||
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, err := url.JoinPath(downloadUrlPrefix, sum.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating url: %w", err)
|
||||
}
|
||||
|
||||
// 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", "6.0"},
|
||||
OS: target,
|
||||
Arch: arch,
|
||||
FileName: sum.Path,
|
||||
DownloadUrl: downloadUrl,
|
||||
ShaSumsUrl: shasumsUrl,
|
||||
ShaSumsSignatureUrl: shasumsSigUrl,
|
||||
ShaSum: sum.Sum,
|
||||
SigningKeys: SigningKey{},
|
||||
}
|
||||
|
||||
a.SigningKeys = SigningKey{
|
||||
GpgPublicKeys: []GpgPublicKey{
|
||||
{
|
||||
KeyId: p.GpgFingerprint,
|
||||
AsciiArmor: gpgAsciiPub,
|
||||
TrustSignature: "",
|
||||
Source: "",
|
||||
SourceUrl: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
//nolint:gosec // this file is not sensitive, so we can use os.ModePerm
|
||||
err = os.WriteFile(
|
||||
filePath,
|
||||
jsonString,
|
||||
os.ModePerm,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing data: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
14
generator/cmd/publish/gpg.go
Normal file
14
generator/cmd/publish/gpg.go
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package publish
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (p *Provider) ReadGpgFile() (string, error) {
|
||||
gpgFile, err := ReadFile(p.GpgPubKeyFile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error reading '%s' file: %w", p.GpgPubKeyFile, err)
|
||||
}
|
||||
return strings.Join(gpgFile, "\n"), nil
|
||||
}
|
||||
297
generator/cmd/publish/provider.go
Normal file
297
generator/cmd/publish/provider.go
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
package publish
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Provider struct {
|
||||
RootPath string
|
||||
Namespace string
|
||||
Provider string
|
||||
DistPath string
|
||||
RepoName string
|
||||
Version string
|
||||
GpgFingerprint string
|
||||
GpgPubKeyFile string
|
||||
Domain string
|
||||
}
|
||||
|
||||
func (p *Provider) GetRoot() error {
|
||||
cmd := exec.Command("git", "rev-parse", "--show-toplevel")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lines := strings.Split(string(out), "\n")
|
||||
p.RootPath = lines[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
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{
|
||||
Version: p.Version,
|
||||
Protocols: []string{"5.1", "6.1"},
|
||||
Platforms: nil,
|
||||
}
|
||||
for _, sum := range shasums {
|
||||
// get os and arch from filename
|
||||
removeFileExtension := strings.Split(sum.Path, ".zip")
|
||||
if len(removeFileExtension) < 1 {
|
||||
log.Fatalf("error: %s does not have .zip extension", sum.Path)
|
||||
}
|
||||
fileNameSplit := strings.Split(removeFileExtension[0], "_")
|
||||
if len(fileNameSplit) < 4 {
|
||||
log.Fatalf("filename does not match our regex: %s", 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 {
|
||||
slog.Warn("error getting existing versions file, start with empty")
|
||||
// TODO: create flag for first use or make it more robust
|
||||
// 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]/[provider]/[version] directories")
|
||||
|
||||
target := path.Join("release", "v1", "providers", p.Namespace, p.Provider, p.Version)
|
||||
|
||||
err := CreateDir(target)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return target
|
||||
}
|
||||
|
||||
func (p *Provider) CreateWellKnown() error {
|
||||
log.Println("* Creating .well-known directory")
|
||||
pathString := path.Join(p.RootPath, "release", ".well-known")
|
||||
|
||||
//nolint:gosec // this file is not sensitive, so we can use ModePerm
|
||||
err := os.MkdirAll(pathString, os.ModePerm)
|
||||
if err != nil && !errors.Is(err, fs.ErrExist) {
|
||||
return fmt.Errorf("error creating '%s' dir: %w", pathString, err)
|
||||
}
|
||||
|
||||
log.Println(" - Writing to .well-known/terraform.json file")
|
||||
|
||||
//nolint:gosec // this file is not sensitive, so we can use 0644
|
||||
err = os.WriteFile(
|
||||
fmt.Sprintf("%s/terraform.json", pathString),
|
||||
[]byte(`{"providers.v1": "/v1/providers/"}`),
|
||||
0o644,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CreateDir(pathValue string) error {
|
||||
log.Printf("* Creating %s directory", pathValue)
|
||||
//nolint:gosec // this file is not sensitive, so we can use ModePerm
|
||||
err := os.MkdirAll(pathValue, 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 func(source *os.File) {
|
||||
err := source.Close()
|
||||
if err != nil {
|
||||
slog.Error("error closing source file", slog.Any("err", err))
|
||||
}
|
||||
}(source)
|
||||
|
||||
destination, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer func(destination *os.File) {
|
||||
err := destination.Close()
|
||||
if err != nil {
|
||||
slog.Error("error closing destination file", slog.Any("err", err))
|
||||
}
|
||||
}(destination)
|
||||
nBytes, err := io.Copy(destination, source)
|
||||
return nBytes, err
|
||||
}
|
||||
39
generator/cmd/publish/shasums.go
Normal file
39
generator/cmd/publish/shasums.go
Normal 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][1], Path: matches[0][2]})
|
||||
}
|
||||
return shaSums, nil
|
||||
}
|
||||
38
generator/cmd/publish/templates/Caddyfile
Normal file
38
generator/cmd/publish/templates/Caddyfile
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
log {
|
||||
level debug
|
||||
}
|
||||
|
||||
|
||||
filesystem tf s3 {
|
||||
bucket "terraform-provider-privatepreview"
|
||||
region eu01
|
||||
endpoint https://object.storage.eu01.onstackit.cloud
|
||||
use_path_style
|
||||
}
|
||||
}
|
||||
|
||||
tfregistry.sysops.stackit.rocks {
|
||||
encode zstd gzip
|
||||
|
||||
handle_path /docs/* {
|
||||
root /srv/www
|
||||
templates
|
||||
|
||||
@md {
|
||||
file {path}
|
||||
path *.md
|
||||
}
|
||||
|
||||
rewrite @md /markdown.html
|
||||
|
||||
file_server {
|
||||
browse
|
||||
}
|
||||
}
|
||||
|
||||
file_server {
|
||||
fs tf
|
||||
browse
|
||||
}
|
||||
}
|
||||
11
generator/cmd/publish/templates/index.html.gompl
Normal file
11
generator/cmd/publish/templates/index.html.gompl
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<title>Forwarding | Weiterleitung</title>
|
||||
<meta http-equiv="refresh" content="0; URL=index.md">
|
||||
</head>
|
||||
<body>
|
||||
<a href="index.md">Falls Sie nicht automatisch weitergeleitet werden, klicken Sie bitte hier.</a><br />
|
||||
Sie gelangen dann auf unsere Hauptseite
|
||||
</body>
|
||||
</html>
|
||||
34
generator/cmd/publish/templates/index.md.gompl
Normal file
34
generator/cmd/publish/templates/index.md.gompl
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
page_title: STACKIT provider PrivatePreview
|
||||
description: none
|
||||
---
|
||||
|
||||
# provider
|
||||
[Provider](docs/index.md)
|
||||
|
||||
## PostGreSQL alpha
|
||||
### data sources
|
||||
|
||||
- [Flavor](docs/data-sources/postgresflexalpha_flavor.md)
|
||||
- [Database](docs/data-sources/postgresflexalpha_database.md)
|
||||
- [Instance](docs/data-sources/postgresflexalpha_instance.md)
|
||||
- [Flavors](docs/data-sources/postgresflexalpha_flavors.md)
|
||||
- [User](docs/data-sources/postgresflexalpha_user.md)
|
||||
|
||||
### resources
|
||||
- [Database](docs/resources/postgresflexalpha_database.md)
|
||||
- [Instance](docs/resources/postgresflexalpha_instance.md)
|
||||
- [User](docs/resources/postgresflexalpha_user.md)
|
||||
|
||||
## SQL Server alpha
|
||||
### data sources
|
||||
- [Database](docs/data-sources/sqlserverflexalpha_database.md)
|
||||
- [Version](docs/data-sources/sqlserverflexalpha_version.md)
|
||||
- [User](docs/data-sources/sqlserverflexalpha_user.md)
|
||||
- [Flavor](docs/data-sources/sqlserverflexalpha_flavor.md)
|
||||
- [Instance](docs/data-sources/sqlserverflexalpha_instance.md)
|
||||
|
||||
### resources
|
||||
- [Database](docs/resources/sqlserverflexalpha_database.md)
|
||||
- [User](docs/resources/sqlserverflexalpha_user.md)
|
||||
- [Instance](docs/resources/sqlserverflexalpha_instance.md)
|
||||
79
generator/cmd/publish/templates/markdown.html.gompl
Normal file
79
generator/cmd/publish/templates/markdown.html.gompl
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
<!DOCTYPE html>
|
||||
{{ $mdFile := .OriginalReq.URL.Path | trimPrefix "/docs" }}
|
||||
{{ $md := (include $mdFile | splitFrontMatter) }}
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{$md.Meta.page_title}}</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="/docs/terraform-registry.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{$md.Meta.page_title}}</h1>
|
||||
<div class="provider-view">
|
||||
<div class="provider-nav">
|
||||
<nav class="bread-crumbs is-light" aria-label="Provider">
|
||||
<div class="container is-widescreen">
|
||||
<div class="level">
|
||||
<ul class="provider-nav-breadcrumbs bread-crumbs-list">
|
||||
<li class="bread-crumbs-item">
|
||||
<a id="ember20" class="ember-view bread-crumbs-link" href="/">
|
||||
Main
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<nav class="block-border section-navbar section-header" aria-label="Provider details">
|
||||
<div class="container">
|
||||
<div class="columns is-vcentered">
|
||||
<div class="column is-4">
|
||||
<div class="provider-nav-info-header">
|
||||
<div class="provider-overview-logo">
|
||||
<span class="provider-logo">
|
||||
<img class="github-image" src="https://avatars3.githubusercontent.com/stackitcloud" alt="stackitcloud">
|
||||
</span>
|
||||
</div>
|
||||
<div class="provider-nav-info-origin">
|
||||
<h1>PRIVATE PREVIEW</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-8">
|
||||
|
||||
<ul class="nav-tabs-list nav-tabs tabs">
|
||||
|
||||
<li class="nav-tabs-item">
|
||||
<a id="ember30" class="ember-view navbar-item" href="/">
|
||||
Overview
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<div class="provider-nav-provision-wrapper">
|
||||
<!----> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="section block-border block-white section-content">
|
||||
<div class="container">
|
||||
<div class="columns columns-provider-docs">
|
||||
<div class="column is-3 column-provider-docs-menu"></div>
|
||||
<article id="provider-docs-content" class="column is-6 provider-docs-content">
|
||||
<div class="markdown">
|
||||
<div class="highlighted-code-wrapper">
|
||||
{{markdown $md.Body}}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<div class="column is-3 column-provider-docs-menu"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
169
generator/cmd/publish/versions.go
Normal file
169
generator/cmd/publish/versions.go
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
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 {
|
||||
Id string `json:"id,omitempty"`
|
||||
Versions []Version `json:"versions"`
|
||||
}
|
||||
|
||||
func (d *Data) WriteToFile(filePath string) error {
|
||||
// TODO: make it variable
|
||||
d.Id = "tfregistry.sysops.stackit.rocks/mhenselin/stackitprivatepreview"
|
||||
|
||||
jsonString, err := json.Marshal(d)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error encoding data: %w", err)
|
||||
}
|
||||
|
||||
//nolint:gosec // this file is not sensitive, so we can use os.ModePerm
|
||||
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 {
|
||||
var newVersions []Version
|
||||
for _, ver := range d.Versions {
|
||||
if ver.Version != v.Version {
|
||||
newVersions = append(newVersions, ver)
|
||||
}
|
||||
}
|
||||
newVersions = append(newVersions, v)
|
||||
d.Versions = newVersions
|
||||
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 func(name string) {
|
||||
//nolint:gosec // The file path is generated by os.CreateTemp and is not user-controllable
|
||||
err := os.Remove(name)
|
||||
if err != nil {
|
||||
slog.Error("failed to remove temporary file", slog.Any("err", err))
|
||||
}
|
||||
}(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(urlValue, filepath string) error {
|
||||
// Create the file
|
||||
//nolint:gosec // path traversal is not a concern here, as the filepath is generated by us and not user input
|
||||
out, err := os.Create(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(out *os.File) {
|
||||
err := out.Close()
|
||||
if err != nil {
|
||||
slog.Error("failed to close file", slog.Any("err", err))
|
||||
}
|
||||
}(out)
|
||||
|
||||
// Get the data
|
||||
|
||||
//nolint:gosec,bodyclose // this is a controlled URL, not user input
|
||||
resp, err := http.Get(urlValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
// Write the body to file
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue