Remove code to publish to RPM and deb repos (#61786)
This was replaced here: https://github.com/grafana/grafana/pull/53553 and it's no longer used
(cherry picked from commit a190e03133)
Co-authored-by: Julien Duchesne <julien.duchesne@grafana.com>
This commit is contained in:
co-authored by
Julien Duchesne
parent
70d0b7a0d2
commit
81d4d8f228
@@ -1,246 +0,0 @@
|
|||||||
package packaging
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/build/config"
|
|
||||||
"github.com/grafana/grafana/pkg/build/fsutil"
|
|
||||||
"github.com/grafana/grafana/pkg/infra/fs"
|
|
||||||
)
|
|
||||||
|
|
||||||
func writeAptlyConf(dbDir, repoDir string) error {
|
|
||||||
aptlyConf := fmt.Sprintf(`{
|
|
||||||
"rootDir": "%s",
|
|
||||||
"downloadConcurrency": 4,
|
|
||||||
"downloadSpeedLimit": 0,
|
|
||||||
"architectures": [],
|
|
||||||
"dependencyFollowSuggests": false,
|
|
||||||
"dependencyFollowRecommends": false,
|
|
||||||
"dependencyFollowAllVariants": false,
|
|
||||||
"dependencyFollowSource": false,
|
|
||||||
"dependencyVerboseResolve": false,
|
|
||||||
"gpgDisableSign": false,
|
|
||||||
"gpgDisableVerify": false,
|
|
||||||
"gpgProvider": "gpg2",
|
|
||||||
"downloadSourcePackages": false,
|
|
||||||
"skipLegacyPool": true,
|
|
||||||
"ppaDistributorID": "ubuntu",
|
|
||||||
"ppaCodename": "",
|
|
||||||
"skipContentsPublishing": false,
|
|
||||||
"FileSystemPublishEndpoints": {
|
|
||||||
"repo": {
|
|
||||||
"rootDir": "%s",
|
|
||||||
"linkMethod": "copy"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"S3PublishEndpoints": {},
|
|
||||||
"SwiftPublishEndpoints": {}
|
|
||||||
}
|
|
||||||
`, dbDir, repoDir)
|
|
||||||
home, err := os.UserHomeDir()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return os.WriteFile(filepath.Join(home, ".aptly.conf"), []byte(aptlyConf), 0600)
|
|
||||||
}
|
|
||||||
|
|
||||||
// downloadDebs downloads Deb packages.
|
|
||||||
func downloadDebs(cfg PublishConfig, workDir string) error {
|
|
||||||
if cfg.Bucket == "" {
|
|
||||||
panic("cfg.Bucket has to be set")
|
|
||||||
}
|
|
||||||
if !strings.HasSuffix(workDir, string(filepath.Separator)) {
|
|
||||||
workDir += string(filepath.Separator)
|
|
||||||
}
|
|
||||||
|
|
||||||
var version string
|
|
||||||
if cfg.ReleaseMode.Mode == config.TagMode {
|
|
||||||
if cfg.ReleaseMode.IsBeta {
|
|
||||||
version = strings.ReplaceAll(cfg.Version, "-", "~")
|
|
||||||
} else {
|
|
||||||
version = cfg.Version
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if version == "" {
|
|
||||||
panic(fmt.Sprintf("Unrecognized version mode %s", cfg.ReleaseMode.Mode))
|
|
||||||
}
|
|
||||||
|
|
||||||
var sfx string
|
|
||||||
switch cfg.Edition {
|
|
||||||
case config.EditionOSS:
|
|
||||||
case config.EditionEnterprise:
|
|
||||||
sfx = EnterpriseSfx
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unrecognized edition %q", cfg.Edition)
|
|
||||||
}
|
|
||||||
|
|
||||||
u := fmt.Sprintf("gs://%s/%s/%s/grafana%s_%s_*.deb*", cfg.Bucket,
|
|
||||||
strings.ToLower(string(cfg.Edition)), ReleaseFolder, sfx, version)
|
|
||||||
log.Printf("Downloading Deb packages %q...\n", u)
|
|
||||||
args := []string{
|
|
||||||
"-m",
|
|
||||||
"cp",
|
|
||||||
u,
|
|
||||||
workDir,
|
|
||||||
}
|
|
||||||
//nolint:gosec
|
|
||||||
cmd := exec.Command("gsutil", args...)
|
|
||||||
if output, err := cmd.CombinedOutput(); err != nil {
|
|
||||||
return fmt.Errorf("failed to download Deb packages %q: %w\n%s", u, err, output)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateDebRepo updates the Debian repository with the new release.
|
|
||||||
func UpdateDebRepo(cfg PublishConfig, workDir string) error {
|
|
||||||
if cfg.ReleaseMode.Mode != config.TagMode {
|
|
||||||
panic(fmt.Sprintf("Unsupported version mode: %s", cfg.ReleaseMode.Mode))
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.ReleaseMode.IsTest {
|
|
||||||
if cfg.Config.DebDBBucket == DefaultDebDBBucket {
|
|
||||||
return fmt.Errorf("in test-release mode, the default Deb DB bucket shouldn't be used")
|
|
||||||
}
|
|
||||||
if cfg.Config.DebRepoBucket == DefaultDebRepoBucket {
|
|
||||||
return fmt.Errorf("in test-release mode, the default Deb repo bucket shouldn't be used")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := downloadDebs(cfg, workDir); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
repoName := "grafana"
|
|
||||||
if cfg.ReleaseMode.IsBeta {
|
|
||||||
repoName = "beta"
|
|
||||||
}
|
|
||||||
|
|
||||||
repoRoot, err := fsutil.CreateTempDir("deb-repo")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := os.RemoveAll(repoRoot); err != nil {
|
|
||||||
log.Printf("Failed to remove temporary directory %q: %s\n", repoRoot, err.Error())
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
dbDir := filepath.Join(repoRoot, "db")
|
|
||||||
repoDir := filepath.Join(repoRoot, "repo")
|
|
||||||
tmpDir := filepath.Join(repoRoot, "tmp")
|
|
||||||
for _, dpath := range []string{dbDir, repoDir, tmpDir} {
|
|
||||||
if err := os.MkdirAll(dpath, 0750); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := writeAptlyConf(dbDir, repoDir); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Download the Debian repo database
|
|
||||||
u := fmt.Sprintf("gs://%s/%s", cfg.DebDBBucket, strings.ToLower(string(cfg.Edition)))
|
|
||||||
log.Printf("Downloading Debian repo database from %s...\n", u)
|
|
||||||
//nolint:gosec
|
|
||||||
cmd := exec.Command("gsutil", "-m", "rsync", "-r", "-d", u, dbDir)
|
|
||||||
if output, err := cmd.CombinedOutput(); err != nil {
|
|
||||||
return fmt.Errorf("failed to download Debian repo database: %w\n%s", err, output)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := addPkgsToRepo(cfg, workDir, tmpDir, repoName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Updating local Debian package repository...")
|
|
||||||
// Update published local repository. This assumes that there exists already a local, published repo.
|
|
||||||
for _, tp := range []string{"stable", "beta"} {
|
|
||||||
passArg := fmt.Sprintf("-passphrase-file=%s", cfg.GPGPassPath)
|
|
||||||
//nolint:gosec
|
|
||||||
cmd := exec.Command("aptly", "publish", "update", "-batch", passArg, "-force-overwrite", tp,
|
|
||||||
"filesystem:repo:grafana")
|
|
||||||
if output, err := cmd.CombinedOutput(); err != nil {
|
|
||||||
return cli.Exit(fmt.Sprintf("failed to update Debian %q repository: %s", tp, output), 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update database in GCS
|
|
||||||
u = fmt.Sprintf("gs://%s/%s", cfg.DebDBBucket, strings.ToLower(string(cfg.Edition)))
|
|
||||||
if cfg.DryRun {
|
|
||||||
log.Printf("Simulating upload of Debian repo database to GCS (%s)\n", u)
|
|
||||||
} else {
|
|
||||||
log.Printf("Uploading Debian repo database to GCS (%s)...\n", u)
|
|
||||||
//nolint:gosec
|
|
||||||
cmd = exec.Command("gsutil", "-m", "rsync", "-r", "-d", dbDir, u)
|
|
||||||
if output, err := cmd.CombinedOutput(); err != nil {
|
|
||||||
return cli.Exit(fmt.Sprintf("failed to upload Debian repo database to GCS: %s", output), 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update metadata and binaries in repository bucket
|
|
||||||
u = fmt.Sprintf("gs://%s/%s/deb", cfg.DebRepoBucket, strings.ToLower(string(cfg.Edition)))
|
|
||||||
grafDir := filepath.Join(repoDir, "grafana")
|
|
||||||
if cfg.DryRun {
|
|
||||||
log.Printf("Simulating upload of Debian repo resources to GCS (%s)\n", u)
|
|
||||||
} else {
|
|
||||||
log.Printf("Uploading Debian repo resources to GCS (%s)...\n", u)
|
|
||||||
//nolint:gosec
|
|
||||||
cmd = exec.Command("gsutil", "-m", "rsync", "-r", "-d", grafDir, u)
|
|
||||||
if output, err := cmd.CombinedOutput(); err != nil {
|
|
||||||
return cli.Exit(fmt.Sprintf("failed to upload Debian repo resources to GCS: %s", output), 1)
|
|
||||||
}
|
|
||||||
allRepoResources := fmt.Sprintf("%s/**/*", u)
|
|
||||||
log.Printf("Setting cache ttl for Debian repo resources on GCS (%s)...\n", allRepoResources)
|
|
||||||
//nolint:gosec
|
|
||||||
cmd = exec.Command("gsutil", "-m", "setmeta", "-h", CacheSettings+cfg.TTL, allRepoResources)
|
|
||||||
if output, err := cmd.CombinedOutput(); err != nil {
|
|
||||||
return cli.Exit(fmt.Sprintf("failed to set cache ttl for Debian repo resources on GCS: %s", output), 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func addPkgsToRepo(cfg PublishConfig, workDir, tmpDir, repoName string) error {
|
|
||||||
var sfx string
|
|
||||||
switch cfg.Edition {
|
|
||||||
case config.EditionOSS:
|
|
||||||
case config.EditionEnterprise:
|
|
||||||
sfx = EnterpriseSfx
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unsupported edition %q", cfg.Edition)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Adding packages to Debian %q repo...\n", repoName)
|
|
||||||
// TODO: Be more specific about filename pattern
|
|
||||||
debs, err := filepath.Glob(filepath.Join(workDir, fmt.Sprintf("grafana%s*.deb", sfx)))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, deb := range debs {
|
|
||||||
basename := filepath.Base(deb)
|
|
||||||
if strings.Contains(basename, "latest") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
tgt := filepath.Join(tmpDir, basename)
|
|
||||||
if err := fs.CopyFile(deb, tgt); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// XXX: Adds too many packages in enterprise (Arve: What does this mean exactly?)
|
|
||||||
//nolint:gosec
|
|
||||||
cmd := exec.Command("aptly", "repo", "add", "-force-replace", repoName, tmpDir)
|
|
||||||
if output, err := cmd.CombinedOutput(); err != nil {
|
|
||||||
return cli.Exit(fmt.Sprintf("failed to add packages to local Debian repository: %s", output), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,370 +0,0 @@
|
|||||||
package packaging
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
// Consider switching this over to a community fork unless there is
|
|
||||||
// an option to move us away from OpenPGP.
|
|
||||||
"golang.org/x/crypto/openpgp" //nolint:staticcheck
|
|
||||||
"golang.org/x/crypto/openpgp/armor" //nolint:staticcheck
|
|
||||||
"golang.org/x/crypto/openpgp/packet" //nolint:staticcheck
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/build/config"
|
|
||||||
"github.com/grafana/grafana/pkg/build/fsutil"
|
|
||||||
"github.com/grafana/grafana/pkg/infra/fs"
|
|
||||||
)
|
|
||||||
|
|
||||||
// UpdateRPMRepo updates the RPM repository with the new release.
|
|
||||||
func UpdateRPMRepo(cfg PublishConfig, workDir string) error {
|
|
||||||
if cfg.ReleaseMode.Mode != config.TagMode {
|
|
||||||
panic(fmt.Sprintf("Unsupported version mode %s", cfg.ReleaseMode.Mode))
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.ReleaseMode.IsTest && cfg.Config.RPMRepoBucket == DefaultRPMRepoBucket {
|
|
||||||
return fmt.Errorf("in test-release mode, the default RPM repo bucket shouldn't be used")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := downloadRPMs(cfg, workDir); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
repoRoot, err := fsutil.CreateTempDir("rpm-repo")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := os.RemoveAll(repoRoot); err != nil {
|
|
||||||
log.Printf("Failed to remove %q: %s\n", repoRoot, err.Error())
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
repoName := "rpm"
|
|
||||||
if cfg.ReleaseMode.IsBeta {
|
|
||||||
repoName = "rpm-beta"
|
|
||||||
}
|
|
||||||
folderURI := fmt.Sprintf("gs://%s/%s/%s", cfg.RPMRepoBucket, strings.ToLower(string(cfg.Edition)), repoName)
|
|
||||||
|
|
||||||
// Download the RPM database
|
|
||||||
log.Printf("Downloading RPM database from GCS (%s)...\n", folderURI)
|
|
||||||
//nolint:gosec
|
|
||||||
cmd := exec.Command("gsutil", "-m", "rsync", "-r", "-d", folderURI, repoRoot)
|
|
||||||
if output, err := cmd.CombinedOutput(); err != nil {
|
|
||||||
return fmt.Errorf("failed to download RPM database from GCS: %w\n%s", err, output)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the new release to the repo
|
|
||||||
var sfx string
|
|
||||||
switch cfg.Edition {
|
|
||||||
case config.EditionOSS:
|
|
||||||
case config.EditionEnterprise:
|
|
||||||
sfx = EnterpriseSfx
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unsupported edition %q", cfg.Edition)
|
|
||||||
}
|
|
||||||
allRPMs, err := filepath.Glob(filepath.Join(workDir, fmt.Sprintf("grafana%s-*.rpm", sfx)))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to list RPMs in %q: %w", workDir, err)
|
|
||||||
}
|
|
||||||
rpms := []string{}
|
|
||||||
for _, rpm := range allRPMs {
|
|
||||||
if strings.Contains(rpm, "-latest") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
rpms = append(rpms, rpm)
|
|
||||||
}
|
|
||||||
// XXX: What does the following comment mean?
|
|
||||||
// adds to many files for enterprise
|
|
||||||
for _, rpm := range rpms {
|
|
||||||
if err := fs.CopyFile(rpm, filepath.Join(repoRoot, filepath.Base(rpm))); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint:gosec
|
|
||||||
cmd = exec.Command("createrepo", repoRoot)
|
|
||||||
if output, err := cmd.CombinedOutput(); err != nil {
|
|
||||||
return fmt.Errorf("failed to create repo at %q: %w\n%s", repoRoot, err, output)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := signRPMRepo(repoRoot, cfg); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the repo in GCS
|
|
||||||
|
|
||||||
// Sync packages first to avoid cache misses
|
|
||||||
if cfg.DryRun {
|
|
||||||
log.Printf("Simulating upload of RPMs to GCS (%s)\n", folderURI)
|
|
||||||
} else {
|
|
||||||
log.Printf("Uploading RPMs to GCS (%s)...\n", folderURI)
|
|
||||||
args := []string{"-m", "cp"}
|
|
||||||
args = append(args, rpms...)
|
|
||||||
args = append(args, folderURI)
|
|
||||||
//nolint:gosec
|
|
||||||
cmd = exec.Command("gsutil", args...)
|
|
||||||
if output, err := cmd.CombinedOutput(); err != nil {
|
|
||||||
return fmt.Errorf("failed to upload RPMs to GCS: %w\n%s", err, output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.DryRun {
|
|
||||||
log.Printf("Simulating upload of RPM repo metadata to GCS (%s)\n", folderURI)
|
|
||||||
} else {
|
|
||||||
log.Printf("Uploading RPM repo metadata to GCS (%s)...\n", folderURI)
|
|
||||||
//nolint:gosec
|
|
||||||
cmd = exec.Command("gsutil", "-m", "rsync", "-r", "-d", repoRoot, folderURI)
|
|
||||||
if output, err := cmd.CombinedOutput(); err != nil {
|
|
||||||
return fmt.Errorf("failed to upload RPM repo metadata to GCS: %w\n%s", err, output)
|
|
||||||
}
|
|
||||||
allRepoResources := fmt.Sprintf("%s/**/*", folderURI)
|
|
||||||
log.Printf("Setting cache ttl for RPM repo resources on GCS (%s)...\n", allRepoResources)
|
|
||||||
//nolint:gosec
|
|
||||||
cmd = exec.Command("gsutil", "-m", "setmeta", "-h", CacheSettings+cfg.TTL, allRepoResources)
|
|
||||||
if output, err := cmd.CombinedOutput(); err != nil {
|
|
||||||
return fmt.Errorf("failed to set cache ttl for RPM repo resources on GCS: %w\n%s", err, output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// downloadRPMs downloads RPM packages.
|
|
||||||
func downloadRPMs(cfg PublishConfig, workDir string) error {
|
|
||||||
if !strings.HasSuffix(workDir, string(filepath.Separator)) {
|
|
||||||
workDir += string(filepath.Separator)
|
|
||||||
}
|
|
||||||
var version string
|
|
||||||
if cfg.ReleaseMode.Mode == config.TagMode {
|
|
||||||
if cfg.ReleaseMode.IsBeta {
|
|
||||||
version = strings.ReplaceAll(cfg.Version, "-", "~")
|
|
||||||
} else {
|
|
||||||
version = cfg.Version
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if version == "" {
|
|
||||||
panic(fmt.Sprintf("Unrecognized version mode %s", cfg.ReleaseMode.Mode))
|
|
||||||
}
|
|
||||||
|
|
||||||
var sfx string
|
|
||||||
switch cfg.Edition {
|
|
||||||
case config.EditionOSS:
|
|
||||||
case config.EditionEnterprise:
|
|
||||||
sfx = EnterpriseSfx
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unrecognized edition %q", cfg.Edition)
|
|
||||||
}
|
|
||||||
|
|
||||||
u := fmt.Sprintf("gs://%s/%s/%s/grafana%s-%s-*.*.rpm*", cfg.Bucket,
|
|
||||||
strings.ToLower(string(cfg.Edition)), ReleaseFolder, sfx, version)
|
|
||||||
log.Printf("Downloading RPM packages %q...\n", u)
|
|
||||||
args := []string{
|
|
||||||
"-m",
|
|
||||||
"cp",
|
|
||||||
u,
|
|
||||||
workDir,
|
|
||||||
}
|
|
||||||
//nolint:gosec
|
|
||||||
cmd := exec.Command("gsutil", args...)
|
|
||||||
if output, err := cmd.CombinedOutput(); err != nil {
|
|
||||||
return fmt.Errorf("failed to download RPM packages %q: %w\n%s", u, err, output)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPublicKey(cfg PublishConfig) (*packet.PublicKey, error) {
|
|
||||||
f, err := os.Open(cfg.GPGPublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to open %q: %w", cfg.GPGPublicKey, err)
|
|
||||||
}
|
|
||||||
defer func(f *os.File) {
|
|
||||||
err := f.Close()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}(f)
|
|
||||||
|
|
||||||
block, err := armor.Decode(f)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if block.Type != openpgp.PublicKeyType {
|
|
||||||
return nil, fmt.Errorf("invalid public key block type: %q", block.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
packetReader := packet.NewReader(block.Body)
|
|
||||||
pkt, err := packetReader.Next()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
key, ok := pkt.(*packet.PublicKey)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("got non-public key from packet reader: %T", pkt)
|
|
||||||
}
|
|
||||||
|
|
||||||
return key, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPrivateKey(cfg PublishConfig) (*packet.PrivateKey, error) {
|
|
||||||
f, err := os.Open(cfg.GPGPrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to open %q: %w", cfg.GPGPrivateKey, err)
|
|
||||||
}
|
|
||||||
defer func(f *os.File) {
|
|
||||||
err := f.Close()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}(f)
|
|
||||||
|
|
||||||
passphraseB, err := os.ReadFile(cfg.GPGPassPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to read %q: %w", cfg.GPGPrivateKey, err)
|
|
||||||
}
|
|
||||||
passphraseB = bytes.TrimSuffix(passphraseB, []byte("\n"))
|
|
||||||
|
|
||||||
block, err := armor.Decode(f)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if block.Type != openpgp.PrivateKeyType {
|
|
||||||
return nil, fmt.Errorf("invalid private key block type: %q", block.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
packetReader := packet.NewReader(block.Body)
|
|
||||||
pkt, err := packetReader.Next()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
key, ok := pkt.(*packet.PrivateKey)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("got non-private key from packet reader: %T", pkt)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := key.Decrypt(passphraseB); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to decrypt private key: %w", err)
|
|
||||||
}
|
|
||||||
return key, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// signRPMRepo signs an RPM repository using PGP.
|
|
||||||
// The signature gets written to the file repodata/repomd.xml.asc.
|
|
||||||
func signRPMRepo(repoRoot string, cfg PublishConfig) error {
|
|
||||||
if cfg.GPGPublicKey == "" || cfg.GPGPrivateKey == "" {
|
|
||||||
return fmt.Errorf("private or public key is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Signing RPM repo")
|
|
||||||
|
|
||||||
pubKey, err := getPublicKey(cfg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
privKey, err := getPrivateKey(cfg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
pcfg := packet.Config{
|
|
||||||
DefaultHash: crypto.SHA256,
|
|
||||||
DefaultCipher: packet.CipherAES256,
|
|
||||||
DefaultCompressionAlgo: packet.CompressionZLIB,
|
|
||||||
CompressionConfig: &packet.CompressionConfig{
|
|
||||||
Level: 9,
|
|
||||||
},
|
|
||||||
RSABits: 4096,
|
|
||||||
}
|
|
||||||
currentTime := pcfg.Now()
|
|
||||||
uid := packet.NewUserId("", "", "")
|
|
||||||
|
|
||||||
isPrimaryID := false
|
|
||||||
keyLifetimeSecs := uint32(86400 * 365)
|
|
||||||
signer := openpgp.Entity{
|
|
||||||
PrimaryKey: pubKey,
|
|
||||||
PrivateKey: privKey,
|
|
||||||
Identities: map[string]*openpgp.Identity{
|
|
||||||
uid.Id: {
|
|
||||||
Name: uid.Name,
|
|
||||||
UserId: uid,
|
|
||||||
SelfSignature: &packet.Signature{
|
|
||||||
CreationTime: currentTime,
|
|
||||||
SigType: packet.SigTypePositiveCert,
|
|
||||||
PubKeyAlgo: packet.PubKeyAlgoRSA,
|
|
||||||
Hash: pcfg.Hash(),
|
|
||||||
IsPrimaryId: &isPrimaryID,
|
|
||||||
FlagsValid: true,
|
|
||||||
FlagSign: true,
|
|
||||||
FlagCertify: true,
|
|
||||||
IssuerKeyId: &pubKey.KeyId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Subkeys: []openpgp.Subkey{
|
|
||||||
{
|
|
||||||
PublicKey: pubKey,
|
|
||||||
PrivateKey: privKey,
|
|
||||||
Sig: &packet.Signature{
|
|
||||||
CreationTime: currentTime,
|
|
||||||
SigType: packet.SigTypeSubkeyBinding,
|
|
||||||
PubKeyAlgo: packet.PubKeyAlgoRSA,
|
|
||||||
Hash: pcfg.Hash(),
|
|
||||||
PreferredHash: []uint8{8}, // SHA-256
|
|
||||||
FlagsValid: true,
|
|
||||||
FlagEncryptStorage: true,
|
|
||||||
FlagEncryptCommunications: true,
|
|
||||||
IssuerKeyId: &pubKey.KeyId,
|
|
||||||
KeyLifetimeSecs: &keyLifetimeSecs,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore gosec G304 as this function is only used in the build process.
|
|
||||||
//nolint:gosec
|
|
||||||
freader, err := os.Open(filepath.Join(repoRoot, "repodata", "repomd.xml"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer func(freader *os.File) {
|
|
||||||
err := freader.Close()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}(freader)
|
|
||||||
|
|
||||||
// Ignore gosec G304 as this function is only used in the build process.
|
|
||||||
//nolint:gosec
|
|
||||||
sigwriter, err := os.Create(filepath.Join(repoRoot, "repodata", "repomd.xml.asc"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer func(sigwriter *os.File) {
|
|
||||||
err := sigwriter.Close()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}(sigwriter)
|
|
||||||
|
|
||||||
if err := openpgp.ArmoredDetachSignText(sigwriter, &signer, freader, nil); err != nil {
|
|
||||||
return fmt.Errorf("failed to write PGP signature: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := sigwriter.Close(); err != nil {
|
|
||||||
return fmt.Errorf("failed to write PGP signature: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
package packaging
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/build/config"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
const pubKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
||||||
Version: OpenPGP.js v4.10.10
|
|
||||||
Comment: https://openpgpjs.org
|
|
||||||
|
|
||||||
xsBNBGM1b9wBCADZM49X7vwOS93KbgA6yhpwrYf8ZlzksGcDaYgp1IzvqHbs
|
|
||||||
xeU1mmBYVH/bSKRDG0tt3Qdky4Nvl4Oqd+g0e2ZGjmlEy9zUiPTTK/BtXT+5
|
|
||||||
s8oqih2NIAkyF91BNZABAgvh/vJdYImhYeUQBDqMJgqZ/Y/Ha31N7rSW+jUt
|
|
||||||
LHspbN0ztJYjuEd/bg2NKH7Gs/AyNvX9IQTC4k7iRRafx7q/PBCVtsk+NCwz
|
|
||||||
BEkL93xpAdcdYiMNrRP2eIHQjBmNZ/oUCkcDsLCBvcSq6P2lGpNnpPzVoTJf
|
|
||||||
v2qrWkVn5txJJsOkmBGpEDbECPunVilrWO6RPomP0yYkr6NE4XeCJ3QhABEB
|
|
||||||
AAHNGWR1bW15IDxkdW1teUBob3RtYWlsLmNvbT7CwI0EEAEIACAFAmM1b9wG
|
|
||||||
CwkHCAMCBBUICgIEFgIBAAIZAQIbAwIeAQAhCRAoJ1i5w6kkAxYhBCQv+iwt
|
|
||||||
IFn7vj9PLygnWLnDqSQDPxkH/0Ju2Cah+bOxl09uv2Ft2BVlQh0u+wJyRVgs
|
|
||||||
KxTxldAXFZwMrN4wK/GUoGWDiy2tzNtoVE6GpxWUj+LvSGFaVLNVjW+Le77I
|
|
||||||
BP/sl1wKHJbQhseKc7Mz5Zj3i0F1FPM+rLik7tNk6kiEBqYVyyXahyT98Hu1
|
|
||||||
1OKEV+8NiRG47iNgd/dpgEdVSS4DN/dL6m5q+CVy9YnlR+wXxF/2xcMmWBzR
|
|
||||||
V2cPVw0JzunpUV8lDDQ/n1sPw61D3oL1aH0bkn8aA8pEceKOVIYOaja7LkLX
|
|
||||||
uSlROlALA/M2fuubradW9I3FcrJNn+/xA52el2l/Hn/Syf9GQV/Ll/R+qKIo
|
|
||||||
Z57xWd7OwE0EYzVv3AEIAJl/PNYOF2prNKY58BfZ74XurDb9mNlZ1wsIqrOu
|
|
||||||
J/euzHEnzkCAjMUuXV7wcugjQlmpcZn6Y0QmQ2uX7SwPCMovDvngbXeAfbdd
|
|
||||||
6FUKecQ0sG54Plm8HSMNdjetdUVl7ACxjJO8Rdc/Asx7ua7gMm42CVfqMj4L
|
|
||||||
qN5foUBlaKJ1iGKUpQ+673UQWMYeOBuu9G8awbSzGaphN97CIX7xEMGzGeff
|
|
||||||
yHLHK+MsfX935uDgDwJQzxJKEugIJDMKgWOLgVz1jRCsJKHlywHTWpVuMiKY
|
|
||||||
Wnuq4tDNLBUQtaRL7uclG7Wejw/XNN0uD/zNHPgF5rmlYHVhrtDbBCP2XqTn
|
|
||||||
WU8AEQEAAcLAdgQYAQgACQUCYzVv3AIbDAAhCRAoJ1i5w6kkAxYhBCQv+iwt
|
|
||||||
IFn7vj9PLygnWLnDqSQDFqYH/AkdNaPUQlE7RQBigNRGOFBuqjhbLsV/rZf+
|
|
||||||
/4K6wDHojM606lgLm57T4NUXnk53VIF3KO8+v8N11mCtPb+zBngfvVU14COC
|
|
||||||
HEDNdOK19TlR+tH25cftfUiF+OJsgMQysErGuFEtwLE6TNzpQIcnw7SbjxMr
|
|
||||||
EGacF9xCBKexB6MlR3GwJ2LBUJm3Lq/fvqImztoTlKDsrpk4JOH5FfYG+G2f
|
|
||||||
1zU73fVsCCElX4qA/49rRQf0RNfhjRjmHULP8hSvCXUEhfiBggEgxof/vKlC
|
|
||||||
qauHC55luuIeabju8HaXTjpz019cq+3IUgewX/ky0PhQXEW9SoODKabPY2yS
|
|
||||||
yUbHFm4=
|
|
||||||
=OCSx
|
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
|
||||||
`
|
|
||||||
|
|
||||||
const privKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
|
||||||
Version: OpenPGP.js v4.10.10
|
|
||||||
Comment: https://openpgpjs.org
|
|
||||||
|
|
||||||
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
|
|
||||||
RscWbg==
|
|
||||||
=KJNy
|
|
||||||
-----END PGP PRIVATE KEY BLOCK-----
|
|
||||||
`
|
|
||||||
|
|
||||||
// Dummy GPG keys, used only for testing
|
|
||||||
// nolint:gosec
|
|
||||||
const passPhrase = `MkDgjkrgdGxt`
|
|
||||||
|
|
||||||
func TestSignRPMRepo(t *testing.T) {
|
|
||||||
repoDir := t.TempDir()
|
|
||||||
workDir := t.TempDir()
|
|
||||||
pubKeyPath := filepath.Join(workDir, "pub.key")
|
|
||||||
err := os.WriteFile(pubKeyPath, []byte(pubKey), 0600)
|
|
||||||
require.NoError(t, err)
|
|
||||||
privKeyPath := filepath.Join(workDir, "priv.key")
|
|
||||||
err = os.WriteFile(privKeyPath, []byte(privKey), 0600)
|
|
||||||
require.NoError(t, err)
|
|
||||||
passPhrasePath := filepath.Join(workDir, "passphrase")
|
|
||||||
err = os.WriteFile(passPhrasePath, []byte(passPhrase), 0600)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = os.Mkdir(filepath.Join(repoDir, "repodata"), 0700)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = os.WriteFile(filepath.Join(repoDir, "repodata", "repomd.xml"), []byte("<xml></xml>"), 0600)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
cfg := PublishConfig{
|
|
||||||
Config: config.Config{
|
|
||||||
GPGPrivateKey: privKeyPath,
|
|
||||||
GPGPublicKey: pubKeyPath,
|
|
||||||
GPGPassPath: passPhrasePath,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err = signRPMRepo(repoDir, cfg)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user